435c0d9fc8
This PR switches the Nomad repository from using govendor to Go modules for managing dependencies. Aspects of the Nomad workflow remain pretty much the same. The usual Makefile targets should continue to work as they always did. The API submodule simply defers to the parent Nomad version on the repository, keeping the semantics of API versioning that currently exists.
867 lines
20 KiB
Go
867 lines
20 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package ssh
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// These are SSH message type numbers. They are scattered around several
|
|
// documents but many were taken from [SSH-PARAMETERS].
|
|
const (
|
|
msgIgnore = 2
|
|
msgUnimplemented = 3
|
|
msgDebug = 4
|
|
msgNewKeys = 21
|
|
)
|
|
|
|
// SSH messages:
|
|
//
|
|
// These structures mirror the wire format of the corresponding SSH messages.
|
|
// They are marshaled using reflection with the marshal and unmarshal functions
|
|
// in this file. The only wrinkle is that a final member of type []byte with a
|
|
// ssh tag of "rest" receives the remainder of a packet when unmarshaling.
|
|
|
|
// See RFC 4253, section 11.1.
|
|
const msgDisconnect = 1
|
|
|
|
// disconnectMsg is the message that signals a disconnect. It is also
|
|
// the error type returned from mux.Wait()
|
|
type disconnectMsg struct {
|
|
Reason uint32 `sshtype:"1"`
|
|
Message string
|
|
Language string
|
|
}
|
|
|
|
func (d *disconnectMsg) Error() string {
|
|
return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message)
|
|
}
|
|
|
|
// See RFC 4253, section 7.1.
|
|
const msgKexInit = 20
|
|
|
|
type kexInitMsg struct {
|
|
Cookie [16]byte `sshtype:"20"`
|
|
KexAlgos []string
|
|
ServerHostKeyAlgos []string
|
|
CiphersClientServer []string
|
|
CiphersServerClient []string
|
|
MACsClientServer []string
|
|
MACsServerClient []string
|
|
CompressionClientServer []string
|
|
CompressionServerClient []string
|
|
LanguagesClientServer []string
|
|
LanguagesServerClient []string
|
|
FirstKexFollows bool
|
|
Reserved uint32
|
|
}
|
|
|
|
// See RFC 4253, section 8.
|
|
|
|
// Diffie-Helman
|
|
const msgKexDHInit = 30
|
|
|
|
type kexDHInitMsg struct {
|
|
X *big.Int `sshtype:"30"`
|
|
}
|
|
|
|
const msgKexECDHInit = 30
|
|
|
|
type kexECDHInitMsg struct {
|
|
ClientPubKey []byte `sshtype:"30"`
|
|
}
|
|
|
|
const msgKexECDHReply = 31
|
|
|
|
type kexECDHReplyMsg struct {
|
|
HostKey []byte `sshtype:"31"`
|
|
EphemeralPubKey []byte
|
|
Signature []byte
|
|
}
|
|
|
|
const msgKexDHReply = 31
|
|
|
|
type kexDHReplyMsg struct {
|
|
HostKey []byte `sshtype:"31"`
|
|
Y *big.Int
|
|
Signature []byte
|
|
}
|
|
|
|
// See RFC 4419, section 5.
|
|
const msgKexDHGexGroup = 31
|
|
|
|
type kexDHGexGroupMsg struct {
|
|
P *big.Int `sshtype:"31"`
|
|
G *big.Int
|
|
}
|
|
|
|
const msgKexDHGexInit = 32
|
|
|
|
type kexDHGexInitMsg struct {
|
|
X *big.Int `sshtype:"32"`
|
|
}
|
|
|
|
const msgKexDHGexReply = 33
|
|
|
|
type kexDHGexReplyMsg struct {
|
|
HostKey []byte `sshtype:"33"`
|
|
Y *big.Int
|
|
Signature []byte
|
|
}
|
|
|
|
const msgKexDHGexRequest = 34
|
|
|
|
type kexDHGexRequestMsg struct {
|
|
MinBits uint32 `sshtype:"34"`
|
|
PreferedBits uint32
|
|
MaxBits uint32
|
|
}
|
|
|
|
// See RFC 4253, section 10.
|
|
const msgServiceRequest = 5
|
|
|
|
type serviceRequestMsg struct {
|
|
Service string `sshtype:"5"`
|
|
}
|
|
|
|
// See RFC 4253, section 10.
|
|
const msgServiceAccept = 6
|
|
|
|
type serviceAcceptMsg struct {
|
|
Service string `sshtype:"6"`
|
|
}
|
|
|
|
// See RFC 4252, section 5.
|
|
const msgUserAuthRequest = 50
|
|
|
|
type userAuthRequestMsg struct {
|
|
User string `sshtype:"50"`
|
|
Service string
|
|
Method string
|
|
Payload []byte `ssh:"rest"`
|
|
}
|
|
|
|
// Used for debug printouts of packets.
|
|
type userAuthSuccessMsg struct {
|
|
}
|
|
|
|
// See RFC 4252, section 5.1
|
|
const msgUserAuthFailure = 51
|
|
|
|
type userAuthFailureMsg struct {
|
|
Methods []string `sshtype:"51"`
|
|
PartialSuccess bool
|
|
}
|
|
|
|
// See RFC 4252, section 5.1
|
|
const msgUserAuthSuccess = 52
|
|
|
|
// See RFC 4252, section 5.4
|
|
const msgUserAuthBanner = 53
|
|
|
|
type userAuthBannerMsg struct {
|
|
Message string `sshtype:"53"`
|
|
// unused, but required to allow message parsing
|
|
Language string
|
|
}
|
|
|
|
// See RFC 4256, section 3.2
|
|
const msgUserAuthInfoRequest = 60
|
|
const msgUserAuthInfoResponse = 61
|
|
|
|
type userAuthInfoRequestMsg struct {
|
|
User string `sshtype:"60"`
|
|
Instruction string
|
|
DeprecatedLanguage string
|
|
NumPrompts uint32
|
|
Prompts []byte `ssh:"rest"`
|
|
}
|
|
|
|
// See RFC 4254, section 5.1.
|
|
const msgChannelOpen = 90
|
|
|
|
type channelOpenMsg struct {
|
|
ChanType string `sshtype:"90"`
|
|
PeersID uint32
|
|
PeersWindow uint32
|
|
MaxPacketSize uint32
|
|
TypeSpecificData []byte `ssh:"rest"`
|
|
}
|
|
|
|
const msgChannelExtendedData = 95
|
|
const msgChannelData = 94
|
|
|
|
// Used for debug print outs of packets.
|
|
type channelDataMsg struct {
|
|
PeersID uint32 `sshtype:"94"`
|
|
Length uint32
|
|
Rest []byte `ssh:"rest"`
|
|
}
|
|
|
|
// See RFC 4254, section 5.1.
|
|
const msgChannelOpenConfirm = 91
|
|
|
|
type channelOpenConfirmMsg struct {
|
|
PeersID uint32 `sshtype:"91"`
|
|
MyID uint32
|
|
MyWindow uint32
|
|
MaxPacketSize uint32
|
|
TypeSpecificData []byte `ssh:"rest"`
|
|
}
|
|
|
|
// See RFC 4254, section 5.1.
|
|
const msgChannelOpenFailure = 92
|
|
|
|
type channelOpenFailureMsg struct {
|
|
PeersID uint32 `sshtype:"92"`
|
|
Reason RejectionReason
|
|
Message string
|
|
Language string
|
|
}
|
|
|
|
const msgChannelRequest = 98
|
|
|
|
type channelRequestMsg struct {
|
|
PeersID uint32 `sshtype:"98"`
|
|
Request string
|
|
WantReply bool
|
|
RequestSpecificData []byte `ssh:"rest"`
|
|
}
|
|
|
|
// See RFC 4254, section 5.4.
|
|
const msgChannelSuccess = 99
|
|
|
|
type channelRequestSuccessMsg struct {
|
|
PeersID uint32 `sshtype:"99"`
|
|
}
|
|
|
|
// See RFC 4254, section 5.4.
|
|
const msgChannelFailure = 100
|
|
|
|
type channelRequestFailureMsg struct {
|
|
PeersID uint32 `sshtype:"100"`
|
|
}
|
|
|
|
// See RFC 4254, section 5.3
|
|
const msgChannelClose = 97
|
|
|
|
type channelCloseMsg struct {
|
|
PeersID uint32 `sshtype:"97"`
|
|
}
|
|
|
|
// See RFC 4254, section 5.3
|
|
const msgChannelEOF = 96
|
|
|
|
type channelEOFMsg struct {
|
|
PeersID uint32 `sshtype:"96"`
|
|
}
|
|
|
|
// See RFC 4254, section 4
|
|
const msgGlobalRequest = 80
|
|
|
|
type globalRequestMsg struct {
|
|
Type string `sshtype:"80"`
|
|
WantReply bool
|
|
Data []byte `ssh:"rest"`
|
|
}
|
|
|
|
// See RFC 4254, section 4
|
|
const msgRequestSuccess = 81
|
|
|
|
type globalRequestSuccessMsg struct {
|
|
Data []byte `ssh:"rest" sshtype:"81"`
|
|
}
|
|
|
|
// See RFC 4254, section 4
|
|
const msgRequestFailure = 82
|
|
|
|
type globalRequestFailureMsg struct {
|
|
Data []byte `ssh:"rest" sshtype:"82"`
|
|
}
|
|
|
|
// See RFC 4254, section 5.2
|
|
const msgChannelWindowAdjust = 93
|
|
|
|
type windowAdjustMsg struct {
|
|
PeersID uint32 `sshtype:"93"`
|
|
AdditionalBytes uint32
|
|
}
|
|
|
|
// See RFC 4252, section 7
|
|
const msgUserAuthPubKeyOk = 60
|
|
|
|
type userAuthPubKeyOkMsg struct {
|
|
Algo string `sshtype:"60"`
|
|
PubKey []byte
|
|
}
|
|
|
|
// See RFC 4462, section 3
|
|
const msgUserAuthGSSAPIResponse = 60
|
|
|
|
type userAuthGSSAPIResponse struct {
|
|
SupportMech []byte `sshtype:"60"`
|
|
}
|
|
|
|
const msgUserAuthGSSAPIToken = 61
|
|
|
|
type userAuthGSSAPIToken struct {
|
|
Token []byte `sshtype:"61"`
|
|
}
|
|
|
|
const msgUserAuthGSSAPIMIC = 66
|
|
|
|
type userAuthGSSAPIMIC struct {
|
|
MIC []byte `sshtype:"66"`
|
|
}
|
|
|
|
// See RFC 4462, section 3.9
|
|
const msgUserAuthGSSAPIErrTok = 64
|
|
|
|
type userAuthGSSAPIErrTok struct {
|
|
ErrorToken []byte `sshtype:"64"`
|
|
}
|
|
|
|
// See RFC 4462, section 3.8
|
|
const msgUserAuthGSSAPIError = 65
|
|
|
|
type userAuthGSSAPIError struct {
|
|
MajorStatus uint32 `sshtype:"65"`
|
|
MinorStatus uint32
|
|
Message string
|
|
LanguageTag string
|
|
}
|
|
|
|
// typeTags returns the possible type bytes for the given reflect.Type, which
|
|
// should be a struct. The possible values are separated by a '|' character.
|
|
func typeTags(structType reflect.Type) (tags []byte) {
|
|
tagStr := structType.Field(0).Tag.Get("sshtype")
|
|
|
|
for _, tag := range strings.Split(tagStr, "|") {
|
|
i, err := strconv.Atoi(tag)
|
|
if err == nil {
|
|
tags = append(tags, byte(i))
|
|
}
|
|
}
|
|
|
|
return tags
|
|
}
|
|
|
|
func fieldError(t reflect.Type, field int, problem string) error {
|
|
if problem != "" {
|
|
problem = ": " + problem
|
|
}
|
|
return fmt.Errorf("ssh: unmarshal error for field %s of type %s%s", t.Field(field).Name, t.Name(), problem)
|
|
}
|
|
|
|
var errShortRead = errors.New("ssh: short read")
|
|
|
|
// Unmarshal parses data in SSH wire format into a structure. The out
|
|
// argument should be a pointer to struct. If the first member of the
|
|
// struct has the "sshtype" tag set to a '|'-separated set of numbers
|
|
// in decimal, the packet must start with one of those numbers. In
|
|
// case of error, Unmarshal returns a ParseError or
|
|
// UnexpectedMessageError.
|
|
func Unmarshal(data []byte, out interface{}) error {
|
|
v := reflect.ValueOf(out).Elem()
|
|
structType := v.Type()
|
|
expectedTypes := typeTags(structType)
|
|
|
|
var expectedType byte
|
|
if len(expectedTypes) > 0 {
|
|
expectedType = expectedTypes[0]
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
return parseError(expectedType)
|
|
}
|
|
|
|
if len(expectedTypes) > 0 {
|
|
goodType := false
|
|
for _, e := range expectedTypes {
|
|
if e > 0 && data[0] == e {
|
|
goodType = true
|
|
break
|
|
}
|
|
}
|
|
if !goodType {
|
|
return fmt.Errorf("ssh: unexpected message type %d (expected one of %v)", data[0], expectedTypes)
|
|
}
|
|
data = data[1:]
|
|
}
|
|
|
|
var ok bool
|
|
for i := 0; i < v.NumField(); i++ {
|
|
field := v.Field(i)
|
|
t := field.Type()
|
|
switch t.Kind() {
|
|
case reflect.Bool:
|
|
if len(data) < 1 {
|
|
return errShortRead
|
|
}
|
|
field.SetBool(data[0] != 0)
|
|
data = data[1:]
|
|
case reflect.Array:
|
|
if t.Elem().Kind() != reflect.Uint8 {
|
|
return fieldError(structType, i, "array of unsupported type")
|
|
}
|
|
if len(data) < t.Len() {
|
|
return errShortRead
|
|
}
|
|
for j, n := 0, t.Len(); j < n; j++ {
|
|
field.Index(j).Set(reflect.ValueOf(data[j]))
|
|
}
|
|
data = data[t.Len():]
|
|
case reflect.Uint64:
|
|
var u64 uint64
|
|
if u64, data, ok = parseUint64(data); !ok {
|
|
return errShortRead
|
|
}
|
|
field.SetUint(u64)
|
|
case reflect.Uint32:
|
|
var u32 uint32
|
|
if u32, data, ok = parseUint32(data); !ok {
|
|
return errShortRead
|
|
}
|
|
field.SetUint(uint64(u32))
|
|
case reflect.Uint8:
|
|
if len(data) < 1 {
|
|
return errShortRead
|
|
}
|
|
field.SetUint(uint64(data[0]))
|
|
data = data[1:]
|
|
case reflect.String:
|
|
var s []byte
|
|
if s, data, ok = parseString(data); !ok {
|
|
return fieldError(structType, i, "")
|
|
}
|
|
field.SetString(string(s))
|
|
case reflect.Slice:
|
|
switch t.Elem().Kind() {
|
|
case reflect.Uint8:
|
|
if structType.Field(i).Tag.Get("ssh") == "rest" {
|
|
field.Set(reflect.ValueOf(data))
|
|
data = nil
|
|
} else {
|
|
var s []byte
|
|
if s, data, ok = parseString(data); !ok {
|
|
return errShortRead
|
|
}
|
|
field.Set(reflect.ValueOf(s))
|
|
}
|
|
case reflect.String:
|
|
var nl []string
|
|
if nl, data, ok = parseNameList(data); !ok {
|
|
return errShortRead
|
|
}
|
|
field.Set(reflect.ValueOf(nl))
|
|
default:
|
|
return fieldError(structType, i, "slice of unsupported type")
|
|
}
|
|
case reflect.Ptr:
|
|
if t == bigIntType {
|
|
var n *big.Int
|
|
if n, data, ok = parseInt(data); !ok {
|
|
return errShortRead
|
|
}
|
|
field.Set(reflect.ValueOf(n))
|
|
} else {
|
|
return fieldError(structType, i, "pointer to unsupported type")
|
|
}
|
|
default:
|
|
return fieldError(structType, i, fmt.Sprintf("unsupported type: %v", t))
|
|
}
|
|
}
|
|
|
|
if len(data) != 0 {
|
|
return parseError(expectedType)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Marshal serializes the message in msg to SSH wire format. The msg
|
|
// argument should be a struct or pointer to struct. If the first
|
|
// member has the "sshtype" tag set to a number in decimal, that
|
|
// number is prepended to the result. If the last of member has the
|
|
// "ssh" tag set to "rest", its contents are appended to the output.
|
|
func Marshal(msg interface{}) []byte {
|
|
out := make([]byte, 0, 64)
|
|
return marshalStruct(out, msg)
|
|
}
|
|
|
|
func marshalStruct(out []byte, msg interface{}) []byte {
|
|
v := reflect.Indirect(reflect.ValueOf(msg))
|
|
msgTypes := typeTags(v.Type())
|
|
if len(msgTypes) > 0 {
|
|
out = append(out, msgTypes[0])
|
|
}
|
|
|
|
for i, n := 0, v.NumField(); i < n; i++ {
|
|
field := v.Field(i)
|
|
switch t := field.Type(); t.Kind() {
|
|
case reflect.Bool:
|
|
var v uint8
|
|
if field.Bool() {
|
|
v = 1
|
|
}
|
|
out = append(out, v)
|
|
case reflect.Array:
|
|
if t.Elem().Kind() != reflect.Uint8 {
|
|
panic(fmt.Sprintf("array of non-uint8 in field %d: %T", i, field.Interface()))
|
|
}
|
|
for j, l := 0, t.Len(); j < l; j++ {
|
|
out = append(out, uint8(field.Index(j).Uint()))
|
|
}
|
|
case reflect.Uint32:
|
|
out = appendU32(out, uint32(field.Uint()))
|
|
case reflect.Uint64:
|
|
out = appendU64(out, uint64(field.Uint()))
|
|
case reflect.Uint8:
|
|
out = append(out, uint8(field.Uint()))
|
|
case reflect.String:
|
|
s := field.String()
|
|
out = appendInt(out, len(s))
|
|
out = append(out, s...)
|
|
case reflect.Slice:
|
|
switch t.Elem().Kind() {
|
|
case reflect.Uint8:
|
|
if v.Type().Field(i).Tag.Get("ssh") != "rest" {
|
|
out = appendInt(out, field.Len())
|
|
}
|
|
out = append(out, field.Bytes()...)
|
|
case reflect.String:
|
|
offset := len(out)
|
|
out = appendU32(out, 0)
|
|
if n := field.Len(); n > 0 {
|
|
for j := 0; j < n; j++ {
|
|
f := field.Index(j)
|
|
if j != 0 {
|
|
out = append(out, ',')
|
|
}
|
|
out = append(out, f.String()...)
|
|
}
|
|
// overwrite length value
|
|
binary.BigEndian.PutUint32(out[offset:], uint32(len(out)-offset-4))
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("slice of unknown type in field %d: %T", i, field.Interface()))
|
|
}
|
|
case reflect.Ptr:
|
|
if t == bigIntType {
|
|
var n *big.Int
|
|
nValue := reflect.ValueOf(&n)
|
|
nValue.Elem().Set(field)
|
|
needed := intLength(n)
|
|
oldLength := len(out)
|
|
|
|
if cap(out)-len(out) < needed {
|
|
newOut := make([]byte, len(out), 2*(len(out)+needed))
|
|
copy(newOut, out)
|
|
out = newOut
|
|
}
|
|
out = out[:oldLength+needed]
|
|
marshalInt(out[oldLength:], n)
|
|
} else {
|
|
panic(fmt.Sprintf("pointer to unknown type in field %d: %T", i, field.Interface()))
|
|
}
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
var bigOne = big.NewInt(1)
|
|
|
|
func parseString(in []byte) (out, rest []byte, ok bool) {
|
|
if len(in) < 4 {
|
|
return
|
|
}
|
|
length := binary.BigEndian.Uint32(in)
|
|
in = in[4:]
|
|
if uint32(len(in)) < length {
|
|
return
|
|
}
|
|
out = in[:length]
|
|
rest = in[length:]
|
|
ok = true
|
|
return
|
|
}
|
|
|
|
var (
|
|
comma = []byte{','}
|
|
emptyNameList = []string{}
|
|
)
|
|
|
|
func parseNameList(in []byte) (out []string, rest []byte, ok bool) {
|
|
contents, rest, ok := parseString(in)
|
|
if !ok {
|
|
return
|
|
}
|
|
if len(contents) == 0 {
|
|
out = emptyNameList
|
|
return
|
|
}
|
|
parts := bytes.Split(contents, comma)
|
|
out = make([]string, len(parts))
|
|
for i, part := range parts {
|
|
out[i] = string(part)
|
|
}
|
|
return
|
|
}
|
|
|
|
func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) {
|
|
contents, rest, ok := parseString(in)
|
|
if !ok {
|
|
return
|
|
}
|
|
out = new(big.Int)
|
|
|
|
if len(contents) > 0 && contents[0]&0x80 == 0x80 {
|
|
// This is a negative number
|
|
notBytes := make([]byte, len(contents))
|
|
for i := range notBytes {
|
|
notBytes[i] = ^contents[i]
|
|
}
|
|
out.SetBytes(notBytes)
|
|
out.Add(out, bigOne)
|
|
out.Neg(out)
|
|
} else {
|
|
// Positive number
|
|
out.SetBytes(contents)
|
|
}
|
|
ok = true
|
|
return
|
|
}
|
|
|
|
func parseUint32(in []byte) (uint32, []byte, bool) {
|
|
if len(in) < 4 {
|
|
return 0, nil, false
|
|
}
|
|
return binary.BigEndian.Uint32(in), in[4:], true
|
|
}
|
|
|
|
func parseUint64(in []byte) (uint64, []byte, bool) {
|
|
if len(in) < 8 {
|
|
return 0, nil, false
|
|
}
|
|
return binary.BigEndian.Uint64(in), in[8:], true
|
|
}
|
|
|
|
func intLength(n *big.Int) int {
|
|
length := 4 /* length bytes */
|
|
if n.Sign() < 0 {
|
|
nMinus1 := new(big.Int).Neg(n)
|
|
nMinus1.Sub(nMinus1, bigOne)
|
|
bitLen := nMinus1.BitLen()
|
|
if bitLen%8 == 0 {
|
|
// The number will need 0xff padding
|
|
length++
|
|
}
|
|
length += (bitLen + 7) / 8
|
|
} else if n.Sign() == 0 {
|
|
// A zero is the zero length string
|
|
} else {
|
|
bitLen := n.BitLen()
|
|
if bitLen%8 == 0 {
|
|
// The number will need 0x00 padding
|
|
length++
|
|
}
|
|
length += (bitLen + 7) / 8
|
|
}
|
|
|
|
return length
|
|
}
|
|
|
|
func marshalUint32(to []byte, n uint32) []byte {
|
|
binary.BigEndian.PutUint32(to, n)
|
|
return to[4:]
|
|
}
|
|
|
|
func marshalUint64(to []byte, n uint64) []byte {
|
|
binary.BigEndian.PutUint64(to, n)
|
|
return to[8:]
|
|
}
|
|
|
|
func marshalInt(to []byte, n *big.Int) []byte {
|
|
lengthBytes := to
|
|
to = to[4:]
|
|
length := 0
|
|
|
|
if n.Sign() < 0 {
|
|
// A negative number has to be converted to two's-complement
|
|
// form. So we'll subtract 1 and invert. If the
|
|
// most-significant-bit isn't set then we'll need to pad the
|
|
// beginning with 0xff in order to keep the number negative.
|
|
nMinus1 := new(big.Int).Neg(n)
|
|
nMinus1.Sub(nMinus1, bigOne)
|
|
bytes := nMinus1.Bytes()
|
|
for i := range bytes {
|
|
bytes[i] ^= 0xff
|
|
}
|
|
if len(bytes) == 0 || bytes[0]&0x80 == 0 {
|
|
to[0] = 0xff
|
|
to = to[1:]
|
|
length++
|
|
}
|
|
nBytes := copy(to, bytes)
|
|
to = to[nBytes:]
|
|
length += nBytes
|
|
} else if n.Sign() == 0 {
|
|
// A zero is the zero length string
|
|
} else {
|
|
bytes := n.Bytes()
|
|
if len(bytes) > 0 && bytes[0]&0x80 != 0 {
|
|
// We'll have to pad this with a 0x00 in order to
|
|
// stop it looking like a negative number.
|
|
to[0] = 0
|
|
to = to[1:]
|
|
length++
|
|
}
|
|
nBytes := copy(to, bytes)
|
|
to = to[nBytes:]
|
|
length += nBytes
|
|
}
|
|
|
|
lengthBytes[0] = byte(length >> 24)
|
|
lengthBytes[1] = byte(length >> 16)
|
|
lengthBytes[2] = byte(length >> 8)
|
|
lengthBytes[3] = byte(length)
|
|
return to
|
|
}
|
|
|
|
func writeInt(w io.Writer, n *big.Int) {
|
|
length := intLength(n)
|
|
buf := make([]byte, length)
|
|
marshalInt(buf, n)
|
|
w.Write(buf)
|
|
}
|
|
|
|
func writeString(w io.Writer, s []byte) {
|
|
var lengthBytes [4]byte
|
|
lengthBytes[0] = byte(len(s) >> 24)
|
|
lengthBytes[1] = byte(len(s) >> 16)
|
|
lengthBytes[2] = byte(len(s) >> 8)
|
|
lengthBytes[3] = byte(len(s))
|
|
w.Write(lengthBytes[:])
|
|
w.Write(s)
|
|
}
|
|
|
|
func stringLength(n int) int {
|
|
return 4 + n
|
|
}
|
|
|
|
func marshalString(to []byte, s []byte) []byte {
|
|
to[0] = byte(len(s) >> 24)
|
|
to[1] = byte(len(s) >> 16)
|
|
to[2] = byte(len(s) >> 8)
|
|
to[3] = byte(len(s))
|
|
to = to[4:]
|
|
copy(to, s)
|
|
return to[len(s):]
|
|
}
|
|
|
|
var bigIntType = reflect.TypeOf((*big.Int)(nil))
|
|
|
|
// Decode a packet into its corresponding message.
|
|
func decode(packet []byte) (interface{}, error) {
|
|
var msg interface{}
|
|
switch packet[0] {
|
|
case msgDisconnect:
|
|
msg = new(disconnectMsg)
|
|
case msgServiceRequest:
|
|
msg = new(serviceRequestMsg)
|
|
case msgServiceAccept:
|
|
msg = new(serviceAcceptMsg)
|
|
case msgKexInit:
|
|
msg = new(kexInitMsg)
|
|
case msgKexDHInit:
|
|
msg = new(kexDHInitMsg)
|
|
case msgKexDHReply:
|
|
msg = new(kexDHReplyMsg)
|
|
case msgUserAuthRequest:
|
|
msg = new(userAuthRequestMsg)
|
|
case msgUserAuthSuccess:
|
|
return new(userAuthSuccessMsg), nil
|
|
case msgUserAuthFailure:
|
|
msg = new(userAuthFailureMsg)
|
|
case msgUserAuthPubKeyOk:
|
|
msg = new(userAuthPubKeyOkMsg)
|
|
case msgGlobalRequest:
|
|
msg = new(globalRequestMsg)
|
|
case msgRequestSuccess:
|
|
msg = new(globalRequestSuccessMsg)
|
|
case msgRequestFailure:
|
|
msg = new(globalRequestFailureMsg)
|
|
case msgChannelOpen:
|
|
msg = new(channelOpenMsg)
|
|
case msgChannelData:
|
|
msg = new(channelDataMsg)
|
|
case msgChannelOpenConfirm:
|
|
msg = new(channelOpenConfirmMsg)
|
|
case msgChannelOpenFailure:
|
|
msg = new(channelOpenFailureMsg)
|
|
case msgChannelWindowAdjust:
|
|
msg = new(windowAdjustMsg)
|
|
case msgChannelEOF:
|
|
msg = new(channelEOFMsg)
|
|
case msgChannelClose:
|
|
msg = new(channelCloseMsg)
|
|
case msgChannelRequest:
|
|
msg = new(channelRequestMsg)
|
|
case msgChannelSuccess:
|
|
msg = new(channelRequestSuccessMsg)
|
|
case msgChannelFailure:
|
|
msg = new(channelRequestFailureMsg)
|
|
case msgUserAuthGSSAPIToken:
|
|
msg = new(userAuthGSSAPIToken)
|
|
case msgUserAuthGSSAPIMIC:
|
|
msg = new(userAuthGSSAPIMIC)
|
|
case msgUserAuthGSSAPIErrTok:
|
|
msg = new(userAuthGSSAPIErrTok)
|
|
case msgUserAuthGSSAPIError:
|
|
msg = new(userAuthGSSAPIError)
|
|
default:
|
|
return nil, unexpectedMessageError(0, packet[0])
|
|
}
|
|
if err := Unmarshal(packet, msg); err != nil {
|
|
return nil, err
|
|
}
|
|
return msg, nil
|
|
}
|
|
|
|
var packetTypeNames = map[byte]string{
|
|
msgDisconnect: "disconnectMsg",
|
|
msgServiceRequest: "serviceRequestMsg",
|
|
msgServiceAccept: "serviceAcceptMsg",
|
|
msgKexInit: "kexInitMsg",
|
|
msgKexDHInit: "kexDHInitMsg",
|
|
msgKexDHReply: "kexDHReplyMsg",
|
|
msgUserAuthRequest: "userAuthRequestMsg",
|
|
msgUserAuthSuccess: "userAuthSuccessMsg",
|
|
msgUserAuthFailure: "userAuthFailureMsg",
|
|
msgUserAuthPubKeyOk: "userAuthPubKeyOkMsg",
|
|
msgGlobalRequest: "globalRequestMsg",
|
|
msgRequestSuccess: "globalRequestSuccessMsg",
|
|
msgRequestFailure: "globalRequestFailureMsg",
|
|
msgChannelOpen: "channelOpenMsg",
|
|
msgChannelData: "channelDataMsg",
|
|
msgChannelOpenConfirm: "channelOpenConfirmMsg",
|
|
msgChannelOpenFailure: "channelOpenFailureMsg",
|
|
msgChannelWindowAdjust: "windowAdjustMsg",
|
|
msgChannelEOF: "channelEOFMsg",
|
|
msgChannelClose: "channelCloseMsg",
|
|
msgChannelRequest: "channelRequestMsg",
|
|
msgChannelSuccess: "channelRequestSuccessMsg",
|
|
msgChannelFailure: "channelRequestFailureMsg",
|
|
}
|