// 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-Hellman
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"`
	PreferredBits 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 8308, section 2.3
const msgExtInfo = 7

type extInfoMsg struct {
	NumExtensions uint32 `sshtype:"7"`
	Payload       []byte `ssh:"rest"`
}

// 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 {
	Name        string `sshtype:"60"`
	Instruction string
	Language    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
}

// Transport layer OpenSSH extension. See [PROTOCOL], section 1.9
const msgPing = 192

type pingMsg struct {
	Data string `sshtype:"192"`
}

// Transport layer OpenSSH extension. See [PROTOCOL], section 1.9
const msgPong = 193

type pongMsg struct {
	Data string `sshtype:"193"`
}

// 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.TypeFor[*big.Int]()

// 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 msgExtInfo:
		msg = new(extInfoMsg)
	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 msgUserAuthBanner:
		msg = new(userAuthBannerMsg)
	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",
	msgExtInfo:             "extInfoMsg",
	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",
}
