librats 1.0.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/README.md +145 -331
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +288 -696
  4. package/lib/index.js +407 -44
  5. package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
  7. package/native-src/CMakeLists.txt +404 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +762 -0
  10. package/native-src/src/librats/bindings/rats.h +380 -0
  11. package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
  12. package/native-src/src/librats/bittorrent/bencode.h +176 -0
  13. package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
  14. package/native-src/src/librats/bittorrent/bitfield.h +76 -0
  15. package/native-src/src/librats/bittorrent/byte_io.h +58 -0
  16. package/native-src/src/librats/bittorrent/choker.cpp +25 -0
  17. package/native-src/src/librats/bittorrent/choker.h +46 -0
  18. package/native-src/src/librats/bittorrent/client.cpp +413 -0
  19. package/native-src/src/librats/bittorrent/client.h +227 -0
  20. package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
  21. package/native-src/src/librats/bittorrent/disk_io.h +150 -0
  22. package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
  23. package/native-src/src/librats/bittorrent/extensions.h +94 -0
  24. package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
  25. package/native-src/src/librats/bittorrent/file_storage.h +79 -0
  26. package/native-src/src/librats/bittorrent/log.h +42 -0
  27. package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
  28. package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
  29. package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
  30. package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
  31. package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
  32. package/native-src/src/librats/bittorrent/peer_list.h +75 -0
  33. package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
  34. package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
  35. package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
  36. package/native-src/src/librats/bittorrent/reactor.h +89 -0
  37. package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
  38. package/native-src/src/librats/bittorrent/resume_data.h +41 -0
  39. package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
  40. package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
  41. package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
  42. package/native-src/src/librats/bittorrent/torrent.h +260 -0
  43. package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
  44. package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
  45. package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
  46. package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
  47. package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
  48. package/native-src/src/librats/bittorrent/tracker.h +108 -0
  49. package/native-src/src/librats/bittorrent/types.cpp +206 -0
  50. package/native-src/src/librats/bittorrent/types.h +86 -0
  51. package/native-src/src/librats/core/address.cpp +35 -0
  52. package/native-src/src/librats/core/address.h +78 -0
  53. package/native-src/src/librats/core/bytes.h +69 -0
  54. package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
  55. package/native-src/src/librats/core/chained_send_buffer.h +183 -0
  56. package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
  57. package/native-src/src/librats/core/endpoint_parse.h +31 -0
  58. package/native-src/src/librats/core/event_bus.h +70 -0
  59. package/native-src/src/librats/core/host_endpoint.h +56 -0
  60. package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
  61. package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
  62. package/native-src/src/librats/core/ip_address.cpp +120 -0
  63. package/native-src/src/librats/core/ip_address.h +109 -0
  64. package/native-src/src/librats/core/mpsc_queue.h +47 -0
  65. package/native-src/src/librats/core/notifier.h +74 -0
  66. package/native-src/src/librats/core/receive_buffer.cpp +219 -0
  67. package/native-src/src/librats/core/receive_buffer.h +171 -0
  68. package/native-src/src/librats/core/service_registry.h +58 -0
  69. package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
  70. package/native-src/src/librats/core/socket.h +496 -0
  71. package/native-src/src/librats/core/timer_queue.h +105 -0
  72. package/native-src/src/librats/core/types.cpp +43 -0
  73. package/native-src/src/librats/core/types.h +103 -0
  74. package/native-src/src/librats/core/wakeup_pipe.h +83 -0
  75. package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
  76. package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
  77. package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
  78. package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
  79. package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
  80. package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
  81. package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
  82. package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
  83. package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
  84. package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
  85. package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
  86. package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
  87. package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
  88. package/native-src/src/librats/crypto/hkdf.c +266 -0
  89. package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
  90. package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
  91. package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
  92. package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
  93. package/native-src/src/librats/crypto/poly1305.h +37 -0
  94. package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
  95. package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
  96. package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
  97. package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
  98. package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
  99. package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
  100. package/native-src/src/librats/dht/announce.cpp +37 -0
  101. package/native-src/src/librats/dht/announce.h +41 -0
  102. package/native-src/src/librats/dht/bep42.cpp +109 -0
  103. package/native-src/src/librats/dht/bep42.h +48 -0
  104. package/native-src/src/librats/dht/dht.cpp +501 -0
  105. package/native-src/src/librats/dht/dht.h +119 -0
  106. package/native-src/src/librats/dht/dht_runner.cpp +103 -0
  107. package/native-src/src/librats/dht/dht_runner.h +71 -0
  108. package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
  109. package/native-src/src/librats/dht/dos_blocker.h +47 -0
  110. package/native-src/src/librats/dht/find_peers.cpp +52 -0
  111. package/native-src/src/librats/dht/find_peers.h +73 -0
  112. package/native-src/src/librats/dht/id.h +167 -0
  113. package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
  114. package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
  115. package/native-src/src/librats/dht/log.h +38 -0
  116. package/native-src/src/librats/dht/node.cpp +473 -0
  117. package/native-src/src/librats/dht/node.h +164 -0
  118. package/native-src/src/librats/dht/node_entry.h +81 -0
  119. package/native-src/src/librats/dht/observer.h +72 -0
  120. package/native-src/src/librats/dht/persistence.cpp +90 -0
  121. package/native-src/src/librats/dht/persistence.h +32 -0
  122. package/native-src/src/librats/dht/routing_table.cpp +559 -0
  123. package/native-src/src/librats/dht/routing_table.h +185 -0
  124. package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
  125. package/native-src/src/librats/dht/rpc_manager.h +77 -0
  126. package/native-src/src/librats/dht/storage.cpp +92 -0
  127. package/native-src/src/librats/dht/storage.h +74 -0
  128. package/native-src/src/librats/dht/transport.h +27 -0
  129. package/native-src/src/librats/dht/traversal.cpp +326 -0
  130. package/native-src/src/librats/dht/traversal.h +120 -0
  131. package/native-src/src/librats/dht/udp_transport.cpp +49 -0
  132. package/native-src/src/librats/dht/udp_transport.h +51 -0
  133. package/native-src/src/librats/mdns/log.h +22 -0
  134. package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
  135. package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
  136. package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
  137. package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
  138. package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
  139. package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
  140. package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
  141. package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
  142. package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
  143. package/native-src/src/librats/node/circuit_service.h +84 -0
  144. package/native-src/src/librats/node/config.h +110 -0
  145. package/native-src/src/librats/node/dial_service.h +54 -0
  146. package/native-src/src/librats/node/dialer.cpp +264 -0
  147. package/native-src/src/librats/node/dialer.h +188 -0
  148. package/native-src/src/librats/node/host_events.h +26 -0
  149. package/native-src/src/librats/node/identify.cpp +130 -0
  150. package/native-src/src/librats/node/identify.h +71 -0
  151. package/native-src/src/librats/node/nat_status.cpp +103 -0
  152. package/native-src/src/librats/node/nat_status.h +118 -0
  153. package/native-src/src/librats/node/node.cpp +865 -0
  154. package/native-src/src/librats/node/node.h +344 -0
  155. package/native-src/src/librats/node/node_context.h +33 -0
  156. package/native-src/src/librats/node/peer_network.h +91 -0
  157. package/native-src/src/librats/peer/peer.h +49 -0
  158. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  159. package/native-src/src/librats/peer/peer_book.h +88 -0
  160. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  161. package/native-src/src/librats/peer/peer_id.h +62 -0
  162. package/native-src/src/librats/peer/peer_info.h +37 -0
  163. package/native-src/src/librats/peer/peer_table.cpp +170 -0
  164. package/native-src/src/librats/peer/peer_table.h +148 -0
  165. package/native-src/src/librats/security/handshaker.h +66 -0
  166. package/native-src/src/librats/security/identity.h +43 -0
  167. package/native-src/src/librats/security/noise_security.cpp +122 -0
  168. package/native-src/src/librats/security/noise_security.h +37 -0
  169. package/native-src/src/librats/security/plaintext_security.h +106 -0
  170. package/native-src/src/librats/security/session.h +37 -0
  171. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  172. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  173. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  174. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  176. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  177. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  178. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  179. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  180. package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
  181. package/native-src/src/librats/subsystems/hole_punch.h +290 -0
  182. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  184. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  185. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  186. package/native-src/src/librats/subsystems/message_json.h +88 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  188. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  189. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  190. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  192. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  193. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  194. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  195. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  196. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  197. package/native-src/src/librats/subsystems/relay.cpp +1142 -0
  198. package/native-src/src/librats/subsystems/relay.h +211 -0
  199. package/native-src/src/librats/subsystems/relay_service.h +46 -0
  200. package/native-src/src/librats/transport/connection.cpp +343 -0
  201. package/native-src/src/librats/transport/connection.h +283 -0
  202. package/native-src/src/librats/transport/link.h +96 -0
  203. package/native-src/src/librats/transport/reactor.cpp +588 -0
  204. package/native-src/src/librats/transport/reactor.h +262 -0
  205. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  206. package/native-src/src/librats/transport/relay_link.cpp +208 -0
  207. package/native-src/src/librats/transport/relay_link.h +303 -0
  208. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  209. package/native-src/src/librats/transport/tcp_link.h +43 -0
  210. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  211. package/native-src/src/librats/transport/udp_mux.h +363 -0
  212. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  213. package/native-src/src/librats/transport/udp_packet.h +190 -0
  214. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  215. package/native-src/src/librats/transport/udp_stream.h +614 -0
  216. package/native-src/src/librats/util/features.h.in +51 -0
  217. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  218. package/native-src/src/librats/util/fs.h +136 -0
  219. package/native-src/src/librats/util/json.cpp +1002 -0
  220. package/native-src/src/librats/util/json.h +444 -0
  221. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  222. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  223. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  224. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  225. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  226. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  227. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  228. package/native-src/src/librats/util/rats_export.h +69 -0
  229. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  230. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  231. package/native-src/src/librats/wire/frame.cpp +76 -0
  232. package/native-src/src/librats/wire/frame.h +111 -0
  233. package/native-src/src/librats/wire/message_router.cpp +45 -0
  234. package/native-src/src/librats/wire/message_router.h +47 -0
  235. package/package.json +5 -4
  236. package/scripts/build-librats.js +1 -0
  237. package/scripts/postinstall.js +3 -3
  238. package/scripts/prepare-package.js +4 -4
  239. package/scripts/verify-installation.js +63 -105
  240. package/src/librats_node.cpp +1067 -1323
  241. package/native-src/src/bencode.cpp +0 -485
  242. package/native-src/src/bencode.h +0 -145
  243. package/native-src/src/bittorrent.cpp +0 -14
  244. package/native-src/src/bittorrent.h +0 -74
  245. package/native-src/src/bt_bitfield.cpp +0 -372
  246. package/native-src/src/bt_bitfield.h +0 -316
  247. package/native-src/src/bt_choker.cpp +0 -228
  248. package/native-src/src/bt_choker.h +0 -147
  249. package/native-src/src/bt_client.cpp +0 -1047
  250. package/native-src/src/bt_client.h +0 -445
  251. package/native-src/src/bt_create_torrent.cpp +0 -677
  252. package/native-src/src/bt_create_torrent.h +0 -473
  253. package/native-src/src/bt_extension.cpp +0 -469
  254. package/native-src/src/bt_extension.h +0 -309
  255. package/native-src/src/bt_file_storage.cpp +0 -261
  256. package/native-src/src/bt_file_storage.h +0 -298
  257. package/native-src/src/bt_handshake.cpp +0 -134
  258. package/native-src/src/bt_handshake.h +0 -157
  259. package/native-src/src/bt_messages.cpp +0 -364
  260. package/native-src/src/bt_messages.h +0 -324
  261. package/native-src/src/bt_network.cpp +0 -1007
  262. package/native-src/src/bt_network.h +0 -417
  263. package/native-src/src/bt_peer_connection.cpp +0 -742
  264. package/native-src/src/bt_peer_connection.h +0 -592
  265. package/native-src/src/bt_piece_picker.cpp +0 -786
  266. package/native-src/src/bt_piece_picker.h +0 -473
  267. package/native-src/src/bt_resume_data.cpp +0 -410
  268. package/native-src/src/bt_resume_data.h +0 -249
  269. package/native-src/src/bt_torrent.cpp +0 -2120
  270. package/native-src/src/bt_torrent.h +0 -641
  271. package/native-src/src/bt_torrent_info.cpp +0 -659
  272. package/native-src/src/bt_torrent_info.h +0 -418
  273. package/native-src/src/bt_types.h +0 -621
  274. package/native-src/src/chained_send_buffer.cpp +0 -75
  275. package/native-src/src/chained_send_buffer.h +0 -137
  276. package/native-src/src/crypto/hkdf.c +0 -266
  277. package/native-src/src/crypto/poly1305.h +0 -36
  278. package/native-src/src/dht.cpp +0 -3311
  279. package/native-src/src/dht.h +0 -717
  280. package/native-src/src/disk_io.cpp +0 -632
  281. package/native-src/src/disk_io.h +0 -315
  282. package/native-src/src/file_transfer.cpp +0 -1415
  283. package/native-src/src/file_transfer.h +0 -286
  284. package/native-src/src/fs.h +0 -108
  285. package/native-src/src/gossipsub.cpp +0 -1139
  286. package/native-src/src/gossipsub.h +0 -403
  287. package/native-src/src/ice.cpp +0 -893
  288. package/native-src/src/ice.h +0 -559
  289. package/native-src/src/json.hpp +0 -25526
  290. package/native-src/src/librats.cpp +0 -2378
  291. package/native-src/src/librats.h +0 -2324
  292. package/native-src/src/librats_bittorrent.cpp +0 -601
  293. package/native-src/src/librats_c.cpp +0 -1557
  294. package/native-src/src/librats_c.h +0 -323
  295. package/native-src/src/librats_discovery.cpp +0 -402
  296. package/native-src/src/librats_encryption.cpp +0 -275
  297. package/native-src/src/librats_file_transfer.cpp +0 -144
  298. package/native-src/src/librats_gossipsub.cpp +0 -289
  299. package/native-src/src/librats_ice.cpp +0 -213
  300. package/native-src/src/librats_log_macros.h +0 -36
  301. package/native-src/src/librats_logging.cpp +0 -173
  302. package/native-src/src/librats_mdns.cpp +0 -166
  303. package/native-src/src/librats_persistence.cpp +0 -796
  304. package/native-src/src/librats_portmap.cpp +0 -419
  305. package/native-src/src/librats_reconnection.cpp +0 -218
  306. package/native-src/src/librats_statistic.cpp +0 -105
  307. package/native-src/src/librats_storage.cpp +0 -189
  308. package/native-src/src/rats_export.h +0 -17
  309. package/native-src/src/receive_buffer.cpp +0 -82
  310. package/native-src/src/receive_buffer.h +0 -127
  311. package/native-src/src/socket.h +0 -228
  312. package/native-src/src/threadmanager.cpp +0 -105
  313. package/native-src/src/threadmanager.h +0 -53
  314. package/native-src/src/tracker.cpp +0 -1264
  315. package/native-src/src/tracker.h +0 -319
  316. package/native-src/src/turn.cpp +0 -762
  317. package/native-src/src/turn.h +0 -460
  318. package/native-src/src/wakeup_pipe.h +0 -60
  319. /package/native-src/src/{os.h → librats/util/os.h} +0 -0
