payload.go 8.2 KB

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