| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- package handshake
- import (
- "errors"
- "math"
- "google.golang.org/protobuf/encoding/protowire"
- )
- var (
- errInvalidHandshakeMessage = errors.New("invalid handshake message")
- errInvalidHandshakeDetails = errors.New("invalid handshake details")
- )
- // Payload represents the decoded fields of a handshake message.
- // Wire format is protobuf-compatible with NebulaHandshake{Details: NebulaHandshakeDetails{...}}.
- type Payload struct {
- Cert []byte
- InitiatorIndex uint32
- ResponderIndex uint32
- Time uint64
- CertVersion uint32
- // Multiport lane negotiation; nil when the sender has multiport disabled
- // (which keeps the encoded payload byte-identical to a vanilla one).
- InitiatorLanes *LaneDetails
- ResponderLanes *LaneDetails
- }
- // LaneDetails advertises multiport lane capability. LaneIndex is zero on base
- // handshakes and the initiator's lane number (>= 1) on lane handshakes.
- type LaneDetails struct {
- PortCount uint32
- BasePort uint32
- LaneIndex uint32
- }
- // Proto field numbers for NebulaHandshakeDetails
- const (
- fieldCert = 1 // bytes
- fieldInitiatorIndex = 2 // uint32
- fieldResponderIndex = 3 // uint32
- fieldTime = 5 // uint64
- fieldInitiatorLanes = 6 // LaneDetails
- fieldResponderLanes = 7 // LaneDetails
- fieldCertVersion = 8 // uint32
- )
- // Proto field numbers for LaneDetails
- const (
- fieldLanePortCount = 1 // uint32
- fieldLaneBasePort = 2 // uint32
- fieldLaneLaneIndex = 3 // uint32
- )
- // MarshalPayload encodes a handshake payload in protobuf wire format compatible
- // with NebulaHandshake{Details: NebulaHandshakeDetails{...}}.
- // Returns out (which may be nil), with the marshalled Payload appended to it.
- func MarshalPayload(out []byte, p Payload) []byte {
- var details []byte
- if len(p.Cert) > 0 {
- details = protowire.AppendTag(details, fieldCert, protowire.BytesType)
- details = protowire.AppendBytes(details, p.Cert)
- }
- if p.InitiatorIndex != 0 {
- details = protowire.AppendTag(details, fieldInitiatorIndex, protowire.VarintType)
- details = protowire.AppendVarint(details, uint64(p.InitiatorIndex))
- }
- if p.ResponderIndex != 0 {
- details = protowire.AppendTag(details, fieldResponderIndex, protowire.VarintType)
- details = protowire.AppendVarint(details, uint64(p.ResponderIndex))
- }
- if p.Time != 0 {
- details = protowire.AppendTag(details, fieldTime, protowire.VarintType)
- details = protowire.AppendVarint(details, p.Time)
- }
- if p.InitiatorLanes != nil {
- details = protowire.AppendTag(details, fieldInitiatorLanes, protowire.BytesType)
- details = protowire.AppendBytes(details, p.InitiatorLanes.marshal(nil))
- }
- if p.ResponderLanes != nil {
- details = protowire.AppendTag(details, fieldResponderLanes, protowire.BytesType)
- details = protowire.AppendBytes(details, p.ResponderLanes.marshal(nil))
- }
- if p.CertVersion != 0 {
- details = protowire.AppendTag(details, fieldCertVersion, protowire.VarintType)
- details = protowire.AppendVarint(details, uint64(p.CertVersion))
- }
- out = protowire.AppendTag(out, 1, protowire.BytesType)
- out = protowire.AppendBytes(out, details)
- return out
- }
- // marshal appends the LaneDetails submessage fields to out. All fields are
- // emitted unconditionally: a LaneDetails is only present at all when multiport
- // is negotiating, and explicit zeros keep the parser's presence semantics
- // trivial.
- func (d *LaneDetails) marshal(out []byte) []byte {
- out = protowire.AppendTag(out, fieldLanePortCount, protowire.VarintType)
- out = protowire.AppendVarint(out, uint64(d.PortCount))
- out = protowire.AppendTag(out, fieldLaneBasePort, protowire.VarintType)
- out = protowire.AppendVarint(out, uint64(d.BasePort))
- out = protowire.AppendTag(out, fieldLaneLaneIndex, protowire.VarintType)
- out = protowire.AppendVarint(out, uint64(d.LaneIndex))
- return out
- }
- // UnmarshalPayload decodes a protobuf-encoded NebulaHandshake message.
- func UnmarshalPayload(b []byte) (Payload, error) {
- var p Payload
- for len(b) > 0 {
- num, typ, n := protowire.ConsumeTag(b)
- if n < 0 {
- return p, errInvalidHandshakeMessage
- }
- b = b[n:]
- switch {
- case num == 1 && typ == protowire.BytesType:
- details, n := protowire.ConsumeBytes(b)
- if n < 0 {
- return p, errInvalidHandshakeMessage
- }
- b = b[n:]
- if err := unmarshalPayloadDetails(&p, details); err != nil {
- return p, err
- }
- default:
- n := protowire.ConsumeFieldValue(num, typ, b)
- if n < 0 {
- return p, errInvalidHandshakeMessage
- }
- b = b[n:]
- }
- }
- return p, nil
- }
- func unmarshalPayloadDetails(p *Payload, b []byte) error {
- for len(b) > 0 {
- num, typ, n := protowire.ConsumeTag(b)
- if n < 0 {
- return errInvalidHandshakeDetails
- }
- b = b[n:]
- // For known field numbers, reject any non-matching wire type as a
- // hard error rather than silently skipping. The caller will catch
- // missing-field cases downstream, but a wire-type mismatch on a tag
- // we know is a peer protocol violation worth flagging here.
- // Repeated occurrences of a singular field follow proto3 last-wins.
- switch num {
- case fieldCert:
- if typ != protowire.BytesType {
- return errInvalidHandshakeDetails
- }
- v, n := protowire.ConsumeBytes(b)
- if n < 0 {
- return errInvalidHandshakeDetails
- }
- p.Cert = append([]byte(nil), v...)
- b = b[n:]
- case fieldInitiatorIndex:
- if typ != protowire.VarintType {
- return errInvalidHandshakeDetails
- }
- v, n := protowire.ConsumeVarint(b)
- if n < 0 || v > math.MaxUint32 {
- return errInvalidHandshakeDetails
- }
- p.InitiatorIndex = uint32(v)
- b = b[n:]
- case fieldResponderIndex:
- if typ != protowire.VarintType {
- return errInvalidHandshakeDetails
- }
- v, n := protowire.ConsumeVarint(b)
- if n < 0 || v > math.MaxUint32 {
- return errInvalidHandshakeDetails
- }
- p.ResponderIndex = uint32(v)
- b = b[n:]
- case fieldTime:
- if typ != protowire.VarintType {
- return errInvalidHandshakeDetails
- }
- v, n := protowire.ConsumeVarint(b)
- if n < 0 {
- return errInvalidHandshakeDetails
- }
- p.Time = v
- b = b[n:]
- case fieldCertVersion:
- if typ != protowire.VarintType {
- return errInvalidHandshakeDetails
- }
- v, n := protowire.ConsumeVarint(b)
- if n < 0 || v > math.MaxUint32 {
- return errInvalidHandshakeDetails
- }
- p.CertVersion = uint32(v)
- b = b[n:]
- case fieldInitiatorLanes:
- if typ != protowire.BytesType {
- return errInvalidHandshakeDetails
- }
- v, n := protowire.ConsumeBytes(b)
- if n < 0 {
- return errInvalidHandshakeDetails
- }
- p.InitiatorLanes = new(LaneDetails)
- if err := unmarshalLaneDetails(p.InitiatorLanes, v); err != nil {
- return err
- }
- b = b[n:]
- case fieldResponderLanes:
- if typ != protowire.BytesType {
- return errInvalidHandshakeDetails
- }
- v, n := protowire.ConsumeBytes(b)
- if n < 0 {
- return errInvalidHandshakeDetails
- }
- p.ResponderLanes = new(LaneDetails)
- if err := unmarshalLaneDetails(p.ResponderLanes, v); err != nil {
- return err
- }
- b = b[n:]
- default:
- n := protowire.ConsumeFieldValue(num, typ, b)
- if n < 0 {
- return errInvalidHandshakeDetails
- }
- b = b[n:]
- }
- }
- return nil
- }
- func unmarshalLaneDetails(d *LaneDetails, b []byte) error {
- for len(b) > 0 {
- num, typ, n := protowire.ConsumeTag(b)
- if n < 0 {
- return errInvalidHandshakeDetails
- }
- b = b[n:]
- // Same contract as the details parser: known fields hard-fail on a
- // wire-type mismatch, unknown fields are skipped, repeated singular
- // fields follow proto3 last-wins.
- switch num {
- case fieldLanePortCount, fieldLaneBasePort, fieldLaneLaneIndex:
- if typ != protowire.VarintType {
- return errInvalidHandshakeDetails
- }
- v, n := protowire.ConsumeVarint(b)
- if n < 0 || v > math.MaxUint32 {
- return errInvalidHandshakeDetails
- }
- switch num {
- case fieldLanePortCount:
- d.PortCount = uint32(v)
- case fieldLaneBasePort:
- d.BasePort = uint32(v)
- case fieldLaneLaneIndex:
- d.LaneIndex = uint32(v)
- }
- b = b[n:]
- default:
- n := protowire.ConsumeFieldValue(num, typ, b)
- if n < 0 {
- return errInvalidHandshakeDetails
- }
- b = b[n:]
- }
- }
- return nil
- }
|