@@ -0,0 +1,363 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file udp_mux.h
5
+ * @brief One UDP socket, every peer: demultiplexing, admission and timers.
6
+ *
7
+ * The whole point of running peer connections over UDP is that they share a
8
+ * single socket and a single port. That is what makes a NAT hold one mapping
9
+ * instead of one per peer, what makes hole punching possible at all, and what
10
+ * makes the port a peer observes us sending from the same port it can dial us
11
+ * back on. The price is that the kernel no longer separates the peers for us, so
12
+ * this class does it: every datagram carries a connection id (see udp_packet.h)
13
+ * that resolves to a UdpStream in one hash lookup.
14
+ *
15
+ * Responsibilities, and deliberately nothing more:
16
+ * - own the socket and the streams on it;
17
+ * - route each datagram to its stream, creating one for an unrecognised Syn
18
+ * (after asking the delegate whether a new inbound peer is welcome);
19
+ * - answer a datagram for a stream we do not have with a Reset, so the peer
20
+ * learns immediately instead of retransmitting into a void;
21
+ * - run every stream's timers off one deadline queue (see below);
22
+ * - keep a stream alive briefly after its connection is gone, so the last
23
+ * bytes it owes the peer still get delivered (see release());
24
+ * - move datagrams in batches rather than one syscall at a time (see
25
+ * flush_output()).
26
+ *
27
+ * ── Why the timers are scheduled, not swept ─────────────────────────────────
28
+ * Every stream owes its peer some work on a deadline: a retransmission timeout, a
29
+ * delayed acknowledgement, a keep-alive, and the idle timeout that ends it. The
30
+ * obvious way to serve those is a fixed sweep — visit every stream every 20 ms and
31
+ * ask each what it needs. It is simple, and it is wrong in both directions:
32
+ *
33
+ * - it costs O(streams) whether or not anything is due, and
34
+ * - far worse, it wakes the reactor fifty times a second *forever*. A connected
35
+ * stream that is doing nothing genuinely needs to be visited once per
36
+ * keep-alive — every 10 s — and a node with no datagram streams at all needs
37
+ * no timer whatsoever. A sweep pays 500x that, and a dial-only node pays it
38
+ * for peers it does not have.
39
+ *
40
+ * So each stream reports when it next needs servicing (UdpStream::next_deadline)
41
+ * and the mux keeps them in a min-heap ordered by that deadline. tick() services
42
+ * only what has come due — O(due + log n) — and next_timeout_ms() tells the
43
+ * reactor how long it may sleep, so an idle mux contributes no wake-ups at all.
44
+ *
45
+ * Keeping that honest is one rule: a deadline must never move *earlier* without
46
+ * the mux being told. Every path that can mutate a stream therefore re-arms it —
47
+ * the datagram path here, and read()/write()/close() through UdpStreamLink, which
48
+ * call stream_touched(). Moving a deadline *later* needs no notification: the
49
+ * stream is then visited early, finds nothing to do, and re-arms itself.
50
+ *
51
+ * ── Why the socket is worked in batches ─────────────────────────────────────
52
+ * A datagram transport emits one packet per call, so at a 1200-byte payload a
53
+ * bulk transfer costs tens of thousands of syscalls per megabyte — far more than
54
+ * the framing, the congestion control and the encryption above it put together,
55
+ * and the reason a user-space stream costs measurably more CPU per byte than the
56
+ * kernel's TCP. So the mux does not hand each datagram to the socket as it is
57
+ * produced: outgoing datagrams are staged and leave together (kUdpBatchMax per
58
+ * syscall), and incoming ones are collected the same way.
59
+ *
60
+ * Staging is deliberately *not* deferred past the work that produced it. Every
61
+ * entry point flushes before it returns, and the Reactor flushes once more at the
62
+ * end of each loop turn to cover datagrams produced by application sends — so a
63
+ * packet never waits on a timer, and batching costs no latency, only syscalls.
64
+ *
65
+ * All of that is conditional on the platform actually having a batched send
66
+ * (kUdpBatchIsOneSyscall). Where it does not — Windows, macOS — staging would pay
67
+ * a copy per datagram to save syscalls that cannot be saved, so send_datagram()
68
+ * hands each datagram to the socket as it is produced and the staging path is
69
+ * compiled out entirely. The receive side has no such split: its fallback fills
70
+ * the caller's slots directly, so it costs nothing extra either way.
71
+ *
72
+ * Threading follows the rest of the transport: the mux belongs to exactly one
73
+ * Reactor and is touched only by that reactor's thread.
74
+ *
75
+ * Events are never delivered from inside the datagram loop. A stream records what
76
+ * its connection should see, and the mux dispatches once the batch it is standing
77
+ * in is finished — so a handler that tears its connection down can never free a
78
+ * stream the loop is still walking. Per batch, not per drain: what a stream
79
+ * delivers waits in its in-order buffer until the connection reads it out, so
80
+ * holding every event to the end of the drain would let that buffer grow to
81
+ * everything one peer could send in it. Events for the same connection coalesce,
82
+ * because a bulk sender raises one per packet and only the first has work to do.
83
+ */
84
+
85
+ #include "librats/core/address.h"
86
+ #include "librats/core/socket.h"
87
+ #include "librats/core/types.h"
88
+ #include "librats/transport/link.h"
89
+ #include "librats/transport/udp_stream.h"
90
+
91
+ #include <array>
92
+ #include <chrono>
93
+ #include <cstdint>
94
+ #include <memory>
95
+ #include <random>
96
+ #include <unordered_map>
97
+ #include <vector>
98
+
99
+ namespace librats {
100
+
101
+ /// What the mux needs from the reactor above it.
102
+ class UdpMuxDelegate {
103
+ public:
104
+ virtual ~UdpMuxDelegate() = default;
105
+
106
+ /// Adopt a freshly accepted inbound stream as a connection. Returns the new
107
+ /// ConnId, or kInvalidConnId to refuse it (the mux then resets the peer).
108
+ virtual ConnId adopt_inbound_link(std::unique_ptr<Link> link) = 0;
109
+
110
+ /// Deliver poll-equivalent events (PollIn/PollOut/PollErr) to a connection.
111
+ virtual void dispatch_link_events(ConnId id, uint32_t events) = 0;
112
+ };
113
+
114
+ /// What the mux is willing to spend on peers it has not heard a round trip from.
115
+ ///
116
+ /// A TCP listener gets this for free: an inbound connection costs a file
117
+ /// descriptor, and an attacker cannot make the kernel allocate one without
118
+ /// completing a three-way handshake — which a forged source address cannot do.
119
+ /// A datagram listener has neither the descriptor nor the handshake, so an
120
+ /// unanswered Syn would otherwise buy an attacker a stream, a connection and a
121
+ /// half-built Noise handshake for the price of sixteen forged bytes. These two
122
+ /// numbers are what puts the datagram side back on the same footing.
123
+ struct UdpMuxLimits {
124
+ /// Concurrent streams the mux will hold at all. A backstop rather than the
125
+ /// working limit — with validation on (below), everything counted here has
126
+ /// proved it can receive at its address, and the node's own peer cap governs
127
+ /// long before this does. Reaching it answers further dials with a Reset.
128
+ size_t max_streams = 8192;
129
+
130
+ /// Streams above which a Syn must carry a valid address-validation cookie
131
+ /// before it costs anything. This, not max_streams, is the bound on state a
132
+ /// forged source address can create: past it a bare Syn is answered with a
133
+ /// Retry and nothing is kept, so unvalidated state can never exceed roughly
134
+ /// this many streams (~1.5 MiB at the default).
135
+ ///
136
+ /// The cost of validation is one extra round trip on an inbound dial, paid
137
+ /// only by a node that is already carrying this many streams — so an ordinary
138
+ /// node never pays it, and a node under a flood pays it instead of dying.
139
+ /// 0 validates every inbound dial; SIZE_MAX never validates any.
140
+ size_t validate_above = 1024;
141
+ };
142
+
143
+ class UdpMux final : public UdpStreamHost {
144
+ public:
145
+ using Clock = UdpStream::Clock;
146
+
147
+ /// How long a stream is kept after its connection is gone, to finish handing
148
+ /// over what it already accepted from the application. TCP gets this from the
149
+ /// kernel, which keeps retransmitting after close(); without it, a node that
150
+ /// sends a last message and disconnects would simply lose it.
151
+ static constexpr std::chrono::seconds kLingerTimeout{5};
152
+
153
+ /// Datagrams drained in one readable event before yielding back to the
154
+ /// reactor, so a flood cannot monopolise the loop. Hitting it is reported so
155
+ /// the caller can come straight back (the poller is edge-triggered on kqueue,
156
+ /// which makes "drain until empty" a correctness requirement, not a nicety).
157
+ static constexpr size_t kMaxDatagramsPerRead = 4096;
158
+
159
+ /// Replies emitted per kUnsolicitedReplyWindow to datagrams that belong to no
160
+ /// stream — the Reset that tells a peer we have forgotten it, and the Retry
161
+ /// that asks an unproven one to come back with a cookie. Both are answers to
162
+ /// unauthenticated traffic from an address we have not verified, so both are
163
+ /// budgeted together: a hostile sender must not be able to turn this socket
164
+ /// into a reflector, whichever of the two it provokes.
165
+ ///
166
+ /// The budget is anchored to the clock rather than to tick(), because tick()
167
+ /// no longer runs on a fixed cadence — and the case this defends against is a
168
+ /// flood of junk at a node with *no* streams, which is precisely when a
169
+ /// deadline-driven tick would never run at all.
170
+ static constexpr int kMaxUnsolicitedReplies = 64;
171
+ static constexpr std::chrono::milliseconds kUnsolicitedReplyWindow{20};
172
+
173
+ /// How long a cookie secret stays current. Two are kept, so a cookie is good
174
+ /// for between one and two of these — long enough to survive a slow round trip
175
+ /// and a retransmitted Syn, short enough that a leaked secret is worthless
176
+ /// almost at once.
177
+ static constexpr std::chrono::seconds kCookieSecretLifetime{30};
178
+
179
+ /// Socket buffer requested in each direction. One socket carries every peer,
180
+ /// so the default (tens of kilobytes on most systems) is far too small: a
181
+ /// burst that overruns it is dropped by the kernel, and each drop costs a
182
+ /// retransmission timeout. This is the single most effective knob there is on
183
+ /// datagram throughput.
184
+ static constexpr int kSocketBufferBytes = 4 * 1024 * 1024;
185
+
186
+ UdpMux(socket_t socket, AddressFamily family, UdpMuxDelegate& delegate,
187
+ UdpMuxLimits limits = {});
188
+ ~UdpMux() override;
189
+
190
+ UdpMux(const UdpMux&) = delete;
191
+ UdpMux& operator=(const UdpMux&) = delete;
192
+
193
+ socket_t socket() const noexcept { return socket_; }
194
+
195
+ /// The address family this mux's one socket is bound in. A dial to anything
196
+ /// outside it cannot be carried at all (see family_can_reach), so the reactor
197
+ /// asks before opening a stream rather than after the Syn has run out.
198
+ AddressFamily family() const noexcept { return family_; }
199
+
200
+ /// Drain the socket and route what arrives. Returns true if it stopped at
201
+ /// kMaxDatagramsPerRead with more possibly pending.
202
+ bool on_readable();
203
+
204
+ /// Service the streams whose deadline has passed, and retire finished
205
+ /// lingering ones. Cheap to call on every turn of the reactor loop — with
206
+ /// nothing due it is one comparison against the earliest deadline.
207
+ void tick();
208
+
209
+ /// How long the reactor may sleep before tick() has work: milliseconds until
210
+ /// the earliest scheduled deadline, 0 if one has already passed, and `max_ms`
211
+ /// when no stream is scheduled at all. Mirrors TimerQueue::next_timeout_ms,
212
+ /// including the round-up (truncating a sub-millisecond remainder to 0 would
213
+ /// busy-spin to the deadline instead of sleeping through it).
214
+ int next_timeout_ms(int max_ms) const noexcept;
215
+
216
+ /// Re-read `stream`'s deadline after something outside the datagram path
217
+ /// changed it. Called by UdpStreamLink for read/write/close — read() can
218
+ /// re-open a closed window (an ack owed at once) and write() starts the
219
+ /// retransmission timer, both of which move the deadline *earlier*.
220
+ void stream_touched(UdpStream& stream);
221
+
222
+ /// Open an outbound stream to `remote`, ready to be adopted as a connection.
223
+ /// Returns nullptr only if no free connection id could be found.
224
+ /// @param profile how hard the dial's Syn is retried — a hole punch wants a
225
+ /// denser burst than an ordinary dial does (see DialProfile).
226
+ std::unique_ptr<Link> connect(const Address& remote, DialProfile profile = {});
227
+
228
+ /// Called when the Link that owned `stream` is destroyed. The stream either
229
+ /// goes away at once or lingers until it has flushed what it owes.
230
+ void release(UdpStream& stream);
231
+
232
+ /// Reset every stream (the node is going down) and drop them.
233
+ void shutdown();
234
+
235
+ /// Put every staged datagram on the wire. Called by each of the entry points
236
+ /// above before it returns, and by the Reactor at the end of a loop turn to
237
+ /// cover the ones an application send produced (Connection::send → the stream's
238
+ /// write path, which reaches this class from outside any datagram batch).
239
+ /// Cheap and idempotent when nothing is staged.
240
+ void flush_output();
241
+
242
+ size_t stream_count() const noexcept { return streams_.size(); }
243
+
244
+ // — UdpStreamHost —
245
+ void send_datagram(const Address& to, const uint8_t* data, size_t len) override;
246
+ void stream_events(UdpStream& stream, uint32_t events) override;
247
+
248
+ private:
249
+ struct Entry {
250
+ std::unique_ptr<UdpStream> stream;
251
+ /// When a released stream stops being worth keeping. Epoch = still owned
252
+ /// by a live connection.
253
+ Clock::time_point linger_until{};
254
+ /// The deadline this stream currently occupies a slot in `due_` for.
255
+ /// Meaningful only while `armed`; the pair is what makes lazy deletion
256
+ /// work — a heap slot whose deadline no longer matches has been
257
+ /// superseded by an earlier one and is dropped when it surfaces.
258
+ Clock::time_point scheduled{};
259
+ bool armed = false;
260
+ };
261
+
262
+ /// One slot in the deadline heap. Deliberately a plain value (8 + 4 bytes, no
263
+ /// pointer into the map) so a stream can be erased without hunting down the
264
+ /// slots that name it.
265
+ struct Due {
266
+ Clock::time_point at;
267
+ uint32_t id; ///< the stream's recv id, i.e. its key in streams_
268
+ };
269
+ /// Min-heap ordering: std::*_heap build max-heaps, so "later first" puts the
270
+ /// earliest deadline at front().
271
+ struct LaterFirst {
272
+ bool operator()(const Due& a, const Due& b) const noexcept { return a.at > b.at; }
273
+ };
274
+
275
+ void handle_datagram(const rudp::Packet& p, const Address& from, Clock::time_point now);
276
+ /// Decide whether a Syn for an unknown stream may become one. Answers the peer
277
+ /// itself (Reset at the ceiling, Retry when it has yet to prove its address)
278
+ /// and returns false when nothing should be created.
279
+ bool admit_syn(const rudp::Packet& syn, const Address& from, Clock::time_point now);
280
+ void accept_inbound(const rudp::Packet& syn, const Address& from, Clock::time_point now);
281
+ void send_reset(const Address& to, uint32_t conn_id);
282
+ void send_retry(const Address& to, uint32_t conn_id, uint32_t cookie);
283
+ bool spend_reply_budget(Clock::time_point now);
284
+ UdpStream* find_for_reset(uint32_t conn_id, const Address& from);
285
+ bool allocate_ids(uint32_t& recv_id, uint32_t& send_id);
286
+ void dispatch_pending();
287
+
288
+ // — deadline scheduling —
289
+ /// Give `entry` a heap slot for its next deadline, unless the one it already
290
+ /// holds comes first. Idempotent, and cheap in the common case: a deadline
291
+ /// that moved later costs one comparison and no allocation.
292
+ void arm(uint32_t id, Entry& entry);
293
+ /// Rebuild the heap without the slots lazy deletion left behind, once they
294
+ /// outnumber the live ones badly enough to be worth the pass.
295
+ void compact_due();
296
+
297
+ // — address validation —
298
+ using CookieSecret = std::array<uint8_t, 32>;
299
+ uint32_t cookie_for(const Address& from, uint32_t conn_id, const CookieSecret& secret) const;
300
+ bool cookie_valid(const Address& from, uint32_t conn_id, ByteView payload) const;
301
+ void rotate_cookie_secret(Clock::time_point now);
302
+
303
+ socket_t socket_;
304
+ AddressFamily family_;
305
+ UdpMuxDelegate& delegate_;
306
+ UdpMuxLimits limits_;
307
+
308
+ std::unordered_map<uint32_t, Entry> streams_; ///< keyed by our recv id
309
+ std::vector<Due> due_; ///< min-heap of stream deadlines
310
+ std::vector<std::pair<ConnId, uint32_t>> pending_events_;
311
+
312
+ // Batch scratch. Both directions keep kUdpBatchMax datagrams' worth of storage
313
+ // for the life of the mux — allocated once, never grown, so neither the receive
314
+ // loop nor the send path allocates. The slots point into the storage and are
315
+ // set up in the constructor; only their lengths and endpoints move afterwards.
316
+ std::vector<uint8_t> recv_storage_;
317
+ std::vector<uint8_t> send_storage_;
318
+ std::array<UdpBatchSlot, kUdpBatchMax> recv_slots_{};
319
+ std::array<UdpBatchSlot, kUdpBatchMax> send_slots_{};
320
+ size_t staged_ = 0; ///< datagrams awaiting flush_output()
321
+
322
+ // Cookie secrets: [0] is current, [1] the one it replaced. A cookie is checked
323
+ // against both, so one that was handed out just before a rotation still works.
324
+ std::array<CookieSecret, 2> cookie_secret_{};
325
+ Clock::time_point secret_rotated_at_{};
326
+
327
+ std::mt19937 rng_;
328
+ Clock::time_point reply_window_started_{};
329
+ int replies_in_window_ = 0;
330
+ };
331
+
332
+ /// The Link a Connection holds for a UDP stream.
333
+ ///
334
+ /// The stream itself belongs to the mux, not to the connection: it has to outlive
335
+ /// the connection briefly so the last bytes still get delivered, and it has to be
336
+ /// reachable by connection id from the datagram path. So this is a handle — the
337
+ /// translation between Link's socket-shaped vocabulary and the stream's.
338
+ class UdpStreamLink final : public Link {
339
+ public:
340
+ UdpStreamLink(UdpMux& mux, UdpStream& stream) : mux_(mux), stream_(stream) {}
341
+ ~UdpStreamLink() override { mux_.release(stream_); }
342
+
343
+ TransportKind kind() const noexcept override { return TransportKind::Udp; }
344
+
345
+ IoResult read(ByteSpan into) override;
346
+ IoResult write(const ByteView* slices, size_t count) override;
347
+ bool connect_completed() override { return !stream_.dead(); }
348
+ void want_write(bool on) override { stream_.want_write(on); }
349
+
350
+ std::optional<Address> remote_endpoint() const override { return stream_.remote(); }
351
+
352
+ void attach(ConnId id) override { stream_.set_conn_id(id); }
353
+ CloseReason error_reason() const override { return stream_.close_reason(); }
354
+ void close(CloseReason reason) override;
355
+
356
+ UdpStream& stream() noexcept { return stream_; }
357
+
358
+ private:
359
+ UdpMux& mux_;
360
+ UdpStream& stream_;
361
+ };
362
+
363
+ } // namespace librats
@@ -0,0 +1,121 @@
1
+ #include "librats/transport/udp_packet.h"
2
+
3
+ #include <cstring>
4
+
5
+ namespace librats {
6
+ namespace rudp {
7
+
8
+ namespace {
9
+
10
+ void put_u16(uint8_t* p, uint16_t v) {
11
+ p[0] = static_cast<uint8_t>(v >> 8);
12
+ p[1] = static_cast<uint8_t>(v);
13
+ }
14
+
15
+ void put_u32(uint8_t* p, uint32_t v) {
16
+ p[0] = static_cast<uint8_t>(v >> 24);
17
+ p[1] = static_cast<uint8_t>(v >> 16);
18
+ p[2] = static_cast<uint8_t>(v >> 8);
19
+ p[3] = static_cast<uint8_t>(v);
20
+ }
21
+
22
+ uint16_t get_u16(const uint8_t* p) {
23
+ return static_cast<uint16_t>((static_cast<uint16_t>(p[0]) << 8) | p[1]);
24
+ }
25
+
26
+ uint32_t get_u32(const uint8_t* p) {
27
+ return (static_cast<uint32_t>(p[0]) << 24) | (static_cast<uint32_t>(p[1]) << 16) |
28
+ (static_cast<uint32_t>(p[2]) << 8) | static_cast<uint32_t>(p[3]);
29
+ }
30
+
31
+ } // namespace
32
+
33
+ size_t encode_header(const Packet& p, uint8_t* out) {
34
+ const uint8_t flags = p.has_sack() ? static_cast<uint8_t>(p.flags)
35
+ : static_cast<uint8_t>(p.flags & ~FlagSack);
36
+
37
+ out[0] = static_cast<uint8_t>((kVersion << 4) | (static_cast<uint8_t>(p.type) & 0x0F));
38
+ out[1] = flags;
39
+ put_u16(out + 2, p.window);
40
+ put_u32(out + 4, p.conn_id);
41
+ put_u32(out + 8, p.seq);
42
+ put_u32(out + 12, p.ack);
43
+
44
+ size_t n = kHeaderSize;
45
+ if (flags & FlagSack) {
46
+ put_u32(out + n, p.sack);
47
+ n += kSackSize;
48
+ }
49
+ return n;
50
+ }
51
+
52
+ size_t encode(const Packet& p, uint8_t* out) {
53
+ size_t n = encode_header(p, out);
54
+ if (!p.payload.empty()) {
55
+ std::memcpy(out + n, p.payload.data(), p.payload.size());
56
+ n += p.payload.size();
57
+ }
58
+ return n;
59
+ }
60
+
61
+ bool decode(const uint8_t* data, size_t len, Packet& out) {
62
+ if (len < kHeaderSize) return false;
63
+ if ((data[0] >> 4) != kVersion) return false;
64
+
65
+ const uint8_t type = data[0] & 0x0F;
66
+ if (type > static_cast<uint8_t>(PacketType::Retry)) return false;
67
+
68
+ out.type = static_cast<PacketType>(type);
69
+ out.flags = data[1];
70
+ out.window = get_u16(data + 2);
71
+ out.conn_id = get_u32(data + 4);
72
+ out.seq = get_u32(data + 8);
73
+ out.ack = get_u32(data + 12);
74
+
75
+ size_t offset = kHeaderSize;
76
+ if (out.has_sack()) {
77
+ if (len < offset + kSackSize) return false;
78
+ out.sack = get_u32(data + offset);
79
+ offset += kSackSize;
80
+ } else {
81
+ out.sack = 0;
82
+ }
83
+
84
+ out.payload = ByteView(data + offset, len - offset);
85
+
86
+ // What a type is allowed to carry after the header. Being strict here is what
87
+ // keeps a padded or spliced datagram from ever reaching a stream as stream
88
+ // content, and keeps the accounting ("a Data packet is worth payload.size()
89
+ // bytes") true by construction.
90
+ switch (out.type) {
91
+ case PacketType::Data:
92
+ break; // stream bytes, any length up to kMaxPayload
93
+ case PacketType::Syn:
94
+ case PacketType::Retry:
95
+ // The address-validation cookie, or nothing at all. Fixed width, so
96
+ // there is no room to smuggle anything alongside it.
97
+ if (!out.payload.empty() && out.payload.size() != kCookieSize) return false;
98
+ break;
99
+ default:
100
+ if (!out.payload.empty()) return false; // Ack/Fin/Reset are header-only
101
+ break;
102
+ }
103
+ if (out.payload.size() > kMaxPayload) return false;
104
+
105
+ return true;
106
+ }
107
+
108
+ const char* to_string(PacketType t) noexcept {
109
+ switch (t) {
110
+ case PacketType::Syn: return "SYN";
111
+ case PacketType::Data: return "DATA";
112
+ case PacketType::Ack: return "ACK";
113
+ case PacketType::Fin: return "FIN";
114
+ case PacketType::Reset: return "RESET";
115
+ case PacketType::Retry: return "RETRY";
116
+ }
117
+ return "?";
118
+ }
119
+
120
+ } // namespace rudp
121
+ } // namespace librats
@@ -0,0 +1,190 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file udp_packet.h
5
+ * @brief Wire format of the reliable-UDP transport: one fixed 16-byte header.
6
+ *
7
+ * A datagram is a header, an optional 4-byte selective-ack word, and (for Data)
8
+ * a payload. Everything is big-endian, and every field is fixed-width, so decode
9
+ * is a handful of loads with a single length check — no allocation, no parsing
10
+ * state, and nothing a hostile datagram can make us over-reserve.
11
+ *
12
+ * 0 1 2 3
13
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
14
+ * +-------+-------+---------------+-------------------------------+
15
+ * | ver | type | flags | window |
16
+ * +-------+-------+---------------+-------------------------------+
17
+ * | conn_id |
18
+ * +---------------------------------------------------------------+
19
+ * | seq |
20
+ * +---------------------------------------------------------------+
21
+ * | ack |
22
+ * +---------------------------------------------------------------+
23
+ * | sack (only if flag Sack) |
24
+ * +---------------------------------------------------------------+
25
+ *
26
+ * - conn_id : the id the *receiver* registered this stream under, so a single
27
+ * shared socket can demultiplex thousands of streams with one hash
28
+ * lookup and no per-peer socket. Each side picks its own; see
29
+ * udp_stream.h for how the pair is derived from the Syn.
30
+ * - seq : sequence number of this packet. Syn, Data and Fin each consume
31
+ * one (so all three are retransmitted until acknowledged); an Ack
32
+ * carries the next sequence number to be used and consumes nothing,
33
+ * which is why a pure acknowledgement is never itself acknowledged.
34
+ * - ack : the highest sequence number received *in order* — a cumulative
35
+ * acknowledgement, so a lost Ack costs nothing as long as a later
36
+ * one arrives. 0 means "nothing received yet" (sequence numbers
37
+ * start at 1).
38
+ * - sack : a bitmap acknowledging the 32 packets after the hole at ack+1
39
+ * (bit i ⇒ ack+2+i arrived). This is what lets a single loss be
40
+ * repaired without stalling everything queued behind it.
41
+ * - window : how many further packets the sender of this datagram can buffer,
42
+ * in packets. This is the flow-control signal; 0 stops the peer.
43
+ *
44
+ * Only three types carry anything after the header. Data carries stream bytes.
45
+ * Retry and Syn carry the kCookieSize address-validation cookie, or nothing —
46
+ * a responder under load answers a Syn with a Retry rather than opening a stream,
47
+ * and only a Syn that hands the cookie back costs it any memory (see udp_mux.h).
48
+ * Everything else is header-only, and a datagram that pads one is rejected.
49
+ *
50
+ * Sequence numbers are 32-bit and wrap; compare them only with seq_less/seq_diff,
51
+ * never with < on the raw value.
52
+ */
53
+
54
+ #include "librats/core/bytes.h"
55
+
56
+ #include <cstdint>
57
+ #include <cstddef>
58
+
59
+ namespace librats {
60
+ namespace rudp {
61
+
62
+ /// Protocol version carried in the high nibble of byte 0. Bumped only for a
63
+ /// change no existing peer could parse; a peer that sees another version drops
64
+ /// the datagram (silently — an unauthenticated sender gets no reply).
65
+ constexpr uint8_t kVersion = 1;
66
+
67
+ enum class PacketType : uint8_t {
68
+ Syn = 0, ///< open a stream (initiator → responder); consumes a sequence number
69
+ Data = 1, ///< stream payload; consumes a sequence number
70
+ Ack = 2, ///< pure acknowledgement / window update / keep-alive
71
+ Fin = 3, ///< orderly end of the sender's stream; consumes a sequence number
72
+ Reset = 4, ///< abort now: the stream is gone or was never known
73
+ Retry = 5, ///< "prove you are at that address first" — carries a cookie, holds no state
74
+ };
75
+
76
+ enum PacketFlags : uint8_t {
77
+ FlagNone = 0,
78
+ FlagSack = 1 << 0, ///< the 4-byte selective-ack word follows the header
79
+ };
80
+
81
+ /// Bytes on the wire before the payload, without the selective-ack word.
82
+ constexpr size_t kHeaderSize = 16;
83
+ /// Bytes added by the selective-ack word.
84
+ constexpr size_t kSackSize = 4;
85
+ /// Packets one selective-ack word can name — the 32 that follow the hole at
86
+ /// ack+1. This is a reach as well as a width: a sender learns nothing about a
87
+ /// packet further than this past its oldest unacknowledged one, which is what
88
+ /// bounds how far into the retransmission queue an acknowledgement can ever mark
89
+ /// anything (see UdpStream::repair_sacked_holes).
90
+ constexpr uint32_t kSackBits = 8 * static_cast<uint32_t>(kSackSize);
91
+ /// The largest a header can get. A sender that keeps this much headroom in front
92
+ /// of a payload can write the header directly ahead of the bytes it describes and
93
+ /// hand the socket one contiguous datagram — see encode_header().
94
+ constexpr size_t kMaxHeaderSize = kHeaderSize + kSackSize;
95
+
96
+ /// Bytes of the address-validation cookie a Retry hands out and a Syn hands back
97
+ /// (see udp_mux.h). Four is the width of the truncated keyed hash it carries: an
98
+ /// attacker who cannot receive at the address it is bound to gets one guess in
99
+ /// 2^32 per Syn, which is the same order of protection a TCP SYN cookie encodes
100
+ /// into a 32-bit sequence number.
101
+ constexpr size_t kCookieSize = 4;
102
+
103
+ /// Payload carried by one Data packet. 1200 keeps header+payload inside the
104
+ /// smallest MTU worth designing for (IPv6's 1280 floor, minus room for an IPv6
105
+ /// header plus a tunnel), so a stream never depends on IP fragmentation — which
106
+ /// on a datagram path turns one lost fragment into a lost packet.
107
+ constexpr size_t kMaxPayload = 1200;
108
+
109
+ /// Largest datagram this transport ever sends or expects to receive.
110
+ constexpr size_t kMaxDatagram = kHeaderSize + kSackSize + kMaxPayload;
111
+
112
+ /// Packets a receiver will hold out of order, and therefore the largest window it
113
+ /// ever advertises. This is the hard ceiling on in-flight data, so it is also the
114
+ /// ceiling on throughput: a window of W packets on a path of RTT R can never
115
+ /// exceed W * kMaxPayload / R, whatever the link underneath can do.
116
+ ///
117
+ /// 1024 * 1200 B ≈ 1.2 MiB, i.e. ~96 Mbit/s at 100 ms and ~48 Mbit/s at 200 ms —
118
+ /// enough that an intercontinental path is limited by the path rather than by
119
+ /// this constant. (At the previous 256 it was ~24 Mbit/s at 100 ms, well under
120
+ /// what TCP would have managed on the same path.)
121
+ ///
122
+ /// It is also what bounds the memory one peer can make us hold: the reorder
123
+ /// buffer never holds more than this many packets, so ~1.2 MiB per stream in the
124
+ /// worst case. That worst case needs a window's worth of loss to reach, and both
125
+ /// the reorder map and the retransmission queue only ever grow to what is
126
+ /// actually outstanding — an idle or slow stream costs nothing near it.
127
+ constexpr uint16_t kMaxWindowPackets = 1024;
128
+
129
+ struct Packet {
130
+ PacketType type = PacketType::Ack;
131
+ uint8_t flags = FlagNone;
132
+ uint16_t window = 0;
133
+ uint32_t conn_id = 0;
134
+ uint32_t seq = 0;
135
+ uint32_t ack = 0;
136
+ uint32_t sack = 0; ///< meaningful only when (flags & FlagSack)
137
+ ByteView payload; ///< points into the caller's receive buffer
138
+
139
+ bool has_sack() const noexcept { return (flags & FlagSack) != 0; }
140
+ };
141
+
142
+ /// Bytes `p`'s header occupies on the wire: the fixed part, plus the selective-ack
143
+ /// word when one is carried.
144
+ inline size_t header_size(const Packet& p) noexcept {
145
+ return p.has_sack() ? kHeaderSize + kSackSize : kHeaderSize;
146
+ }
147
+
148
+ /// Serialise only `p`'s header (and its optional sack word) into `out`, which must
149
+ /// have room for kMaxHeaderSize bytes. The payload is NOT copied.
150
+ ///
151
+ /// This is the form used on the send path: a packet buffer carries kMaxHeaderSize
152
+ /// bytes of headroom in front of its payload, so the header is written directly
153
+ /// ahead of the bytes it describes and the whole datagram goes to the socket in
154
+ /// one piece — no second copy of the payload just to prefix a header to it.
155
+ /// @return the number of bytes written (kHeaderSize, or kHeaderSize + kSackSize).
156
+ size_t encode_header(const Packet& p, uint8_t* out);
157
+
158
+ /// Serialise `p` (header, optional sack word, then payload) into `out`, which must
159
+ /// have room for kMaxHeaderSize + p.payload.size() bytes.
160
+ /// @return the number of bytes written.
161
+ size_t encode(const Packet& p, uint8_t* out);
162
+
163
+ /// Parse one datagram. Returns false for anything malformed: a short buffer, an
164
+ /// unknown version, an unknown type, or a payload on a type that cannot carry one.
165
+ /// `out.payload` points into `data` and is valid only while that buffer is.
166
+ bool decode(const uint8_t* data, size_t len, Packet& out);
167
+
168
+ // ── Wrapping sequence arithmetic ────────────────────────────────────────────
169
+ //
170
+ // Sequence numbers advance forever in a 32-bit space, so ordering is only ever
171
+ // meaningful over distances far smaller than half that space. Comparing the raw
172
+ // values would invert the moment the counter wraps; comparing the *difference* as
173
+ // a signed number is correct across the wrap and is what every window check here
174
+ // goes through.
175
+
176
+ /// Signed distance a - b, correct across the 32-bit wrap.
177
+ inline int32_t seq_diff(uint32_t a, uint32_t b) noexcept {
178
+ return static_cast<int32_t>(a - b);
179
+ }
180
+
181
+ /// True when a precedes b.
182
+ inline bool seq_less(uint32_t a, uint32_t b) noexcept { return seq_diff(a, b) < 0; }
183
+
184
+ /// True when a precedes or equals b.
185
+ inline bool seq_le(uint32_t a, uint32_t b) noexcept { return seq_diff(a, b) <= 0; }
186
+
187
+ const char* to_string(PacketType) noexcept;
188
+
189
+ } // namespace rudp
190
+ } // namespace librats