a geicko-2 based round robin ranking system designed to test c++ battleship submissions
battleship.dunkirk.sh
1package sftp
2
3import (
4 "bytes"
5 "encoding"
6 "encoding/binary"
7 "errors"
8 "fmt"
9 "io"
10 "os"
11 "reflect"
12)
13
14var (
15 errLongPacket = errors.New("packet too long")
16 errShortPacket = errors.New("packet too short")
17 errUnknownExtendedPacket = errors.New("unknown extended packet")
18)
19
20const (
21 maxMsgLength = 256 * 1024
22 debugDumpTxPacket = false
23 debugDumpRxPacket = false
24 debugDumpTxPacketBytes = false
25 debugDumpRxPacketBytes = false
26)
27
28func marshalUint32(b []byte, v uint32) []byte {
29 return append(b, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
30}
31
32func marshalUint64(b []byte, v uint64) []byte {
33 return marshalUint32(marshalUint32(b, uint32(v>>32)), uint32(v))
34}
35
36func marshalString(b []byte, v string) []byte {
37 return append(marshalUint32(b, uint32(len(v))), v...)
38}
39
40func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
41 // attributes variable struct, and also variable per protocol version
42 // spec version 3 attributes:
43 // uint32 flags
44 // uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
45 // uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
46 // uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
47 // uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
48 // uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
49 // uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
50 // uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
51 // string extended_type
52 // string extended_data
53 // ... more extended data (extended_type - extended_data pairs),
54 // so that number of pairs equals extended_count
55
56 flags, fileStat := fileStatFromInfo(fi)
57
58 b = marshalUint32(b, flags)
59
60 return marshalFileStat(b, flags, fileStat)
61}
62
63func marshalFileStat(b []byte, flags uint32, fileStat *FileStat) []byte {
64 if flags&sshFileXferAttrSize != 0 {
65 b = marshalUint64(b, fileStat.Size)
66 }
67 if flags&sshFileXferAttrUIDGID != 0 {
68 b = marshalUint32(b, fileStat.UID)
69 b = marshalUint32(b, fileStat.GID)
70 }
71 if flags&sshFileXferAttrPermissions != 0 {
72 b = marshalUint32(b, fileStat.Mode)
73 }
74 if flags&sshFileXferAttrACmodTime != 0 {
75 b = marshalUint32(b, fileStat.Atime)
76 b = marshalUint32(b, fileStat.Mtime)
77 }
78
79 if flags&sshFileXferAttrExtended != 0 {
80 b = marshalUint32(b, uint32(len(fileStat.Extended)))
81
82 for _, attr := range fileStat.Extended {
83 b = marshalString(b, attr.ExtType)
84 b = marshalString(b, attr.ExtData)
85 }
86 }
87
88 return b
89}
90
91func marshalStatus(b []byte, err StatusError) []byte {
92 b = marshalUint32(b, err.Code)
93 b = marshalString(b, err.msg)
94 b = marshalString(b, err.lang)
95 return b
96}
97
98func marshal(b []byte, v interface{}) []byte {
99 switch v := v.(type) {
100 case nil:
101 return b
102 case uint8:
103 return append(b, v)
104 case uint32:
105 return marshalUint32(b, v)
106 case uint64:
107 return marshalUint64(b, v)
108 case string:
109 return marshalString(b, v)
110 case []byte:
111 return append(b, v...)
112 case os.FileInfo:
113 return marshalFileInfo(b, v)
114 default:
115 switch d := reflect.ValueOf(v); d.Kind() {
116 case reflect.Struct:
117 for i, n := 0, d.NumField(); i < n; i++ {
118 b = marshal(b, d.Field(i).Interface())
119 }
120 return b
121 case reflect.Slice:
122 for i, n := 0, d.Len(); i < n; i++ {
123 b = marshal(b, d.Index(i).Interface())
124 }
125 return b
126 default:
127 panic(fmt.Sprintf("marshal(%#v): cannot handle type %T", v, v))
128 }
129 }
130}
131
132func unmarshalUint32(b []byte) (uint32, []byte) {
133 v := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
134 return v, b[4:]
135}
136
137func unmarshalUint32Safe(b []byte) (uint32, []byte, error) {
138 var v uint32
139 if len(b) < 4 {
140 return 0, nil, errShortPacket
141 }
142 v, b = unmarshalUint32(b)
143 return v, b, nil
144}
145
146func unmarshalUint64(b []byte) (uint64, []byte) {
147 h, b := unmarshalUint32(b)
148 l, b := unmarshalUint32(b)
149 return uint64(h)<<32 | uint64(l), b
150}
151
152func unmarshalUint64Safe(b []byte) (uint64, []byte, error) {
153 var v uint64
154 if len(b) < 8 {
155 return 0, nil, errShortPacket
156 }
157 v, b = unmarshalUint64(b)
158 return v, b, nil
159}
160
161func unmarshalString(b []byte) (string, []byte) {
162 n, b := unmarshalUint32(b)
163 return string(b[:n]), b[n:]
164}
165
166func unmarshalStringSafe(b []byte) (string, []byte, error) {
167 n, b, err := unmarshalUint32Safe(b)
168 if err != nil {
169 return "", nil, err
170 }
171 if int64(n) > int64(len(b)) {
172 return "", nil, errShortPacket
173 }
174 return string(b[:n]), b[n:], nil
175}
176
177func unmarshalAttrs(b []byte) (*FileStat, []byte, error) {
178 flags, b, err := unmarshalUint32Safe(b)
179 if err != nil {
180 return nil, b, err
181 }
182 return unmarshalFileStat(flags, b)
183}
184
185func unmarshalFileStat(flags uint32, b []byte) (*FileStat, []byte, error) {
186 var fs FileStat
187 var err error
188
189 if flags&sshFileXferAttrSize == sshFileXferAttrSize {
190 fs.Size, b, err = unmarshalUint64Safe(b)
191 if err != nil {
192 return nil, b, err
193 }
194 }
195 if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID {
196 fs.UID, b, err = unmarshalUint32Safe(b)
197 if err != nil {
198 return nil, b, err
199 }
200 fs.GID, b, err = unmarshalUint32Safe(b)
201 if err != nil {
202 return nil, b, err
203 }
204 }
205 if flags&sshFileXferAttrPermissions == sshFileXferAttrPermissions {
206 fs.Mode, b, err = unmarshalUint32Safe(b)
207 if err != nil {
208 return nil, b, err
209 }
210 }
211 if flags&sshFileXferAttrACmodTime == sshFileXferAttrACmodTime {
212 fs.Atime, b, err = unmarshalUint32Safe(b)
213 if err != nil {
214 return nil, b, err
215 }
216 fs.Mtime, b, err = unmarshalUint32Safe(b)
217 if err != nil {
218 return nil, b, err
219 }
220 }
221 if flags&sshFileXferAttrExtended == sshFileXferAttrExtended {
222 var count uint32
223 count, b, err = unmarshalUint32Safe(b)
224 if err != nil {
225 return nil, b, err
226 }
227
228 ext := make([]StatExtended, count)
229 for i := uint32(0); i < count; i++ {
230 var typ string
231 var data string
232 typ, b, err = unmarshalStringSafe(b)
233 if err != nil {
234 return nil, b, err
235 }
236 data, b, err = unmarshalStringSafe(b)
237 if err != nil {
238 return nil, b, err
239 }
240 ext[i] = StatExtended{
241 ExtType: typ,
242 ExtData: data,
243 }
244 }
245 fs.Extended = ext
246 }
247 return &fs, b, nil
248}
249
250func unmarshalStatus(id uint32, data []byte) error {
251 sid, data := unmarshalUint32(data)
252 if sid != id {
253 return &unexpectedIDErr{id, sid}
254 }
255 code, data := unmarshalUint32(data)
256 msg, data, _ := unmarshalStringSafe(data)
257 lang, _, _ := unmarshalStringSafe(data)
258 return &StatusError{
259 Code: code,
260 msg: msg,
261 lang: lang,
262 }
263}
264
265type packetMarshaler interface {
266 marshalPacket() (header, payload []byte, err error)
267}
268
269func marshalPacket(m encoding.BinaryMarshaler) (header, payload []byte, err error) {
270 if m, ok := m.(packetMarshaler); ok {
271 return m.marshalPacket()
272 }
273
274 header, err = m.MarshalBinary()
275 return
276}
277
278// sendPacket marshals p according to RFC 4234.
279func sendPacket(w io.Writer, m encoding.BinaryMarshaler) error {
280 header, payload, err := marshalPacket(m)
281 if err != nil {
282 return fmt.Errorf("binary marshaller failed: %w", err)
283 }
284
285 length := len(header) + len(payload) - 4 // subtract the uint32(length) from the start
286 if debugDumpTxPacketBytes {
287 debug("send packet: %s %d bytes %x%x", fxp(header[4]), length, header[5:], payload)
288 } else if debugDumpTxPacket {
289 debug("send packet: %s %d bytes", fxp(header[4]), length)
290 }
291
292 binary.BigEndian.PutUint32(header[:4], uint32(length))
293
294 if _, err := w.Write(header); err != nil {
295 return fmt.Errorf("failed to send packet: %w", err)
296 }
297
298 if len(payload) > 0 {
299 if _, err := w.Write(payload); err != nil {
300 return fmt.Errorf("failed to send packet payload: %w", err)
301 }
302 }
303
304 return nil
305}
306
307func recvPacket(r io.Reader, alloc *allocator, orderID uint32) (fxp, []byte, error) {
308 var b []byte
309 if alloc != nil {
310 b = alloc.GetPage(orderID)
311 } else {
312 b = make([]byte, 4)
313 }
314
315 if n, err := io.ReadFull(r, b[:4]); err != nil {
316 if err == io.EOF {
317 return 0, nil, err
318 }
319
320 return 0, nil, fmt.Errorf("error reading packet length: %d of 4: %w", n, err)
321 }
322
323 length, _ := unmarshalUint32(b)
324 if length > maxMsgLength {
325 debug("recv packet %d bytes too long", length)
326 return 0, nil, errLongPacket
327 }
328 if length == 0 {
329 debug("recv packet of 0 bytes too short")
330 return 0, nil, errShortPacket
331 }
332
333 if alloc == nil {
334 b = make([]byte, length)
335 }
336
337 n, err := io.ReadFull(r, b[:length])
338 b = b[:n]
339
340 if err != nil {
341 debug("recv packet error: %d of %d bytes: %x", n, length, b)
342
343 // ReadFull only returns EOF if it has read no bytes.
344 // In this case, that means a partial packet, and thus unexpected.
345 if err == io.EOF {
346 err = io.ErrUnexpectedEOF
347 }
348
349 if n == 0 {
350 return 0, nil, fmt.Errorf("error reading packet body: %d of %d: %w", n, length, err)
351 }
352
353 return 0, nil, fmt.Errorf("error reading packet body: %d of %d: (%s) %w", n, length, fxp(b[0]), err)
354 }
355
356 typ, payload := fxp(b[0]), b[1:n]
357
358 if debugDumpRxPacketBytes {
359 debug("recv packet: %s %d bytes %x", typ, length, payload)
360 } else if debugDumpRxPacket {
361 debug("recv packet: %s %d bytes", typ, length)
362 }
363
364 return typ, payload, nil
365}
366
367type extensionPair struct {
368 Name string
369 Data string
370}
371
372func unmarshalExtensionPair(b []byte) (extensionPair, []byte, error) {
373 var ep extensionPair
374 var err error
375 ep.Name, b, err = unmarshalStringSafe(b)
376 if err != nil {
377 return ep, b, err
378 }
379 ep.Data, b, err = unmarshalStringSafe(b)
380 return ep, b, err
381}
382
383// Here starts the definition of packets along with their MarshalBinary
384// implementations.
385// Manually writing the marshalling logic wins us a lot of time and
386// allocation.
387
388type sshFxInitPacket struct {
389 Version uint32
390 Extensions []extensionPair
391}
392
393func (p *sshFxInitPacket) MarshalBinary() ([]byte, error) {
394 l := 4 + 1 + 4 // uint32(length) + byte(type) + uint32(version)
395 for _, e := range p.Extensions {
396 l += 4 + len(e.Name) + 4 + len(e.Data)
397 }
398
399 b := make([]byte, 4, l)
400 b = append(b, sshFxpInit)
401 b = marshalUint32(b, p.Version)
402
403 for _, e := range p.Extensions {
404 b = marshalString(b, e.Name)
405 b = marshalString(b, e.Data)
406 }
407
408 return b, nil
409}
410
411func (p *sshFxInitPacket) UnmarshalBinary(b []byte) error {
412 var err error
413 if p.Version, b, err = unmarshalUint32Safe(b); err != nil {
414 return err
415 }
416 for len(b) > 0 {
417 var ep extensionPair
418 ep, b, err = unmarshalExtensionPair(b)
419 if err != nil {
420 return err
421 }
422 p.Extensions = append(p.Extensions, ep)
423 }
424 return nil
425}
426
427type sshFxVersionPacket struct {
428 Version uint32
429 Extensions []sshExtensionPair
430}
431
432type sshExtensionPair struct {
433 Name, Data string
434}
435
436func (p *sshFxVersionPacket) MarshalBinary() ([]byte, error) {
437 l := 4 + 1 + 4 // uint32(length) + byte(type) + uint32(version)
438 for _, e := range p.Extensions {
439 l += 4 + len(e.Name) + 4 + len(e.Data)
440 }
441
442 b := make([]byte, 4, l)
443 b = append(b, sshFxpVersion)
444 b = marshalUint32(b, p.Version)
445
446 for _, e := range p.Extensions {
447 b = marshalString(b, e.Name)
448 b = marshalString(b, e.Data)
449 }
450
451 return b, nil
452}
453
454func marshalIDStringPacket(packetType byte, id uint32, str string) ([]byte, error) {
455 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
456 4 + len(str)
457
458 b := make([]byte, 4, l)
459 b = append(b, packetType)
460 b = marshalUint32(b, id)
461 b = marshalString(b, str)
462
463 return b, nil
464}
465
466func unmarshalIDString(b []byte, id *uint32, str *string) error {
467 var err error
468 *id, b, err = unmarshalUint32Safe(b)
469 if err != nil {
470 return err
471 }
472 *str, _, err = unmarshalStringSafe(b)
473 return err
474}
475
476type sshFxpReaddirPacket struct {
477 ID uint32
478 Handle string
479}
480
481func (p *sshFxpReaddirPacket) id() uint32 { return p.ID }
482
483func (p *sshFxpReaddirPacket) MarshalBinary() ([]byte, error) {
484 return marshalIDStringPacket(sshFxpReaddir, p.ID, p.Handle)
485}
486
487func (p *sshFxpReaddirPacket) UnmarshalBinary(b []byte) error {
488 return unmarshalIDString(b, &p.ID, &p.Handle)
489}
490
491type sshFxpOpendirPacket struct {
492 ID uint32
493 Path string
494}
495
496func (p *sshFxpOpendirPacket) id() uint32 { return p.ID }
497
498func (p *sshFxpOpendirPacket) MarshalBinary() ([]byte, error) {
499 return marshalIDStringPacket(sshFxpOpendir, p.ID, p.Path)
500}
501
502func (p *sshFxpOpendirPacket) UnmarshalBinary(b []byte) error {
503 return unmarshalIDString(b, &p.ID, &p.Path)
504}
505
506type sshFxpLstatPacket struct {
507 ID uint32
508 Path string
509}
510
511func (p *sshFxpLstatPacket) id() uint32 { return p.ID }
512
513func (p *sshFxpLstatPacket) MarshalBinary() ([]byte, error) {
514 return marshalIDStringPacket(sshFxpLstat, p.ID, p.Path)
515}
516
517func (p *sshFxpLstatPacket) UnmarshalBinary(b []byte) error {
518 return unmarshalIDString(b, &p.ID, &p.Path)
519}
520
521type sshFxpStatPacket struct {
522 ID uint32
523 Path string
524}
525
526func (p *sshFxpStatPacket) id() uint32 { return p.ID }
527
528func (p *sshFxpStatPacket) MarshalBinary() ([]byte, error) {
529 return marshalIDStringPacket(sshFxpStat, p.ID, p.Path)
530}
531
532func (p *sshFxpStatPacket) UnmarshalBinary(b []byte) error {
533 return unmarshalIDString(b, &p.ID, &p.Path)
534}
535
536type sshFxpFstatPacket struct {
537 ID uint32
538 Handle string
539}
540
541func (p *sshFxpFstatPacket) id() uint32 { return p.ID }
542
543func (p *sshFxpFstatPacket) MarshalBinary() ([]byte, error) {
544 return marshalIDStringPacket(sshFxpFstat, p.ID, p.Handle)
545}
546
547func (p *sshFxpFstatPacket) UnmarshalBinary(b []byte) error {
548 return unmarshalIDString(b, &p.ID, &p.Handle)
549}
550
551type sshFxpClosePacket struct {
552 ID uint32
553 Handle string
554}
555
556func (p *sshFxpClosePacket) id() uint32 { return p.ID }
557
558func (p *sshFxpClosePacket) MarshalBinary() ([]byte, error) {
559 return marshalIDStringPacket(sshFxpClose, p.ID, p.Handle)
560}
561
562func (p *sshFxpClosePacket) UnmarshalBinary(b []byte) error {
563 return unmarshalIDString(b, &p.ID, &p.Handle)
564}
565
566type sshFxpRemovePacket struct {
567 ID uint32
568 Filename string
569}
570
571func (p *sshFxpRemovePacket) id() uint32 { return p.ID }
572
573func (p *sshFxpRemovePacket) MarshalBinary() ([]byte, error) {
574 return marshalIDStringPacket(sshFxpRemove, p.ID, p.Filename)
575}
576
577func (p *sshFxpRemovePacket) UnmarshalBinary(b []byte) error {
578 return unmarshalIDString(b, &p.ID, &p.Filename)
579}
580
581type sshFxpRmdirPacket struct {
582 ID uint32
583 Path string
584}
585
586func (p *sshFxpRmdirPacket) id() uint32 { return p.ID }
587
588func (p *sshFxpRmdirPacket) MarshalBinary() ([]byte, error) {
589 return marshalIDStringPacket(sshFxpRmdir, p.ID, p.Path)
590}
591
592func (p *sshFxpRmdirPacket) UnmarshalBinary(b []byte) error {
593 return unmarshalIDString(b, &p.ID, &p.Path)
594}
595
596type sshFxpSymlinkPacket struct {
597 ID uint32
598
599 // The order of the arguments to the SSH_FXP_SYMLINK method was inadvertently reversed.
600 // Unfortunately, the reversal was not noticed until the server was widely deployed.
601 // Covered in Section 4.1 of https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
602
603 Targetpath string
604 Linkpath string
605}
606
607func (p *sshFxpSymlinkPacket) id() uint32 { return p.ID }
608
609func (p *sshFxpSymlinkPacket) MarshalBinary() ([]byte, error) {
610 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
611 4 + len(p.Targetpath) +
612 4 + len(p.Linkpath)
613
614 b := make([]byte, 4, l)
615 b = append(b, sshFxpSymlink)
616 b = marshalUint32(b, p.ID)
617 b = marshalString(b, p.Targetpath)
618 b = marshalString(b, p.Linkpath)
619
620 return b, nil
621}
622
623func (p *sshFxpSymlinkPacket) UnmarshalBinary(b []byte) error {
624 var err error
625 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
626 return err
627 } else if p.Targetpath, b, err = unmarshalStringSafe(b); err != nil {
628 return err
629 } else if p.Linkpath, _, err = unmarshalStringSafe(b); err != nil {
630 return err
631 }
632 return nil
633}
634
635type sshFxpHardlinkPacket struct {
636 ID uint32
637 Oldpath string
638 Newpath string
639}
640
641func (p *sshFxpHardlinkPacket) id() uint32 { return p.ID }
642
643func (p *sshFxpHardlinkPacket) MarshalBinary() ([]byte, error) {
644 const ext = "hardlink@openssh.com"
645 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
646 4 + len(ext) +
647 4 + len(p.Oldpath) +
648 4 + len(p.Newpath)
649
650 b := make([]byte, 4, l)
651 b = append(b, sshFxpExtended)
652 b = marshalUint32(b, p.ID)
653 b = marshalString(b, ext)
654 b = marshalString(b, p.Oldpath)
655 b = marshalString(b, p.Newpath)
656
657 return b, nil
658}
659
660type sshFxpReadlinkPacket struct {
661 ID uint32
662 Path string
663}
664
665func (p *sshFxpReadlinkPacket) id() uint32 { return p.ID }
666
667func (p *sshFxpReadlinkPacket) MarshalBinary() ([]byte, error) {
668 return marshalIDStringPacket(sshFxpReadlink, p.ID, p.Path)
669}
670
671func (p *sshFxpReadlinkPacket) UnmarshalBinary(b []byte) error {
672 return unmarshalIDString(b, &p.ID, &p.Path)
673}
674
675type sshFxpRealpathPacket struct {
676 ID uint32
677 Path string
678}
679
680func (p *sshFxpRealpathPacket) id() uint32 { return p.ID }
681
682func (p *sshFxpRealpathPacket) MarshalBinary() ([]byte, error) {
683 return marshalIDStringPacket(sshFxpRealpath, p.ID, p.Path)
684}
685
686func (p *sshFxpRealpathPacket) UnmarshalBinary(b []byte) error {
687 return unmarshalIDString(b, &p.ID, &p.Path)
688}
689
690type sshFxpNameAttr struct {
691 Name string
692 LongName string
693 Attrs []interface{}
694}
695
696func (p *sshFxpNameAttr) MarshalBinary() ([]byte, error) {
697 var b []byte
698 b = marshalString(b, p.Name)
699 b = marshalString(b, p.LongName)
700 for _, attr := range p.Attrs {
701 b = marshal(b, attr)
702 }
703 return b, nil
704}
705
706type sshFxpNamePacket struct {
707 ID uint32
708 NameAttrs []*sshFxpNameAttr
709}
710
711func (p *sshFxpNamePacket) marshalPacket() ([]byte, []byte, error) {
712 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
713 4
714
715 b := make([]byte, 4, l)
716 b = append(b, sshFxpName)
717 b = marshalUint32(b, p.ID)
718 b = marshalUint32(b, uint32(len(p.NameAttrs)))
719
720 var payload []byte
721 for _, na := range p.NameAttrs {
722 ab, err := na.MarshalBinary()
723 if err != nil {
724 return nil, nil, err
725 }
726
727 payload = append(payload, ab...)
728 }
729
730 return b, payload, nil
731}
732
733func (p *sshFxpNamePacket) MarshalBinary() ([]byte, error) {
734 header, payload, err := p.marshalPacket()
735 return append(header, payload...), err
736}
737
738type sshFxpOpenPacket struct {
739 ID uint32
740 Path string
741 Pflags uint32
742 Flags uint32
743 Attrs interface{}
744}
745
746func (p *sshFxpOpenPacket) id() uint32 { return p.ID }
747
748func (p *sshFxpOpenPacket) marshalPacket() ([]byte, []byte, error) {
749 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
750 4 + len(p.Path) +
751 4 + 4
752
753 b := make([]byte, 4, l)
754 b = append(b, sshFxpOpen)
755 b = marshalUint32(b, p.ID)
756 b = marshalString(b, p.Path)
757 b = marshalUint32(b, p.Pflags)
758 b = marshalUint32(b, p.Flags)
759
760 switch attrs := p.Attrs.(type) {
761 case []byte:
762 return b, attrs, nil // may as well short-ciruit this case.
763 case os.FileInfo:
764 _, fs := fileStatFromInfo(attrs) // we throw away the flags, and override with those in packet.
765 return b, marshalFileStat(nil, p.Flags, fs), nil
766 case *FileStat:
767 return b, marshalFileStat(nil, p.Flags, attrs), nil
768 }
769
770 return b, marshal(nil, p.Attrs), nil
771}
772
773func (p *sshFxpOpenPacket) MarshalBinary() ([]byte, error) {
774 header, payload, err := p.marshalPacket()
775 return append(header, payload...), err
776}
777
778func (p *sshFxpOpenPacket) UnmarshalBinary(b []byte) error {
779 var err error
780 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
781 return err
782 } else if p.Path, b, err = unmarshalStringSafe(b); err != nil {
783 return err
784 } else if p.Pflags, b, err = unmarshalUint32Safe(b); err != nil {
785 return err
786 } else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil {
787 return err
788 }
789 p.Attrs = b
790 return nil
791}
792
793func (p *sshFxpOpenPacket) unmarshalFileStat(flags uint32) (*FileStat, error) {
794 switch attrs := p.Attrs.(type) {
795 case *FileStat:
796 return attrs, nil
797 case []byte:
798 fs, _, err := unmarshalFileStat(flags, attrs)
799 return fs, err
800 default:
801 return nil, fmt.Errorf("invalid type in unmarshalFileStat: %T", attrs)
802 }
803}
804
805type sshFxpReadPacket struct {
806 ID uint32
807 Len uint32
808 Offset uint64
809 Handle string
810}
811
812func (p *sshFxpReadPacket) id() uint32 { return p.ID }
813
814func (p *sshFxpReadPacket) MarshalBinary() ([]byte, error) {
815 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
816 4 + len(p.Handle) +
817 8 + 4 // uint64 + uint32
818
819 b := make([]byte, 4, l)
820 b = append(b, sshFxpRead)
821 b = marshalUint32(b, p.ID)
822 b = marshalString(b, p.Handle)
823 b = marshalUint64(b, p.Offset)
824 b = marshalUint32(b, p.Len)
825
826 return b, nil
827}
828
829func (p *sshFxpReadPacket) UnmarshalBinary(b []byte) error {
830 var err error
831 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
832 return err
833 } else if p.Handle, b, err = unmarshalStringSafe(b); err != nil {
834 return err
835 } else if p.Offset, b, err = unmarshalUint64Safe(b); err != nil {
836 return err
837 } else if p.Len, _, err = unmarshalUint32Safe(b); err != nil {
838 return err
839 }
840 return nil
841}
842
843// We need allocate bigger slices with extra capacity to avoid a re-allocation in sshFxpDataPacket.MarshalBinary
844// So, we need: uint32(length) + byte(type) + uint32(id) + uint32(data_length)
845const dataHeaderLen = 4 + 1 + 4 + 4
846
847func (p *sshFxpReadPacket) getDataSlice(alloc *allocator, orderID uint32, maxTxPacket uint32) []byte {
848 dataLen := p.Len
849 if dataLen > maxTxPacket {
850 dataLen = maxTxPacket
851 }
852
853 if alloc != nil {
854 // GetPage returns a slice with capacity = maxMsgLength this is enough to avoid new allocations in
855 // sshFxpDataPacket.MarshalBinary
856 return alloc.GetPage(orderID)[:dataLen]
857 }
858
859 // allocate with extra space for the header
860 return make([]byte, dataLen, dataLen+dataHeaderLen)
861}
862
863type sshFxpRenamePacket struct {
864 ID uint32
865 Oldpath string
866 Newpath string
867}
868
869func (p *sshFxpRenamePacket) id() uint32 { return p.ID }
870
871func (p *sshFxpRenamePacket) MarshalBinary() ([]byte, error) {
872 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
873 4 + len(p.Oldpath) +
874 4 + len(p.Newpath)
875
876 b := make([]byte, 4, l)
877 b = append(b, sshFxpRename)
878 b = marshalUint32(b, p.ID)
879 b = marshalString(b, p.Oldpath)
880 b = marshalString(b, p.Newpath)
881
882 return b, nil
883}
884
885func (p *sshFxpRenamePacket) UnmarshalBinary(b []byte) error {
886 var err error
887 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
888 return err
889 } else if p.Oldpath, b, err = unmarshalStringSafe(b); err != nil {
890 return err
891 } else if p.Newpath, _, err = unmarshalStringSafe(b); err != nil {
892 return err
893 }
894 return nil
895}
896
897type sshFxpPosixRenamePacket struct {
898 ID uint32
899 Oldpath string
900 Newpath string
901}
902
903func (p *sshFxpPosixRenamePacket) id() uint32 { return p.ID }
904
905func (p *sshFxpPosixRenamePacket) MarshalBinary() ([]byte, error) {
906 const ext = "posix-rename@openssh.com"
907 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
908 4 + len(ext) +
909 4 + len(p.Oldpath) +
910 4 + len(p.Newpath)
911
912 b := make([]byte, 4, l)
913 b = append(b, sshFxpExtended)
914 b = marshalUint32(b, p.ID)
915 b = marshalString(b, ext)
916 b = marshalString(b, p.Oldpath)
917 b = marshalString(b, p.Newpath)
918
919 return b, nil
920}
921
922type sshFxpWritePacket struct {
923 ID uint32
924 Length uint32
925 Offset uint64
926 Handle string
927 Data []byte
928}
929
930func (p *sshFxpWritePacket) id() uint32 { return p.ID }
931
932func (p *sshFxpWritePacket) marshalPacket() ([]byte, []byte, error) {
933 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
934 4 + len(p.Handle) +
935 8 + // uint64
936 4
937
938 b := make([]byte, 4, l)
939 b = append(b, sshFxpWrite)
940 b = marshalUint32(b, p.ID)
941 b = marshalString(b, p.Handle)
942 b = marshalUint64(b, p.Offset)
943 b = marshalUint32(b, p.Length)
944
945 return b, p.Data, nil
946}
947
948func (p *sshFxpWritePacket) MarshalBinary() ([]byte, error) {
949 header, payload, err := p.marshalPacket()
950 return append(header, payload...), err
951}
952
953func (p *sshFxpWritePacket) UnmarshalBinary(b []byte) error {
954 var err error
955 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
956 return err
957 } else if p.Handle, b, err = unmarshalStringSafe(b); err != nil {
958 return err
959 } else if p.Offset, b, err = unmarshalUint64Safe(b); err != nil {
960 return err
961 } else if p.Length, b, err = unmarshalUint32Safe(b); err != nil {
962 return err
963 } else if uint32(len(b)) < p.Length {
964 return errShortPacket
965 }
966
967 p.Data = b[:p.Length]
968 return nil
969}
970
971type sshFxpMkdirPacket struct {
972 ID uint32
973 Flags uint32 // ignored
974 Path string
975}
976
977func (p *sshFxpMkdirPacket) id() uint32 { return p.ID }
978
979func (p *sshFxpMkdirPacket) MarshalBinary() ([]byte, error) {
980 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
981 4 + len(p.Path) +
982 4 // uint32
983
984 b := make([]byte, 4, l)
985 b = append(b, sshFxpMkdir)
986 b = marshalUint32(b, p.ID)
987 b = marshalString(b, p.Path)
988 b = marshalUint32(b, p.Flags)
989
990 return b, nil
991}
992
993func (p *sshFxpMkdirPacket) UnmarshalBinary(b []byte) error {
994 var err error
995 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
996 return err
997 } else if p.Path, b, err = unmarshalStringSafe(b); err != nil {
998 return err
999 } else if p.Flags, _, err = unmarshalUint32Safe(b); err != nil {
1000 return err
1001 }
1002 return nil
1003}
1004
1005type sshFxpSetstatPacket struct {
1006 ID uint32
1007 Flags uint32
1008 Path string
1009 Attrs interface{}
1010}
1011
1012type sshFxpFsetstatPacket struct {
1013 ID uint32
1014 Flags uint32
1015 Handle string
1016 Attrs interface{}
1017}
1018
1019func (p *sshFxpSetstatPacket) id() uint32 { return p.ID }
1020func (p *sshFxpFsetstatPacket) id() uint32 { return p.ID }
1021
1022func (p *sshFxpSetstatPacket) marshalPacket() ([]byte, []byte, error) {
1023 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
1024 4 + len(p.Path) +
1025 4 // uint32
1026
1027 b := make([]byte, 4, l)
1028 b = append(b, sshFxpSetstat)
1029 b = marshalUint32(b, p.ID)
1030 b = marshalString(b, p.Path)
1031 b = marshalUint32(b, p.Flags)
1032
1033 switch attrs := p.Attrs.(type) {
1034 case []byte:
1035 return b, attrs, nil // may as well short-ciruit this case.
1036 case os.FileInfo:
1037 _, fs := fileStatFromInfo(attrs) // we throw away the flags, and override with those in packet.
1038 return b, marshalFileStat(nil, p.Flags, fs), nil
1039 case *FileStat:
1040 return b, marshalFileStat(nil, p.Flags, attrs), nil
1041 }
1042
1043 return b, marshal(nil, p.Attrs), nil
1044}
1045
1046func (p *sshFxpSetstatPacket) MarshalBinary() ([]byte, error) {
1047 header, payload, err := p.marshalPacket()
1048 return append(header, payload...), err
1049}
1050
1051func (p *sshFxpFsetstatPacket) marshalPacket() ([]byte, []byte, error) {
1052 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
1053 4 + len(p.Handle) +
1054 4 // uint32
1055
1056 b := make([]byte, 4, l)
1057 b = append(b, sshFxpFsetstat)
1058 b = marshalUint32(b, p.ID)
1059 b = marshalString(b, p.Handle)
1060 b = marshalUint32(b, p.Flags)
1061
1062 switch attrs := p.Attrs.(type) {
1063 case []byte:
1064 return b, attrs, nil // may as well short-ciruit this case.
1065 case os.FileInfo:
1066 _, fs := fileStatFromInfo(attrs) // we throw away the flags, and override with those in packet.
1067 return b, marshalFileStat(nil, p.Flags, fs), nil
1068 case *FileStat:
1069 return b, marshalFileStat(nil, p.Flags, attrs), nil
1070 }
1071
1072 return b, marshal(nil, p.Attrs), nil
1073}
1074
1075func (p *sshFxpFsetstatPacket) MarshalBinary() ([]byte, error) {
1076 header, payload, err := p.marshalPacket()
1077 return append(header, payload...), err
1078}
1079
1080func (p *sshFxpSetstatPacket) UnmarshalBinary(b []byte) error {
1081 var err error
1082 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
1083 return err
1084 } else if p.Path, b, err = unmarshalStringSafe(b); err != nil {
1085 return err
1086 } else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil {
1087 return err
1088 }
1089 p.Attrs = b
1090 return nil
1091}
1092
1093func (p *sshFxpSetstatPacket) unmarshalFileStat(flags uint32) (*FileStat, error) {
1094 switch attrs := p.Attrs.(type) {
1095 case *FileStat:
1096 return attrs, nil
1097 case []byte:
1098 fs, _, err := unmarshalFileStat(flags, attrs)
1099 return fs, err
1100 default:
1101 return nil, fmt.Errorf("invalid type in unmarshalFileStat: %T", attrs)
1102 }
1103}
1104
1105func (p *sshFxpFsetstatPacket) UnmarshalBinary(b []byte) error {
1106 var err error
1107 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
1108 return err
1109 } else if p.Handle, b, err = unmarshalStringSafe(b); err != nil {
1110 return err
1111 } else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil {
1112 return err
1113 }
1114 p.Attrs = b
1115 return nil
1116}
1117
1118func (p *sshFxpFsetstatPacket) unmarshalFileStat(flags uint32) (*FileStat, error) {
1119 switch attrs := p.Attrs.(type) {
1120 case *FileStat:
1121 return attrs, nil
1122 case []byte:
1123 fs, _, err := unmarshalFileStat(flags, attrs)
1124 return fs, err
1125 default:
1126 return nil, fmt.Errorf("invalid type in unmarshalFileStat: %T", attrs)
1127 }
1128}
1129
1130type sshFxpHandlePacket struct {
1131 ID uint32
1132 Handle string
1133}
1134
1135func (p *sshFxpHandlePacket) MarshalBinary() ([]byte, error) {
1136 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
1137 4 + len(p.Handle)
1138
1139 b := make([]byte, 4, l)
1140 b = append(b, sshFxpHandle)
1141 b = marshalUint32(b, p.ID)
1142 b = marshalString(b, p.Handle)
1143
1144 return b, nil
1145}
1146
1147type sshFxpStatusPacket struct {
1148 ID uint32
1149 StatusError
1150}
1151
1152func (p *sshFxpStatusPacket) MarshalBinary() ([]byte, error) {
1153 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
1154 4 +
1155 4 + len(p.StatusError.msg) +
1156 4 + len(p.StatusError.lang)
1157
1158 b := make([]byte, 4, l)
1159 b = append(b, sshFxpStatus)
1160 b = marshalUint32(b, p.ID)
1161 b = marshalStatus(b, p.StatusError)
1162
1163 return b, nil
1164}
1165
1166type sshFxpDataPacket struct {
1167 ID uint32
1168 Length uint32
1169 Data []byte
1170}
1171
1172func (p *sshFxpDataPacket) marshalPacket() ([]byte, []byte, error) {
1173 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
1174 4
1175
1176 b := make([]byte, 4, l)
1177 b = append(b, sshFxpData)
1178 b = marshalUint32(b, p.ID)
1179 b = marshalUint32(b, p.Length)
1180
1181 return b, p.Data, nil
1182}
1183
1184// MarshalBinary encodes the receiver into a binary form and returns the result.
1185// To avoid a new allocation the Data slice must have a capacity >= Length + 9
1186//
1187// This is hand-coded rather than just append(header, payload...),
1188// in order to try and reuse the r.Data backing store in the packet.
1189func (p *sshFxpDataPacket) MarshalBinary() ([]byte, error) {
1190 b := append(p.Data, make([]byte, dataHeaderLen)...)
1191 copy(b[dataHeaderLen:], p.Data[:p.Length])
1192 // b[0:4] will be overwritten with the length in sendPacket
1193 b[4] = sshFxpData
1194 binary.BigEndian.PutUint32(b[5:9], p.ID)
1195 binary.BigEndian.PutUint32(b[9:13], p.Length)
1196 return b, nil
1197}
1198
1199func (p *sshFxpDataPacket) UnmarshalBinary(b []byte) error {
1200 var err error
1201 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
1202 return err
1203 } else if p.Length, b, err = unmarshalUint32Safe(b); err != nil {
1204 return err
1205 } else if uint32(len(b)) < p.Length {
1206 return errShortPacket
1207 }
1208
1209 p.Data = b[:p.Length]
1210 return nil
1211}
1212
1213type sshFxpStatvfsPacket struct {
1214 ID uint32
1215 Path string
1216}
1217
1218func (p *sshFxpStatvfsPacket) id() uint32 { return p.ID }
1219
1220func (p *sshFxpStatvfsPacket) MarshalBinary() ([]byte, error) {
1221 const ext = "statvfs@openssh.com"
1222 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
1223 4 + len(ext) +
1224 4 + len(p.Path)
1225
1226 b := make([]byte, 4, l)
1227 b = append(b, sshFxpExtended)
1228 b = marshalUint32(b, p.ID)
1229 b = marshalString(b, ext)
1230 b = marshalString(b, p.Path)
1231
1232 return b, nil
1233}
1234
1235// A StatVFS contains statistics about a filesystem.
1236type StatVFS struct {
1237 ID uint32
1238 Bsize uint64 /* file system block size */
1239 Frsize uint64 /* fundamental fs block size */
1240 Blocks uint64 /* number of blocks (unit f_frsize) */
1241 Bfree uint64 /* free blocks in file system */
1242 Bavail uint64 /* free blocks for non-root */
1243 Files uint64 /* total file inodes */
1244 Ffree uint64 /* free file inodes */
1245 Favail uint64 /* free file inodes for to non-root */
1246 Fsid uint64 /* file system id */
1247 Flag uint64 /* bit mask of f_flag values */
1248 Namemax uint64 /* maximum filename length */
1249}
1250
1251// TotalSpace calculates the amount of total space in a filesystem.
1252func (p *StatVFS) TotalSpace() uint64 {
1253 return p.Frsize * p.Blocks
1254}
1255
1256// FreeSpace calculates the amount of free space in a filesystem.
1257func (p *StatVFS) FreeSpace() uint64 {
1258 return p.Frsize * p.Bfree
1259}
1260
1261// marshalPacket converts to ssh_FXP_EXTENDED_REPLY packet binary format
1262func (p *StatVFS) marshalPacket() ([]byte, []byte, error) {
1263 header := []byte{0, 0, 0, 0, sshFxpExtendedReply}
1264
1265 var buf bytes.Buffer
1266 err := binary.Write(&buf, binary.BigEndian, p)
1267
1268 return header, buf.Bytes(), err
1269}
1270
1271// MarshalBinary encodes the StatVFS as an SSH_FXP_EXTENDED_REPLY packet.
1272func (p *StatVFS) MarshalBinary() ([]byte, error) {
1273 header, payload, err := p.marshalPacket()
1274 return append(header, payload...), err
1275}
1276
1277type sshFxpFsyncPacket struct {
1278 ID uint32
1279 Handle string
1280}
1281
1282func (p *sshFxpFsyncPacket) id() uint32 { return p.ID }
1283
1284func (p *sshFxpFsyncPacket) MarshalBinary() ([]byte, error) {
1285 const ext = "fsync@openssh.com"
1286 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
1287 4 + len(ext) +
1288 4 + len(p.Handle)
1289
1290 b := make([]byte, 4, l)
1291 b = append(b, sshFxpExtended)
1292 b = marshalUint32(b, p.ID)
1293 b = marshalString(b, ext)
1294 b = marshalString(b, p.Handle)
1295
1296 return b, nil
1297}
1298
1299type sshFxpExtendedPacket struct {
1300 ID uint32
1301 ExtendedRequest string
1302 SpecificPacket interface {
1303 serverRespondablePacket
1304 readonly() bool
1305 }
1306}
1307
1308func (p *sshFxpExtendedPacket) id() uint32 { return p.ID }
1309func (p *sshFxpExtendedPacket) readonly() bool {
1310 if p.SpecificPacket == nil {
1311 return true
1312 }
1313 return p.SpecificPacket.readonly()
1314}
1315
1316func (p *sshFxpExtendedPacket) respond(svr *Server) responsePacket {
1317 if p.SpecificPacket == nil {
1318 return statusFromError(p.ID, nil)
1319 }
1320 return p.SpecificPacket.respond(svr)
1321}
1322
1323func (p *sshFxpExtendedPacket) UnmarshalBinary(b []byte) error {
1324 var err error
1325 bOrig := b
1326 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
1327 return err
1328 } else if p.ExtendedRequest, _, err = unmarshalStringSafe(b); err != nil {
1329 return err
1330 }
1331
1332 // specific unmarshalling
1333 switch p.ExtendedRequest {
1334 case "statvfs@openssh.com":
1335 p.SpecificPacket = &sshFxpExtendedPacketStatVFS{}
1336 case "posix-rename@openssh.com":
1337 p.SpecificPacket = &sshFxpExtendedPacketPosixRename{}
1338 case "hardlink@openssh.com":
1339 p.SpecificPacket = &sshFxpExtendedPacketHardlink{}
1340 default:
1341 return fmt.Errorf("packet type %v: %w", p.SpecificPacket, errUnknownExtendedPacket)
1342 }
1343
1344 return p.SpecificPacket.UnmarshalBinary(bOrig)
1345}
1346
1347type sshFxpExtendedPacketStatVFS struct {
1348 ID uint32
1349 ExtendedRequest string
1350 Path string
1351}
1352
1353func (p *sshFxpExtendedPacketStatVFS) id() uint32 { return p.ID }
1354func (p *sshFxpExtendedPacketStatVFS) readonly() bool { return true }
1355func (p *sshFxpExtendedPacketStatVFS) UnmarshalBinary(b []byte) error {
1356 var err error
1357 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
1358 return err
1359 } else if p.ExtendedRequest, b, err = unmarshalStringSafe(b); err != nil {
1360 return err
1361 } else if p.Path, _, err = unmarshalStringSafe(b); err != nil {
1362 return err
1363 }
1364 return nil
1365}
1366
1367type sshFxpExtendedPacketPosixRename struct {
1368 ID uint32
1369 ExtendedRequest string
1370 Oldpath string
1371 Newpath string
1372}
1373
1374func (p *sshFxpExtendedPacketPosixRename) id() uint32 { return p.ID }
1375func (p *sshFxpExtendedPacketPosixRename) readonly() bool { return false }
1376func (p *sshFxpExtendedPacketPosixRename) UnmarshalBinary(b []byte) error {
1377 var err error
1378 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
1379 return err
1380 } else if p.ExtendedRequest, b, err = unmarshalStringSafe(b); err != nil {
1381 return err
1382 } else if p.Oldpath, b, err = unmarshalStringSafe(b); err != nil {
1383 return err
1384 } else if p.Newpath, _, err = unmarshalStringSafe(b); err != nil {
1385 return err
1386 }
1387 return nil
1388}
1389
1390func (p *sshFxpExtendedPacketPosixRename) respond(s *Server) responsePacket {
1391 err := os.Rename(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath))
1392 return statusFromError(p.ID, err)
1393}
1394
1395type sshFxpExtendedPacketHardlink struct {
1396 ID uint32
1397 ExtendedRequest string
1398 Oldpath string
1399 Newpath string
1400}
1401
1402// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
1403func (p *sshFxpExtendedPacketHardlink) id() uint32 { return p.ID }
1404func (p *sshFxpExtendedPacketHardlink) readonly() bool { return true }
1405func (p *sshFxpExtendedPacketHardlink) UnmarshalBinary(b []byte) error {
1406 var err error
1407 if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
1408 return err
1409 } else if p.ExtendedRequest, b, err = unmarshalStringSafe(b); err != nil {
1410 return err
1411 } else if p.Oldpath, b, err = unmarshalStringSafe(b); err != nil {
1412 return err
1413 } else if p.Newpath, _, err = unmarshalStringSafe(b); err != nil {
1414 return err
1415 }
1416 return nil
1417}
1418
1419func (p *sshFxpExtendedPacketHardlink) respond(s *Server) responsePacket {
1420 err := os.Link(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath))
1421 return statusFromError(p.ID, err)
1422}