github.com/pkg/sftp@v1.13.6/internal/encoding/ssh/filexfer/extended_packets.go (about)

     1  package sshfx
     2  
     3  import (
     4  	"encoding"
     5  	"sync"
     6  )
     7  
     8  // ExtendedData aliases the untyped interface composition of encoding.BinaryMarshaler and encoding.BinaryUnmarshaler.
     9  type ExtendedData = interface {
    10  	encoding.BinaryMarshaler
    11  	encoding.BinaryUnmarshaler
    12  }
    13  
    14  // ExtendedDataConstructor defines a function that returns a new(ArbitraryExtendedPacket).
    15  type ExtendedDataConstructor func() ExtendedData
    16  
    17  var extendedPacketTypes = struct {
    18  	mu           sync.RWMutex
    19  	constructors map[string]ExtendedDataConstructor
    20  }{
    21  	constructors: make(map[string]ExtendedDataConstructor),
    22  }
    23  
    24  // RegisterExtendedPacketType defines a specific ExtendedDataConstructor for the given extension string.
    25  func RegisterExtendedPacketType(extension string, constructor ExtendedDataConstructor) {
    26  	extendedPacketTypes.mu.Lock()
    27  	defer extendedPacketTypes.mu.Unlock()
    28  
    29  	if _, exist := extendedPacketTypes.constructors[extension]; exist {
    30  		panic("encoding/ssh/filexfer: multiple registration of extended packet type " + extension)
    31  	}
    32  
    33  	extendedPacketTypes.constructors[extension] = constructor
    34  }
    35  
    36  func newExtendedPacket(extension string) ExtendedData {
    37  	extendedPacketTypes.mu.RLock()
    38  	defer extendedPacketTypes.mu.RUnlock()
    39  
    40  	if f := extendedPacketTypes.constructors[extension]; f != nil {
    41  		return f()
    42  	}
    43  
    44  	return new(Buffer)
    45  }
    46  
    47  // ExtendedPacket defines the SSH_FXP_CLOSE packet.
    48  type ExtendedPacket struct {
    49  	ExtendedRequest string
    50  
    51  	Data ExtendedData
    52  }
    53  
    54  // Type returns the SSH_FXP_xy value associated with this packet type.
    55  func (p *ExtendedPacket) Type() PacketType {
    56  	return PacketTypeExtended
    57  }
    58  
    59  // MarshalPacket returns p as a two-part binary encoding of p.
    60  //
    61  // The Data is marshaled into binary, and returned as the payload.
    62  func (p *ExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
    63  	buf := NewBuffer(b)
    64  	if buf.Cap() < 9 {
    65  		size := 4 + len(p.ExtendedRequest) // string(extended-request)
    66  		buf = NewMarshalBuffer(size)
    67  	}
    68  
    69  	buf.StartPacket(PacketTypeExtended, reqid)
    70  	buf.AppendString(p.ExtendedRequest)
    71  
    72  	if p.Data != nil {
    73  		payload, err = p.Data.MarshalBinary()
    74  		if err != nil {
    75  			return nil, nil, err
    76  		}
    77  	}
    78  
    79  	return buf.Packet(payload)
    80  }
    81  
    82  // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
    83  // It is assumed that the uint32(request-id) has already been consumed.
    84  //
    85  // If p.Data is nil, and the extension has been registered, a new type will be made from the registration.
    86  // If the extension has not been registered, then a new Buffer will be allocated.
    87  // Then the request-specific-data will be unmarshaled from the rest of the buffer.
    88  func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
    89  	p.ExtendedRequest = buf.ConsumeString()
    90  	if buf.Err != nil {
    91  		return buf.Err
    92  	}
    93  
    94  	if p.Data == nil {
    95  		p.Data = newExtendedPacket(p.ExtendedRequest)
    96  	}
    97  
    98  	return p.Data.UnmarshalBinary(buf.Bytes())
    99  }
   100  
   101  // ExtendedReplyPacket defines the SSH_FXP_CLOSE packet.
   102  type ExtendedReplyPacket struct {
   103  	Data ExtendedData
   104  }
   105  
   106  // Type returns the SSH_FXP_xy value associated with this packet type.
   107  func (p *ExtendedReplyPacket) Type() PacketType {
   108  	return PacketTypeExtendedReply
   109  }
   110  
   111  // MarshalPacket returns p as a two-part binary encoding of p.
   112  //
   113  // The Data is marshaled into binary, and returned as the payload.
   114  func (p *ExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
   115  	buf := NewBuffer(b)
   116  	if buf.Cap() < 9 {
   117  		buf = NewMarshalBuffer(0)
   118  	}
   119  
   120  	buf.StartPacket(PacketTypeExtendedReply, reqid)
   121  
   122  	if p.Data != nil {
   123  		payload, err = p.Data.MarshalBinary()
   124  		if err != nil {
   125  			return nil, nil, err
   126  		}
   127  	}
   128  
   129  	return buf.Packet(payload)
   130  }
   131  
   132  // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
   133  // It is assumed that the uint32(request-id) has already been consumed.
   134  //
   135  // If p.Data is nil, and there is request-specific-data,
   136  // then the request-specific-data will be wrapped in a Buffer and assigned to p.Data.
   137  func (p *ExtendedReplyPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
   138  	if p.Data == nil {
   139  		p.Data = new(Buffer)
   140  	}
   141  
   142  	return p.Data.UnmarshalBinary(buf.Bytes())
   143  }