mirror of
https://github.com/Luzifer/cloudkeys-go.git
synced 2024-11-10 07:00:08 +00:00
Knut Ahlers
a1df72edc5
commitf0db1ff1f8
Author: Knut Ahlers <knut@ahlers.me> Date: Sun Dec 24 12:19:56 2017 +0100 Mark option as deprecated Signed-off-by: Knut Ahlers <knut@ahlers.me> commit9891df2a16
Author: Knut Ahlers <knut@ahlers.me> Date: Sun Dec 24 12:11:56 2017 +0100 Fix: Typo Signed-off-by: Knut Ahlers <knut@ahlers.me> commit836006de64
Author: Knut Ahlers <knut@ahlers.me> Date: Sun Dec 24 12:04:20 2017 +0100 Add new dependencies Signed-off-by: Knut Ahlers <knut@ahlers.me> commitd64fee60c8
Author: Knut Ahlers <knut@ahlers.me> Date: Sun Dec 24 11:55:52 2017 +0100 Replace insecure password hashing Prior this commit passwords were hashed with a static salt and using the SHA1 hashing function. This could lead to passwords being attackable in case someone gets access to the raw data stored inside the database. This commit introduces password hashing using bcrypt hashing function which addresses this issue. Old passwords are not automatically re-hashed as they are unknown. Replacing the old password scheme is not that easy and needs #10 to be solved. Therefore the old hashing scheme is kept for compatibility reason. Signed-off-by: Knut Ahlers <knut@ahlers.me> Signed-off-by: Knut Ahlers <knut@ahlers.me> closes #14 closes #15
766 lines
17 KiB
Go
766 lines
17 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 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
|
|
}
|
|
|
|
// 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)
|
|
default:
|
|
return nil, unexpectedMessageError(0, packet[0])
|
|
}
|
|
if err := Unmarshal(packet, msg); err != nil {
|
|
return nil, err
|
|
}
|
|
return msg, nil
|
|
}
|