Client implementation of the 9P protocol for constrained devices
git clone https://git.8pit.net/ninenano.git
1package main 2 3import ( 4 "bytes" 5 "errors" 6 "github.com/Harvey-OS/ninep/protocol" 7) 8 9// Maps strings written by the client to the control socket to 10// server replies. Every test function needs an entry in this table. 11var ctlcmds = map[string]ServerReply{ 12 "header_too_short1": {HeaderTooShort1, protocol.Tversion}, 13 "header_too_short2": {HeaderTooShort2, protocol.Tversion}, 14 "header_too_large": {HeaderTooLarge, protocol.Tversion}, 15 "header_wrong_type": {HeaderWrongType, protocol.Tversion}, 16 "header_invalid_type": {HeaderInvalidType, protocol.Tversion}, 17 "header_tag_mismatch": {HeaderTagMismatch, protocol.Tversion}, 18 "header_type_mismatch": {HeaderTypeMismatch, protocol.Tversion}, 19 20 "rversion_success": {RversionSuccess, protocol.Tversion}, 21 "rversion_unknown": {RversionUnknown, protocol.Tversion}, 22 "rversion_msize_too_big": {RversionMsizeTooBig, protocol.Tversion}, 23 "rversion_invalid": {RversionInvalidVersion, protocol.Tversion}, 24 "rversion_invalid_len": {RversionInvalidLength, protocol.Tversion}, 25 "rversion_version_too_long": {RversionVersionTooLong, protocol.Tversion}, 26 27 "rattach_success": {RattachSuccess, protocol.Tattach}, 28 "rattach_invalid_len": {RattachInvalidLength, protocol.Tattach}, 29 30 "rstat_success": {RstatSuccess, protocol.Tstat}, 31 "rstat_too_short": {RstatTooShort, protocol.Tstat}, 32 33 "rwalk_success": {RwalkSuccess, protocol.Twalk}, 34 "rwalk_invalid_len": {RwalkInvalidLen, protocol.Twalk}, 35 "rwalk_nwqid_too_large": {RwalkNwqidTooLarge, protocol.Twalk}, 36 37 "ropen_success": {RopenSuccess, protocol.Topen}, 38 39 "rcreate_success": {RcreateSuccess, protocol.Tcreate}, 40 41 "rread_success": {RreadSuccess, protocol.Tread}, 42 "rread_with_offset": {RreadWithOffset, protocol.Tread}, 43 "rread_count_zero": {RreadCountZero, protocol.Tread}, 44 45 "rwrite_success": {RwriteSuccess, protocol.Twrite}, 46 47 "rclunk_success": {RclunkSuccess, protocol.Tclunk}, 48 49 "remove_success": {RremoveSuccess, protocol.Tremove}, 50} 51 52// Replies with a single byte. This is thus even shorter than the four-byte 53// length field and should not be parsed by the client succesfully. 54func HeaderTooShort1(b *bytes.Buffer) error { 55 b.Reset() 56 b.Write([]byte{0}) 57 return nil 58} 59 60// Replies with a message containing a four-byte size field with a value 61// that is too small to make the message a valid 9p message. 62func HeaderTooShort2(b *bytes.Buffer) error { 63 b.Reset() 64 l := uint64(6) 65 b.Write([]byte{uint8(l), uint8(l >> 8), uint8(l >> 16), uint8(l >> 24)}) 66 return nil 67} 68 69// Replies with a length field that is larger than the actual amount of bytes 70// send to the client. 71func HeaderTooLarge(b *bytes.Buffer) error { 72 b.Reset() 73 74 l := uint64(42) 75 b.Write([]byte{uint8(l), uint8(l >> 8), uint8(l >> 16), uint8(l >> 24)}) 76 return nil 77} 78 79// Replies with a message containing a T-message type field. 80func HeaderWrongType(b *bytes.Buffer) error { 81 _, _, t, err := protocol.UnmarshalTversionPkt(b) 82 if err != nil { 83 return err 84 } 85 86 b.Reset() 87 b.Write([]byte{0, 0, 0, 0, 88 uint8(protocol.Tversion), 89 byte(t), byte(t >> 8), 90 byte(0), byte(0), byte(0), byte(0)}) 91 92 { 93 l := uint64(b.Len()) 94 copy(b.Bytes(), []byte{uint8(l), uint8(l >> 8), uint8(l >> 16), uint8(l >> 24)}) 95 } 96 97 return nil 98} 99100// Replies with an invalid type value. The client should not be able to parse101// this successfully.102func HeaderInvalidType(b *bytes.Buffer) error {103 _, _, t, err := protocol.UnmarshalTversionPkt(b)104 if err != nil {105 return err106 }107108 b.Reset()109 b.Write([]byte{0, 0, 0, 0,110 uint8(protocol.Tlast),111 byte(t), byte(t >> 8),112 byte(0), byte(0), byte(0), byte(0)})113114 return nil115}116117// Replies with a tag mismatched tag message. The client should be able118// to parse this but should raise an error.119func HeaderTagMismatch(b *bytes.Buffer) error {120 _, _, t, err := protocol.UnmarshalTversionPkt(b)121 if err != nil {122 return err123 }124125 t += 1126127 b.Reset()128 b.Write([]byte{0, 0, 0, 0,129 uint8(protocol.Rversion),130 byte(t), byte(t >> 8),131 byte(0), byte(0), byte(0), byte(0)})132133 return nil134}135136// Replies with a type message that is valid but not expected by the137// client. Thus the client should be able to parse this but should raise138// an error.139func HeaderTypeMismatch(b *bytes.Buffer) error {140 _, _, t, err := protocol.UnmarshalTversionPkt(b)141 if err != nil {142 return err143 }144145 b.Reset()146 b.Write([]byte{0, 0, 0, 0,147 uint8(protocol.Rversion),148 byte(t), byte(t >> 8),149 byte(0), byte(0), byte(0), byte(0)})150151 return nil152}153154// Replies with the msize and version send by the client. This should always be155// successfully parsed by the client implementation.156func RversionSuccess(b *bytes.Buffer) error {157 TMsize, TVersion, t, err := protocol.UnmarshalTversionPkt(b)158 if err != nil {159 return err160 }161162 protocol.MarshalRversionPkt(b, t, TMsize, TVersion)163 return nil164}165166// Replies with the version string "unknown".167//168// From version(5):169// If the server does not understand the client's version170// string, it should respond with an Rversion message (not171// Rerror) with the version string the 7 characters172// ``unknown''.173//174// The client should therefore be able to parse this message but should175// return an error.176func RversionUnknown(b *bytes.Buffer) error {177 TMsize, _, t, err := protocol.UnmarshalTversionPkt(b)178 if err != nil {179 return err180 }181182 protocol.MarshalRversionPkt(b, t, TMsize, "unknown")183 return nil184}185186// Replies with an msize value equal to the one send by the client plus187// one.188//189// From version(5):190// The server responds with its own maximum, msize, which must191// be less than or equal to the client's value.192//193// The client should therefore be able to parse this message but should194// return an error.195func RversionMsizeTooBig(b *bytes.Buffer) error {196 TMsize, TVersion, t, err := protocol.UnmarshalTversionPkt(b)197 if err != nil {198 return err199 }200201 protocol.MarshalRversionPkt(b, t, TMsize+1, TVersion)202 return nil203}204205// Replies with an invalid version string.206//207// From version(5):208// After stripping any such period-separated suffix, the server is209// allowed to respond with a string of the form 9Pnnnn, where nnnn is210// less than or equal to the digits sent by the client.211//212// Depending on the client implementation the client may not be able to213// parse the R-message. In any case it should return an error since the214// he shouldn't support this version of the 9P protocol.215func RversionInvalidVersion(b *bytes.Buffer) error {216 TMsize, _, t, err := protocol.UnmarshalTversionPkt(b)217 if err != nil {218 return err219 }220221 protocol.MarshalRversionPkt(b, t, TMsize, "9P20009P2000")222 return nil223}224225// Replies with an length field in the version R-message.226//227// The value of the size field is one byte too short. Thereby causing228// the length field of the version string to report a string size that229// would exceeds the size of the packet itself.230//231// The client should not be able to parse this R-message.232func RversionInvalidLength(b *bytes.Buffer) error {233 TMsize, TVersion, t, err := protocol.UnmarshalTversionPkt(b)234 if err != nil {235 return err236 }237238 protocol.MarshalRversionPkt(b, t, TMsize, TVersion)239240 {241 var l uint64 = uint64(b.Len() - 1)242 copy(b.Bytes(), []byte{uint8(l), uint8(l >> 8), uint8(l >> 16), uint8(l >> 24)})243 }244245 return nil246}247248// Replies with a version string that is one byte longer than the249// longest valid version string `unknown`.250//251// The client will most likely be able to parse this messages but might252// reject it if it exceeds a statically allocated buffer.253func RversionVersionTooLong(b *bytes.Buffer) error {254 TMsize, _, t, err := protocol.UnmarshalTversionPkt(b)255 if err != nil {256 return err257 }258259 protocol.MarshalRversionPkt(b, t, TMsize, "12345678")260 return nil261}262263// Successfully attaches the client. Replying with a vaild qid. The264// client should be able to parse this successfully.265func RattachSuccess(b *bytes.Buffer) error {266 _, _, _, _, t, err := protocol.UnmarshalTattachPkt(b)267 if err != nil {268 return err269 }270271 protocol.MarshalRattachPkt(b, t, protocol.QID{})272 return nil273}274275// Replies with a modified packet length field causing the packet length to be276// one byte shorter than neccessary. The qid should therefore be invalind and277// the client should not be able to parse this.278func RattachInvalidLength(b *bytes.Buffer) error {279 _, _, _, _, t, err := protocol.UnmarshalTattachPkt(b)280 if err != nil {281 return err282 }283284 protocol.MarshalRattachPkt(b, t, protocol.QID{})285286 {287 var l uint64 = uint64(b.Len() - 1)288 copy(b.Bytes(), []byte{uint8(l), uint8(l >> 8), uint8(l >> 16), uint8(l >> 24)})289 }290291 return nil292}293294// Replies with a valid Rstat message. The client must be able to parse295// this R-message.296func RstatSuccess(b *bytes.Buffer) error {297 _, t, err := protocol.UnmarshalTstatPkt(b)298 if err != nil {299 return err300 }301302 qid := protocol.QID{303 Type: 23,304 Version: 2342,305 Path: 1337,306 }307308 dir := protocol.Dir{309 Type: 9001,310 Dev: 5,311 QID: qid,312 Mode: protocol.DMDIR,313 Atime: 1494443596,314 Mtime: 1494443609,315 Length: 2342,316 Name: "testfile",317 User: "testuser",318 Group: "testgroup",319 ModUser: "ken",320 }321322 var B bytes.Buffer323 protocol.Marshaldir(&B, dir)324325 protocol.MarshalRstatPkt(b, t, B.Bytes())326 return nil327}328329// Replies with an Rstat message that is too short to be valid. The330// client must reject this message.331func RstatTooShort(b *bytes.Buffer) error {332 _, t, err := protocol.UnmarshalTstatPkt(b)333 if err != nil {334 return err335 }336337 var D protocol.Dir338 var B bytes.Buffer339340 protocol.Marshaldir(&B, D)341 protocol.MarshalRstatPkt(b, t, B.Bytes())342343 {344 var l uint64 = 57 // _9P_MINSTSIZ + _9P_HEADSIZ - 1345 copy(b.Bytes(), []byte{uint8(l), uint8(l >> 8), uint8(l >> 16), uint8(l >> 24)})346 }347348 return nil349}350351// Replies with a valid Rwalk message. The last QID is different then352// the other ones to test off-by-one errors. The client must be able353// to parse this R-message.354func RwalkSuccess(b *bytes.Buffer) error {355 _, _, p, t, err := protocol.UnmarshalTwalkPkt(b)356 if err != nil {357 return err358 }359 plen := len(p)360361 q := protocol.QID{362 Path: 23,363 Type: 42,364 Version: 5,365 }366367 var qids []protocol.QID368369 for i := 0; i < plen; i++ {370 qids = append(qids, q)371 }372373 if plen > 0 {374 qids[plen-1] = protocol.QID{375 Path: 1337,376 Type: 23,377 Version: 42,378 }379 }380381 protocol.MarshalRwalkPkt(b, t, qids)382 return nil383}384385// Replies with a message that is too short to be a valid Rwalk message.386// The client should therefore reject this message.387func RwalkInvalidLen(b *bytes.Buffer) error {388 _, _, p, t, err := protocol.UnmarshalTwalkPkt(b)389 if err != nil {390 return err391 }392393 var qids []protocol.QID394 for i := 0; i < len(p); i++ {395 qids = append(qids, protocol.QID{})396 }397398 protocol.MarshalRwalkPkt(b, t, qids)399400 {401 var l uint64 = uint64(10)402 copy(b.Bytes(), []byte{uint8(l), uint8(l >> 8), uint8(l >> 16), uint8(l >> 24)})403 }404405 return nil406}407408// Replies with a Rwalk message which contains an `nwqid` field that409// exceeds MAXWELEM and should therefore be considered invalid by the410// client.411func RwalkNwqidTooLarge(b *bytes.Buffer) error {412 _, _, _, t, err := protocol.UnmarshalTwalkPkt(b)413 if err != nil {414 return err415 }416417 var qids []protocol.QID418 for i := 0; i < 17; i++ {419 qids = append(qids, protocol.QID{})420 }421422 protocol.MarshalRwalkPkt(b, t, qids)423 return nil424}425426// Replies with a valid Ropen message. The iounit of the reply will427// always be `1337`. The client must be able to parse this.428func RopenSuccess(b *bytes.Buffer) error {429 _, _, t, err := protocol.UnmarshalTopenPkt(b)430 if err != nil {431 return err432 }433434 protocol.MarshalRopenPkt(b, t, protocol.QID{}, 1337)435 return nil436}437438// Replies with a vaild Rcreate message. The client must be able to439// parse this.440func RcreateSuccess(b *bytes.Buffer) error {441 _, _, _, _, t, err := protocol.UnmarshalTcreatePkt(b)442 if err != nil {443 return err444 }445446 protocol.MarshalRcreatePkt(b, t, protocol.QID{}, 9001)447 return nil448}449450// Replies with a valid Rread message. The data field of the message451// should hold the string `Hello!`. The client must be able to parse452// this. This function ignores the count sent by the client.453func RreadSuccess(b *bytes.Buffer) error {454 _, _, _, t, err := protocol.UnmarshalTreadPkt(b)455 if err != nil {456 return err457 }458459 protocol.MarshalRreadPkt(b, t, []byte("Hello!"))460 return nil461}462463// Replies with a valid Rread message. The data field of the message is464// set to `1234567890`. Contrary to RreadSuccess this function respects465// the offset and count send by the client.466func RreadWithOffset(b *bytes.Buffer) error {467 _, o, l, t, err := protocol.UnmarshalTreadPkt(b)468 if err != nil {469 return err470 }471472 poff := int(o)473 plen := int(l)474475 hstr := "1234567890"476 hlen := len(hstr)477478 if poff > hlen {479 return errors.New("offset is too large")480 }481 if plen > hlen {482 hlen = plen483 }484485 data := hstr[poff : poff+plen]486487 protocol.MarshalRreadPkt(b, t, []byte(data))488 return nil489}490491// Replies with a valid Rread message which contains a count field with492// the value `0`. The client must be able to parse this but should493// treat the message as an error.494func RreadCountZero(b *bytes.Buffer) error {495 _, _, _, t, err := protocol.UnmarshalTreadPkt(b)496 if err != nil {497 return err498 }499500 protocol.MarshalRreadPkt(b, t, []byte(""))501 return nil502}503504// Replies with a valid Rwrite message. The client must be able to parse505// this.506func RwriteSuccess(b *bytes.Buffer) error {507 _, _, Data, t, err := protocol.UnmarshalTwritePkt(b)508 if err != nil {509 return err510 }511512 protocol.MarshalRwritePkt(b, t, protocol.Count(len(Data)))513 return nil514}515516// Replies with a valid Rclunk message. The client must be able to parse517// this.518func RclunkSuccess(b *bytes.Buffer) error {519 _, t, err := protocol.UnmarshalTclunkPkt(b)520 if err != nil {521 return err522 }523524 protocol.MarshalRclunkPkt(b, t)525 return nil526}527528// Replies with a valid Rremove message. The client must be able to529// parse this.530func RremoveSuccess(b *bytes.Buffer) error {531 _, t, err := protocol.UnmarshalTremovePkt(b)532 if err != nil {533 return err534 }535536 protocol.MarshalRremovePkt(b, t)537 return nil538}