Example #1
0
// Names extracts the repeated name data from the Data buffer. This message
// structure is not supported by Unmarshal, so we take advantage of the "rest"
// field.
func (r *fxpNameResp) names() ([]nameData, error) {
	if r.Count == 0 {
		return nil, nil
	}
	data := r.Data
	r.Data = nil
	names := make([]nameData, 0, r.Count)
	for len(data) > 0 {
		name := fxpNameData{}
		if err := ssh.Unmarshal(data, &name); err != nil {
			return nil, err
		}
		data = name.Data
		var attr *FileAttributes
		var err error
		attr, data, err = newFileAttributes(data)
		if err != nil {
			return nil, err
		}
		names = append(names, nameData{
			filename: name.Filename,
			longname: name.Longname,
			attr:     attr,
		})
	}
	if len(data) != 0 {
		return nil, fmt.Errorf("Expected to have 0 bytes of data left, have %d", len(data))
	}

	return names, nil
}
Example #2
0
// decodeClient decodes a response packet's raw data into its corresponding
// message structure.
func decodeClient(packet []byte) (interface{}, error) {
	var msg interface{}
	switch packet[0] {
	case fxpPacketVersion:
		msg = new(fxpVersionMsg)
	case fxpPacketStatus:
		msg = new(fxpStatusResp)
	case fxpPacketHandle:
		msg = new(fxpHandleResp)
	case fxpPacketData:
		msg = new(fxpDataResp)
	case fxpPacketName:
		msg = new(fxpNameResp)
	case fxpPacketAttrs:
		msg = new(fxpAttrsResp)
	case fxpPacketExtendedReply:
		msg = new(fxpExtendedResp)
	default:
		return nil, UnexpectedMessageError{0, packet[0]}
	}
	if err := ssh.Unmarshal(packet, msg); err != nil {
		return nil, err
	}
	return msg, nil
}
Example #3
0
// init starts the SFTP protocol by negotiating the protocol version to use and
// starts the response handler in a goroutine.
func (s *Client) init() error {
	msg := fxpInitMsg{
		Version: 3,
	}
	if err := s.writePacket(ssh.Marshal(msg)); err != nil {
		return err
	}
	packet, err := s.readOnePacket()
	if err != nil {
		return err
	}
	resp, err := decodeClient(packet)
	if err != nil {
		return err
	}
	switch resp := resp.(type) {
	case *fxpVersionMsg:
		if resp.Version != 3 {
			return errors.New("only version 3 of Client protocol supported")
		}
	default:
		return errors.New("invalid packet received during initialization")
	}
	vers := resp.(*fxpVersionMsg)
	s.exts = make(map[string]extension)
	if len(vers.Ext) > 0 {
		exts := vers.Ext
		for len(exts) > 0 {
			ew := extensionWire{}
			if err := ssh.Unmarshal(exts, &ew); err != nil {
				return err
			}
			if len(exts) < 2 {
				break
			}
			exts = ew.Rest

			e := extension{
				Name: ew.Name,
				Data: ew.Data,
			}
			// OpenSSH's sftp-server implementation specifies that
			// the data portion of an extension is an ASCII-encoded
			// version number. This is not part of the SFTP
			// specification, however.
			if n, err := strconv.Atoi(ew.Data); err == nil {
				e.version = n
			}
			s.exts[e.Name] = e
		}
	}
	go s.mainLoop()

	return nil
}
Example #4
0
// newFileAttributes returns a new FileAttributes that represents the passed
// data.
func newFileAttributes(data []byte) (_ *FileAttributes, out []byte, err error) {
	// TODO(ekg): add error len(data) doesn't have enough bytes.
	f := &FileAttributes{
		flags: binary.BigEndian.Uint32(data[0:4]),
	}
	data = data[4:]
	if f.flags&fxpAttrSize > 0 {
		f.size = binary.BigEndian.Uint64(data[0:8])
		data = data[8:]
	}
	if f.flags&fxpAttrUIDGID > 0 {
		f.uid = binary.BigEndian.Uint32(data[0:4])
		f.gid = binary.BigEndian.Uint32(data[4:8])
		data = data[8:]
	}
	if f.flags&fxpAttrPermissions > 0 {
		f.permission = binary.BigEndian.Uint32(data[0:4])
		data = data[4:]
	}
	if f.flags&fxpAttrACModTime > 0 {
		f.aTime = binary.BigEndian.Uint32(data[0:4])
		f.mTime = binary.BigEndian.Uint32(data[4:8])
		data = data[8:]
	}
	if f.flags&fxpAttrExtended > 0 {
		c := binary.BigEndian.Uint32(data[0:4])
		data = data[4:]
		for i := 0; i < int(c); i++ {
			e := extInfo{}
			if err = ssh.Unmarshal(data, &e); err != nil {
				return
			}
			data = e.Rest
			e.Rest = nil
			f.ext = append(f.ext, e)
		}
	}
	return f, data, nil
}