payload.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. package handshake
  2. import (
  3. "errors"
  4. "math"
  5. "google.golang.org/protobuf/encoding/protowire"
  6. )
  7. var (
  8. errInvalidHandshakeMessage = errors.New("invalid handshake message")
  9. errInvalidHandshakeDetails = errors.New("invalid handshake details")
  10. )
  11. // Payload represents the decoded fields of a handshake message.
  12. // Wire format is protobuf-compatible with NebulaHandshake{Details: NebulaHandshakeDetails{...}}.
  13. type Payload struct {
  14. Cert []byte
  15. InitiatorIndex uint32
  16. ResponderIndex uint32
  17. Time uint64
  18. CertVersion uint32
  19. // Multiport lane negotiation; nil when the sender has multiport disabled
  20. // (which keeps the encoded payload byte-identical to a vanilla one).
  21. InitiatorLanes *LaneDetails
  22. ResponderLanes *LaneDetails
  23. }
  24. // LaneDetails advertises multiport lane capability. LaneIndex is zero on base
  25. // handshakes and the initiator's lane number (>= 1) on lane handshakes.
  26. type LaneDetails struct {
  27. PortCount uint32
  28. BasePort uint32
  29. LaneIndex uint32
  30. }
  31. // Proto field numbers for NebulaHandshakeDetails
  32. const (
  33. fieldCert = 1 // bytes
  34. fieldInitiatorIndex = 2 // uint32
  35. fieldResponderIndex = 3 // uint32
  36. fieldTime = 5 // uint64
  37. fieldInitiatorLanes = 6 // LaneDetails
  38. fieldResponderLanes = 7 // LaneDetails
  39. fieldCertVersion = 8 // uint32
  40. )
  41. // Proto field numbers for LaneDetails
  42. const (
  43. fieldLanePortCount = 1 // uint32
  44. fieldLaneBasePort = 2 // uint32
  45. fieldLaneLaneIndex = 3 // uint32
  46. )
  47. // MarshalPayload encodes a handshake payload in protobuf wire format compatible
  48. // with NebulaHandshake{Details: NebulaHandshakeDetails{...}}.
  49. // Returns out (which may be nil), with the marshalled Payload appended to it.
  50. func MarshalPayload(out []byte, p Payload) []byte {
  51. var details []byte
  52. if len(p.Cert) > 0 {
  53. details = protowire.AppendTag(details, fieldCert, protowire.BytesType)
  54. details = protowire.AppendBytes(details, p.Cert)
  55. }
  56. if p.InitiatorIndex != 0 {
  57. details = protowire.AppendTag(details, fieldInitiatorIndex, protowire.VarintType)
  58. details = protowire.AppendVarint(details, uint64(p.InitiatorIndex))
  59. }
  60. if p.ResponderIndex != 0 {
  61. details = protowire.AppendTag(details, fieldResponderIndex, protowire.VarintType)
  62. details = protowire.AppendVarint(details, uint64(p.ResponderIndex))
  63. }
  64. if p.Time != 0 {
  65. details = protowire.AppendTag(details, fieldTime, protowire.VarintType)
  66. details = protowire.AppendVarint(details, p.Time)
  67. }
  68. if p.InitiatorLanes != nil {
  69. details = protowire.AppendTag(details, fieldInitiatorLanes, protowire.BytesType)
  70. details = protowire.AppendBytes(details, p.InitiatorLanes.marshal(nil))
  71. }
  72. if p.ResponderLanes != nil {
  73. details = protowire.AppendTag(details, fieldResponderLanes, protowire.BytesType)
  74. details = protowire.AppendBytes(details, p.ResponderLanes.marshal(nil))
  75. }
  76. if p.CertVersion != 0 {
  77. details = protowire.AppendTag(details, fieldCertVersion, protowire.VarintType)
  78. details = protowire.AppendVarint(details, uint64(p.CertVersion))
  79. }
  80. out = protowire.AppendTag(out, 1, protowire.BytesType)
  81. out = protowire.AppendBytes(out, details)
  82. return out
  83. }
  84. // marshal appends the LaneDetails submessage fields to out. All fields are
  85. // emitted unconditionally: a LaneDetails is only present at all when multiport
  86. // is negotiating, and explicit zeros keep the parser's presence semantics
  87. // trivial.
  88. func (d *LaneDetails) marshal(out []byte) []byte {
  89. out = protowire.AppendTag(out, fieldLanePortCount, protowire.VarintType)
  90. out = protowire.AppendVarint(out, uint64(d.PortCount))
  91. out = protowire.AppendTag(out, fieldLaneBasePort, protowire.VarintType)
  92. out = protowire.AppendVarint(out, uint64(d.BasePort))
  93. out = protowire.AppendTag(out, fieldLaneLaneIndex, protowire.VarintType)
  94. out = protowire.AppendVarint(out, uint64(d.LaneIndex))
  95. return out
  96. }
  97. // UnmarshalPayload decodes a protobuf-encoded NebulaHandshake message.
  98. func UnmarshalPayload(b []byte) (Payload, error) {
  99. var p Payload
  100. for len(b) > 0 {
  101. num, typ, n := protowire.ConsumeTag(b)
  102. if n < 0 {
  103. return p, errInvalidHandshakeMessage
  104. }
  105. b = b[n:]
  106. switch {
  107. case num == 1 && typ == protowire.BytesType:
  108. details, n := protowire.ConsumeBytes(b)
  109. if n < 0 {
  110. return p, errInvalidHandshakeMessage
  111. }
  112. b = b[n:]
  113. if err := unmarshalPayloadDetails(&p, details); err != nil {
  114. return p, err
  115. }
  116. default:
  117. n := protowire.ConsumeFieldValue(num, typ, b)
  118. if n < 0 {
  119. return p, errInvalidHandshakeMessage
  120. }
  121. b = b[n:]
  122. }
  123. }
  124. return p, nil
  125. }
  126. func unmarshalPayloadDetails(p *Payload, b []byte) error {
  127. for len(b) > 0 {
  128. num, typ, n := protowire.ConsumeTag(b)
  129. if n < 0 {
  130. return errInvalidHandshakeDetails
  131. }
  132. b = b[n:]
  133. // For known field numbers, reject any non-matching wire type as a
  134. // hard error rather than silently skipping. The caller will catch
  135. // missing-field cases downstream, but a wire-type mismatch on a tag
  136. // we know is a peer protocol violation worth flagging here.
  137. // Repeated occurrences of a singular field follow proto3 last-wins.
  138. switch num {
  139. case fieldCert:
  140. if typ != protowire.BytesType {
  141. return errInvalidHandshakeDetails
  142. }
  143. v, n := protowire.ConsumeBytes(b)
  144. if n < 0 {
  145. return errInvalidHandshakeDetails
  146. }
  147. p.Cert = append([]byte(nil), v...)
  148. b = b[n:]
  149. case fieldInitiatorIndex:
  150. if typ != protowire.VarintType {
  151. return errInvalidHandshakeDetails
  152. }
  153. v, n := protowire.ConsumeVarint(b)
  154. if n < 0 || v > math.MaxUint32 {
  155. return errInvalidHandshakeDetails
  156. }
  157. p.InitiatorIndex = uint32(v)
  158. b = b[n:]
  159. case fieldResponderIndex:
  160. if typ != protowire.VarintType {
  161. return errInvalidHandshakeDetails
  162. }
  163. v, n := protowire.ConsumeVarint(b)
  164. if n < 0 || v > math.MaxUint32 {
  165. return errInvalidHandshakeDetails
  166. }
  167. p.ResponderIndex = uint32(v)
  168. b = b[n:]
  169. case fieldTime:
  170. if typ != protowire.VarintType {
  171. return errInvalidHandshakeDetails
  172. }
  173. v, n := protowire.ConsumeVarint(b)
  174. if n < 0 {
  175. return errInvalidHandshakeDetails
  176. }
  177. p.Time = v
  178. b = b[n:]
  179. case fieldCertVersion:
  180. if typ != protowire.VarintType {
  181. return errInvalidHandshakeDetails
  182. }
  183. v, n := protowire.ConsumeVarint(b)
  184. if n < 0 || v > math.MaxUint32 {
  185. return errInvalidHandshakeDetails
  186. }
  187. p.CertVersion = uint32(v)
  188. b = b[n:]
  189. case fieldInitiatorLanes:
  190. if typ != protowire.BytesType {
  191. return errInvalidHandshakeDetails
  192. }
  193. v, n := protowire.ConsumeBytes(b)
  194. if n < 0 {
  195. return errInvalidHandshakeDetails
  196. }
  197. p.InitiatorLanes = new(LaneDetails)
  198. if err := unmarshalLaneDetails(p.InitiatorLanes, v); err != nil {
  199. return err
  200. }
  201. b = b[n:]
  202. case fieldResponderLanes:
  203. if typ != protowire.BytesType {
  204. return errInvalidHandshakeDetails
  205. }
  206. v, n := protowire.ConsumeBytes(b)
  207. if n < 0 {
  208. return errInvalidHandshakeDetails
  209. }
  210. p.ResponderLanes = new(LaneDetails)
  211. if err := unmarshalLaneDetails(p.ResponderLanes, v); err != nil {
  212. return err
  213. }
  214. b = b[n:]
  215. default:
  216. n := protowire.ConsumeFieldValue(num, typ, b)
  217. if n < 0 {
  218. return errInvalidHandshakeDetails
  219. }
  220. b = b[n:]
  221. }
  222. }
  223. return nil
  224. }
  225. func unmarshalLaneDetails(d *LaneDetails, b []byte) error {
  226. for len(b) > 0 {
  227. num, typ, n := protowire.ConsumeTag(b)
  228. if n < 0 {
  229. return errInvalidHandshakeDetails
  230. }
  231. b = b[n:]
  232. // Same contract as the details parser: known fields hard-fail on a
  233. // wire-type mismatch, unknown fields are skipped, repeated singular
  234. // fields follow proto3 last-wins.
  235. switch num {
  236. case fieldLanePortCount, fieldLaneBasePort, fieldLaneLaneIndex:
  237. if typ != protowire.VarintType {
  238. return errInvalidHandshakeDetails
  239. }
  240. v, n := protowire.ConsumeVarint(b)
  241. if n < 0 || v > math.MaxUint32 {
  242. return errInvalidHandshakeDetails
  243. }
  244. switch num {
  245. case fieldLanePortCount:
  246. d.PortCount = uint32(v)
  247. case fieldLaneBasePort:
  248. d.BasePort = uint32(v)
  249. case fieldLaneLaneIndex:
  250. d.LaneIndex = uint32(v)
  251. }
  252. b = b[n:]
  253. default:
  254. n := protowire.ConsumeFieldValue(num, typ, b)
  255. if n < 0 {
  256. return errInvalidHandshakeDetails
  257. }
  258. b = b[n:]
  259. }
  260. }
  261. return nil
  262. }