ninenano

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}
 99
100// Replies with an invalid type value. The client should not be able to parse
101// this successfully.
102func HeaderInvalidType(b *bytes.Buffer) error {
103	_, _, t, err := protocol.UnmarshalTversionPkt(b)
104	if err != nil {
105		return err
106	}
107
108	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)})
113
114	return nil
115}
116
117// Replies with a tag mismatched tag message. The client should be able
118// 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 err
123	}
124
125	t += 1
126
127	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)})
132
133	return nil
134}
135
136// Replies with a type message that is valid but not expected by the
137// client. Thus the client should be able to parse this but should raise
138// an error.
139func HeaderTypeMismatch(b *bytes.Buffer) error {
140	_, _, t, err := protocol.UnmarshalTversionPkt(b)
141	if err != nil {
142		return err
143	}
144
145	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)})
150
151	return nil
152}
153
154// Replies with the msize and version send by the client. This should always be
155// 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 err
160	}
161
162	protocol.MarshalRversionPkt(b, t, TMsize, TVersion)
163	return nil
164}
165
166// Replies with the version string "unknown".
167//
168// From version(5):
169//   If the server does not understand the client's version
170//   string, it should respond with an Rversion message (not
171//   Rerror) with the version string the 7 characters
172//   ``unknown''.
173//
174// The client should therefore be able to parse this message but should
175// return an error.
176func RversionUnknown(b *bytes.Buffer) error {
177	TMsize, _, t, err := protocol.UnmarshalTversionPkt(b)
178	if err != nil {
179		return err
180	}
181
182	protocol.MarshalRversionPkt(b, t, TMsize, "unknown")
183	return nil
184}
185
186// Replies with an msize value equal to the one send by the client plus
187// one.
188//
189// From version(5):
190//   The server responds with its own maximum, msize, which must
191//   be less than or equal to the client's value.
192//
193// The client should therefore be able to parse this message but should
194// return an error.
195func RversionMsizeTooBig(b *bytes.Buffer) error {
196	TMsize, TVersion, t, err := protocol.UnmarshalTversionPkt(b)
197	if err != nil {
198		return err
199	}
200
201	protocol.MarshalRversionPkt(b, t, TMsize+1, TVersion)
202	return nil
203}
204
205// Replies with an invalid version string.
206//
207// From version(5):
208//  After stripping any such period-separated suffix, the server is
209//  allowed to respond with a string of the form 9Pnnnn, where nnnn is
210//  less than or equal to the digits sent by the client.
211//
212// Depending on the client implementation the client may not be able to
213// parse the R-message. In any case it should return an error since the
214// 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 err
219	}
220
221	protocol.MarshalRversionPkt(b, t, TMsize, "9P20009P2000")
222	return nil
223}
224
225// Replies with an length field in the version R-message.
226//
227// The value of the size field is one byte too short. Thereby causing
228// the length field of the version string to report a string size that
229// 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 err
236	}
237
238	protocol.MarshalRversionPkt(b, t, TMsize, TVersion)
239
240	{
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	}
244
245	return nil
246}
247
248// Replies with a version string that is one byte longer than the
249// longest valid version string `unknown`.
250//
251// The client will most likely be able to parse this messages but might
252// 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 err
257	}
258
259	protocol.MarshalRversionPkt(b, t, TMsize, "12345678")
260	return nil
261}
262
263// Successfully attaches the client. Replying with a vaild qid. The
264// 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 err
269	}
270
271	protocol.MarshalRattachPkt(b, t, protocol.QID{})
272	return nil
273}
274
275// Replies with a modified packet length field causing the packet length to be
276// one byte shorter than neccessary. The qid should therefore be invalind and
277// 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 err
282	}
283
284	protocol.MarshalRattachPkt(b, t, protocol.QID{})
285
286	{
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	}
290
291	return nil
292}
293
294// Replies with a valid Rstat message. The client must be able to parse
295// this R-message.
296func RstatSuccess(b *bytes.Buffer) error {
297	_, t, err := protocol.UnmarshalTstatPkt(b)
298	if err != nil {
299		return err
300	}
301
302	qid := protocol.QID{
303		Type:    23,
304		Version: 2342,
305		Path:    1337,
306	}
307
308	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	}
321
322	var B bytes.Buffer
323	protocol.Marshaldir(&B, dir)
324
325	protocol.MarshalRstatPkt(b, t, B.Bytes())
326	return nil
327}
328
329// Replies with an Rstat message that is too short to be valid. The
330// client must reject this message.
331func RstatTooShort(b *bytes.Buffer) error {
332	_, t, err := protocol.UnmarshalTstatPkt(b)
333	if err != nil {
334		return err
335	}
336
337	var D protocol.Dir
338	var B bytes.Buffer
339
340	protocol.Marshaldir(&B, D)
341	protocol.MarshalRstatPkt(b, t, B.Bytes())
342
343	{
344		var l uint64 = 57 // _9P_MINSTSIZ + _9P_HEADSIZ - 1
345		copy(b.Bytes(), []byte{uint8(l), uint8(l >> 8), uint8(l >> 16), uint8(l >> 24)})
346	}
347
348	return nil
349}
350
351// Replies with a valid Rwalk message. The last QID is different then
352// the other ones to test off-by-one errors. The client must be able
353// to parse this R-message.
354func RwalkSuccess(b *bytes.Buffer) error {
355	_, _, p, t, err := protocol.UnmarshalTwalkPkt(b)
356	if err != nil {
357		return err
358	}
359	plen := len(p)
360
361	q := protocol.QID{
362		Path:    23,
363		Type:    42,
364		Version: 5,
365	}
366
367	var qids []protocol.QID
368
369	for i := 0; i < plen; i++ {
370		qids = append(qids, q)
371	}
372
373	if plen > 0 {
374		qids[plen-1] = protocol.QID{
375			Path:    1337,
376			Type:    23,
377			Version: 42,
378		}
379	}
380
381	protocol.MarshalRwalkPkt(b, t, qids)
382	return nil
383}
384
385// 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 err
391	}
392
393	var qids []protocol.QID
394	for i := 0; i < len(p); i++ {
395		qids = append(qids, protocol.QID{})
396	}
397
398	protocol.MarshalRwalkPkt(b, t, qids)
399
400	{
401		var l uint64 = uint64(10)
402		copy(b.Bytes(), []byte{uint8(l), uint8(l >> 8), uint8(l >> 16), uint8(l >> 24)})
403	}
404
405	return nil
406}
407
408// Replies with a Rwalk message which contains an `nwqid` field that
409// exceeds MAXWELEM and should therefore be considered invalid by the
410// client.
411func RwalkNwqidTooLarge(b *bytes.Buffer) error {
412	_, _, _, t, err := protocol.UnmarshalTwalkPkt(b)
413	if err != nil {
414		return err
415	}
416
417	var qids []protocol.QID
418	for i := 0; i < 17; i++ {
419		qids = append(qids, protocol.QID{})
420	}
421
422	protocol.MarshalRwalkPkt(b, t, qids)
423	return nil
424}
425
426// Replies with a valid Ropen message. The iounit of the reply will
427// 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 err
432	}
433
434	protocol.MarshalRopenPkt(b, t, protocol.QID{}, 1337)
435	return nil
436}
437
438// Replies with a vaild Rcreate message. The client must be able to
439// parse this.
440func RcreateSuccess(b *bytes.Buffer) error {
441	_, _, _, _, t, err := protocol.UnmarshalTcreatePkt(b)
442	if err != nil {
443		return err
444	}
445
446	protocol.MarshalRcreatePkt(b, t, protocol.QID{}, 9001)
447	return nil
448}
449
450// Replies with a valid Rread message. The data field of the message
451// should hold the string `Hello!`. The client must be able to parse
452// 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 err
457	}
458
459	protocol.MarshalRreadPkt(b, t, []byte("Hello!"))
460	return nil
461}
462
463// Replies with a valid Rread message. The data field of the message is
464// set to `1234567890`. Contrary to RreadSuccess this function respects
465// 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 err
470	}
471
472	poff := int(o)
473	plen := int(l)
474
475	hstr := "1234567890"
476	hlen := len(hstr)
477
478	if poff > hlen {
479		return errors.New("offset is too large")
480	}
481	if plen > hlen {
482		hlen = plen
483	}
484
485	data := hstr[poff : poff+plen]
486
487	protocol.MarshalRreadPkt(b, t, []byte(data))
488	return nil
489}
490
491// Replies with a valid Rread message which contains a count field with
492// the value `0`. The client must be able to parse this but should
493// treat the message as an error.
494func RreadCountZero(b *bytes.Buffer) error {
495	_, _, _, t, err := protocol.UnmarshalTreadPkt(b)
496	if err != nil {
497		return err
498	}
499
500	protocol.MarshalRreadPkt(b, t, []byte(""))
501	return nil
502}
503
504// Replies with a valid Rwrite message. The client must be able to parse
505// this.
506func RwriteSuccess(b *bytes.Buffer) error {
507	_, _, Data, t, err := protocol.UnmarshalTwritePkt(b)
508	if err != nil {
509		return err
510	}
511
512	protocol.MarshalRwritePkt(b, t, protocol.Count(len(Data)))
513	return nil
514}
515
516// Replies with a valid Rclunk message. The client must be able to parse
517// this.
518func RclunkSuccess(b *bytes.Buffer) error {
519	_, t, err := protocol.UnmarshalTclunkPkt(b)
520	if err != nil {
521		return err
522	}
523
524	protocol.MarshalRclunkPkt(b, t)
525	return nil
526}
527
528// Replies with a valid Rremove message. The client must be able to
529// parse this.
530func RremoveSuccess(b *bytes.Buffer) error {
531	_, t, err := protocol.UnmarshalTremovePkt(b)
532	if err != nil {
533		return err
534	}
535
536	protocol.MarshalRremovePkt(b, t)
537	return nil
538}