mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-14 14:49:32 +01:00
866 lines
20 KiB
Go
866 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",
|
|
}
|