summaryrefslogtreecommitdiffstats
path: root/libgo/go/exp/ssh/messages.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/exp/ssh/messages.go')
-rw-r--r--libgo/go/exp/ssh/messages.go636
1 files changed, 636 insertions, 0 deletions
diff --git a/libgo/go/exp/ssh/messages.go b/libgo/go/exp/ssh/messages.go
new file mode 100644
index 00000000000..1d0bc577426
--- /dev/null
+++ b/libgo/go/exp/ssh/messages.go
@@ -0,0 +1,636 @@
+// 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 (
+ "big"
+ "bytes"
+ "io"
+ "os"
+ "reflect"
+)
+
+// These are SSH message type numbers. They are scattered around several
+// documents but many were taken from
+// http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
+const (
+ msgDisconnect = 1
+ msgIgnore = 2
+ msgUnimplemented = 3
+ msgDebug = 4
+ msgServiceRequest = 5
+ msgServiceAccept = 6
+
+ msgKexInit = 20
+ msgNewKeys = 21
+
+ msgKexDHInit = 30
+ msgKexDHReply = 31
+
+ msgUserAuthRequest = 50
+ msgUserAuthFailure = 51
+ msgUserAuthSuccess = 52
+ msgUserAuthBanner = 53
+ msgUserAuthPubKeyOk = 60
+
+ msgGlobalRequest = 80
+ msgRequestSuccess = 81
+ msgRequestFailure = 82
+
+ msgChannelOpen = 90
+ msgChannelOpenConfirm = 91
+ msgChannelOpenFailure = 92
+ msgChannelWindowAdjust = 93
+ msgChannelData = 94
+ msgChannelExtendedData = 95
+ msgChannelEOF = 96
+ msgChannelClose = 97
+ msgChannelRequest = 98
+ msgChannelSuccess = 99
+ msgChannelFailure = 100
+)
+
+// 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.
+type disconnectMsg struct {
+ Reason uint32
+ Message string
+ Language string
+}
+
+// See RFC 4253, section 7.1.
+type kexInitMsg struct {
+ Cookie [16]byte
+ 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.
+type kexDHInitMsg struct {
+ X *big.Int
+}
+
+type kexDHReplyMsg struct {
+ HostKey []byte
+ Y *big.Int
+ Signature []byte
+}
+
+// See RFC 4253, section 10.
+type serviceRequestMsg struct {
+ Service string
+}
+
+// See RFC 4253, section 10.
+type serviceAcceptMsg struct {
+ Service string
+}
+
+// See RFC 4252, section 5.
+type userAuthRequestMsg struct {
+ User string
+ Service string
+ Method string
+ Payload []byte `ssh:"rest"`
+}
+
+// See RFC 4252, section 5.1
+type userAuthFailureMsg struct {
+ Methods []string
+ PartialSuccess bool
+}
+
+// See RFC 4254, section 5.1.
+type channelOpenMsg struct {
+ ChanType string
+ PeersId uint32
+ PeersWindow uint32
+ MaxPacketSize uint32
+ TypeSpecificData []byte `ssh:"rest"`
+}
+
+// See RFC 4254, section 5.1.
+type channelOpenConfirmMsg struct {
+ PeersId uint32
+ MyId uint32
+ MyWindow uint32
+ MaxPacketSize uint32
+ TypeSpecificData []byte `ssh:"rest"`
+}
+
+// See RFC 4254, section 5.1.
+type channelOpenFailureMsg struct {
+ PeersId uint32
+ Reason uint32
+ Message string
+ Language string
+}
+
+// See RFC 4254, section 5.2.
+type channelData struct {
+ PeersId uint32
+ Payload []byte `ssh:"rest"`
+}
+
+// See RFC 4254, section 5.2.
+type channelExtendedData struct {
+ PeersId uint32
+ Datatype uint32
+ Data string
+}
+
+type channelRequestMsg struct {
+ PeersId uint32
+ Request string
+ WantReply bool
+ RequestSpecificData []byte `ssh:"rest"`
+}
+
+// See RFC 4254, section 5.4.
+type channelRequestSuccessMsg struct {
+ PeersId uint32
+}
+
+// See RFC 4254, section 5.4.
+type channelRequestFailureMsg struct {
+ PeersId uint32
+}
+
+// See RFC 4254, section 5.3
+type channelCloseMsg struct {
+ PeersId uint32
+}
+
+// See RFC 4254, section 5.3
+type channelEOFMsg struct {
+ PeersId uint32
+}
+
+// See RFC 4254, section 4
+type globalRequestMsg struct {
+ Type string
+ WantReply bool
+}
+
+// See RFC 4254, section 5.2
+type windowAdjustMsg struct {
+ PeersId uint32
+ AdditionalBytes uint32
+}
+
+// See RFC 4252, section 7
+type userAuthPubKeyOkMsg struct {
+ Algo string
+ PubKey string
+}
+
+// unmarshal parses the SSH wire data in packet into out using reflection.
+// expectedType is the expected SSH message type. It either returns nil on
+// success, or a ParseError or UnexpectedMessageError on error.
+func unmarshal(out interface{}, packet []byte, expectedType uint8) os.Error {
+ if len(packet) == 0 {
+ return ParseError{expectedType}
+ }
+ if packet[0] != expectedType {
+ return UnexpectedMessageError{expectedType, packet[0]}
+ }
+ packet = packet[1:]
+
+ v := reflect.ValueOf(out).Elem()
+ structType := v.Type()
+ 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(packet) < 1 {
+ return ParseError{expectedType}
+ }
+ field.SetBool(packet[0] != 0)
+ packet = packet[1:]
+ case reflect.Array:
+ if t.Elem().Kind() != reflect.Uint8 {
+ panic("array of non-uint8")
+ }
+ if len(packet) < t.Len() {
+ return ParseError{expectedType}
+ }
+ for j := 0; j < t.Len(); j++ {
+ field.Index(j).Set(reflect.ValueOf(packet[j]))
+ }
+ packet = packet[t.Len():]
+ case reflect.Uint32:
+ var u32 uint32
+ if u32, packet, ok = parseUint32(packet); !ok {
+ return ParseError{expectedType}
+ }
+ field.SetUint(uint64(u32))
+ case reflect.String:
+ var s []byte
+ if s, packet, ok = parseString(packet); !ok {
+ return ParseError{expectedType}
+ }
+ 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(packet))
+ packet = nil
+ } else {
+ var s []byte
+ if s, packet, ok = parseString(packet); !ok {
+ return ParseError{expectedType}
+ }
+ field.Set(reflect.ValueOf(s))
+ }
+ case reflect.String:
+ var nl []string
+ if nl, packet, ok = parseNameList(packet); !ok {
+ return ParseError{expectedType}
+ }
+ field.Set(reflect.ValueOf(nl))
+ default:
+ panic("slice of unknown type")
+ }
+ case reflect.Ptr:
+ if t == bigIntType {
+ var n *big.Int
+ if n, packet, ok = parseInt(packet); !ok {
+ return ParseError{expectedType}
+ }
+ field.Set(reflect.ValueOf(n))
+ } else {
+ panic("pointer to unknown type")
+ }
+ default:
+ panic("unknown type")
+ }
+ }
+
+ if len(packet) != 0 {
+ return ParseError{expectedType}
+ }
+
+ return nil
+}
+
+// marshal serializes the message in msg, using the given message type.
+func marshal(msgType uint8, msg interface{}) []byte {
+ var out []byte
+ out = append(out, msgType)
+
+ v := reflect.ValueOf(msg)
+ structType := v.Type()
+ for i := 0; i < v.NumField(); i++ {
+ field := v.Field(i)
+ t := field.Type()
+ switch 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("array of non-uint8")
+ }
+ for j := 0; j < t.Len(); j++ {
+ out = append(out, byte(field.Index(j).Uint()))
+ }
+ case reflect.Uint32:
+ u32 := uint32(field.Uint())
+ out = append(out, byte(u32>>24))
+ out = append(out, byte(u32>>16))
+ out = append(out, byte(u32>>8))
+ out = append(out, byte(u32))
+ case reflect.String:
+ s := field.String()
+ out = append(out, byte(len(s)>>24))
+ out = append(out, byte(len(s)>>16))
+ out = append(out, byte(len(s)>>8))
+ out = append(out, byte(len(s)))
+ out = append(out, s...)
+ case reflect.Slice:
+ switch t.Elem().Kind() {
+ case reflect.Uint8:
+ length := field.Len()
+ if structType.Field(i).Tag.Get("ssh") != "rest" {
+ out = append(out, byte(length>>24))
+ out = append(out, byte(length>>16))
+ out = append(out, byte(length>>8))
+ out = append(out, byte(length))
+ }
+ for j := 0; j < length; j++ {
+ out = append(out, byte(field.Index(j).Uint()))
+ }
+ case reflect.String:
+ var length int
+ for j := 0; j < field.Len(); j++ {
+ if j != 0 {
+ length++ /* comma */
+ }
+ length += len(field.Index(j).String())
+ }
+
+ out = append(out, byte(length>>24))
+ out = append(out, byte(length>>16))
+ out = append(out, byte(length>>8))
+ out = append(out, byte(length))
+ for j := 0; j < field.Len(); j++ {
+ if j != 0 {
+ out = append(out, ',')
+ }
+ out = append(out, field.Index(j).String()...)
+ }
+ default:
+ panic("slice of unknown type")
+ }
+ 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("pointer to unknown type")
+ }
+ }
+ }
+
+ return out
+}
+
+var bigOne = big.NewInt(1)
+
+func parseString(in []byte) (out, rest []byte, ok bool) {
+ if len(in) < 4 {
+ return
+ }
+ length := uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3])
+ if uint32(len(in)) < 4+length {
+ return
+ }
+ out = in[4 : 4+length]
+ rest = in[4+length:]
+ ok = true
+ return
+}
+
+var comma = []byte{','}
+
+func parseNameList(in []byte) (out []string, rest []byte, ok bool) {
+ contents, rest, ok := parseString(in)
+ if !ok {
+ return
+ }
+ if len(contents) == 0 {
+ 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) (out uint32, rest []byte, ok bool) {
+ if len(in) < 4 {
+ return
+ }
+ out = uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3])
+ rest = in[4:]
+ ok = true
+ return
+}
+
+const maxPacketSize = 36000
+
+func nameListLength(namelist []string) int {
+ length := 4 /* uint32 length prefix */
+ for i, name := range namelist {
+ if i != 0 {
+ length++ /* comma */
+ }
+ length += len(name)
+ }
+ return length
+}
+
+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 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(s []byte) int {
+ return 4 + len(s)
+}
+
+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 it's corresponding message.
+func decode(packet []byte) interface{} {
+ 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 msgUserAuthFailure:
+ msg = new(userAuthFailureMsg)
+ case msgUserAuthPubKeyOk:
+ msg = new(userAuthPubKeyOkMsg)
+ case msgGlobalRequest:
+ msg = new(globalRequestMsg)
+ case msgRequestSuccess:
+ msg = new(channelRequestSuccessMsg)
+ case msgRequestFailure:
+ msg = new(channelRequestFailureMsg)
+ case msgChannelOpen:
+ msg = new(channelOpenMsg)
+ case msgChannelOpenConfirm:
+ msg = new(channelOpenConfirmMsg)
+ case msgChannelOpenFailure:
+ msg = new(channelOpenFailureMsg)
+ case msgChannelWindowAdjust:
+ msg = new(windowAdjustMsg)
+ case msgChannelData:
+ msg = new(channelData)
+ case msgChannelExtendedData:
+ msg = new(channelExtendedData)
+ 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 UnexpectedMessageError{0, packet[0]}
+ }
+ if err := unmarshal(msg, packet, packet[0]); err != nil {
+ return err
+ }
+ return msg
+}
OpenPOWER on IntegriCloud