a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh
at main 36 kB view raw
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}