librats 1.0.2 → 2.1.4

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 (313) hide show
  1. package/README.md +137 -332
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +271 -696
  4. package/lib/index.js +383 -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 +396 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +731 -0
  10. package/native-src/src/librats/bindings/rats.h +352 -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 +42 -0
  73. package/native-src/src/librats/core/types.h +93 -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/config.h +110 -0
  144. package/native-src/src/librats/node/dial_service.h +54 -0
  145. package/native-src/src/librats/node/dialer.cpp +264 -0
  146. package/native-src/src/librats/node/dialer.h +188 -0
  147. package/native-src/src/librats/node/host_events.h +26 -0
  148. package/native-src/src/librats/node/identify.cpp +130 -0
  149. package/native-src/src/librats/node/identify.h +71 -0
  150. package/native-src/src/librats/node/nat_status.cpp +103 -0
  151. package/native-src/src/librats/node/nat_status.h +118 -0
  152. package/native-src/src/librats/node/node.cpp +826 -0
  153. package/native-src/src/librats/node/node.h +333 -0
  154. package/native-src/src/librats/node/node_context.h +33 -0
  155. package/native-src/src/librats/node/peer_network.h +91 -0
  156. package/native-src/src/librats/peer/peer.h +49 -0
  157. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  158. package/native-src/src/librats/peer/peer_book.h +88 -0
  159. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  160. package/native-src/src/librats/peer/peer_id.h +62 -0
  161. package/native-src/src/librats/peer/peer_info.h +37 -0
  162. package/native-src/src/librats/peer/peer_table.cpp +137 -0
  163. package/native-src/src/librats/peer/peer_table.h +148 -0
  164. package/native-src/src/librats/security/handshaker.h +66 -0
  165. package/native-src/src/librats/security/identity.h +43 -0
  166. package/native-src/src/librats/security/noise_security.cpp +122 -0
  167. package/native-src/src/librats/security/noise_security.h +37 -0
  168. package/native-src/src/librats/security/plaintext_security.h +106 -0
  169. package/native-src/src/librats/security/session.h +37 -0
  170. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  171. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  172. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  173. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  174. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  176. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  177. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  178. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  179. package/native-src/src/librats/subsystems/hole_punch.cpp +532 -0
  180. package/native-src/src/librats/subsystems/hole_punch.h +266 -0
  181. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  182. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  184. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  185. package/native-src/src/librats/subsystems/message_json.h +88 -0
  186. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  188. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  189. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  190. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  192. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  193. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  194. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  195. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  196. package/native-src/src/librats/transport/connection.cpp +343 -0
  197. package/native-src/src/librats/transport/connection.h +283 -0
  198. package/native-src/src/librats/transport/link.h +96 -0
  199. package/native-src/src/librats/transport/reactor.cpp +540 -0
  200. package/native-src/src/librats/transport/reactor.h +224 -0
  201. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  202. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  203. package/native-src/src/librats/transport/tcp_link.h +43 -0
  204. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  205. package/native-src/src/librats/transport/udp_mux.h +363 -0
  206. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  207. package/native-src/src/librats/transport/udp_packet.h +190 -0
  208. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  209. package/native-src/src/librats/transport/udp_stream.h +614 -0
  210. package/native-src/src/librats/util/features.h.in +51 -0
  211. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  212. package/native-src/src/librats/util/fs.h +136 -0
  213. package/native-src/src/librats/util/json.cpp +1002 -0
  214. package/native-src/src/librats/util/json.h +444 -0
  215. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  216. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  217. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  218. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  219. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  220. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  221. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  222. package/native-src/src/librats/util/rats_export.h +69 -0
  223. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  224. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  225. package/native-src/src/librats/wire/frame.cpp +76 -0
  226. package/native-src/src/librats/wire/frame.h +110 -0
  227. package/native-src/src/librats/wire/message_router.cpp +45 -0
  228. package/native-src/src/librats/wire/message_router.h +47 -0
  229. package/package.json +5 -4
  230. package/scripts/build-librats.js +1 -0
  231. package/scripts/postinstall.js +3 -3
  232. package/scripts/prepare-package.js +4 -4
  233. package/scripts/verify-installation.js +63 -105
  234. package/src/librats_node.cpp +1042 -1324
  235. package/native-src/src/bencode.cpp +0 -485
  236. package/native-src/src/bencode.h +0 -145
  237. package/native-src/src/bittorrent.cpp +0 -14
  238. package/native-src/src/bittorrent.h +0 -74
  239. package/native-src/src/bt_bitfield.cpp +0 -372
  240. package/native-src/src/bt_bitfield.h +0 -316
  241. package/native-src/src/bt_choker.cpp +0 -228
  242. package/native-src/src/bt_choker.h +0 -147
  243. package/native-src/src/bt_client.cpp +0 -1047
  244. package/native-src/src/bt_client.h +0 -445
  245. package/native-src/src/bt_create_torrent.cpp +0 -677
  246. package/native-src/src/bt_create_torrent.h +0 -473
  247. package/native-src/src/bt_extension.cpp +0 -469
  248. package/native-src/src/bt_extension.h +0 -309
  249. package/native-src/src/bt_file_storage.cpp +0 -261
  250. package/native-src/src/bt_file_storage.h +0 -298
  251. package/native-src/src/bt_handshake.cpp +0 -134
  252. package/native-src/src/bt_handshake.h +0 -157
  253. package/native-src/src/bt_messages.cpp +0 -364
  254. package/native-src/src/bt_messages.h +0 -324
  255. package/native-src/src/bt_network.cpp +0 -1007
  256. package/native-src/src/bt_network.h +0 -417
  257. package/native-src/src/bt_peer_connection.cpp +0 -742
  258. package/native-src/src/bt_peer_connection.h +0 -592
  259. package/native-src/src/bt_piece_picker.cpp +0 -786
  260. package/native-src/src/bt_piece_picker.h +0 -473
  261. package/native-src/src/bt_resume_data.cpp +0 -410
  262. package/native-src/src/bt_resume_data.h +0 -249
  263. package/native-src/src/bt_torrent.cpp +0 -2120
  264. package/native-src/src/bt_torrent.h +0 -641
  265. package/native-src/src/bt_torrent_info.cpp +0 -659
  266. package/native-src/src/bt_torrent_info.h +0 -418
  267. package/native-src/src/bt_types.h +0 -621
  268. package/native-src/src/chained_send_buffer.cpp +0 -75
  269. package/native-src/src/chained_send_buffer.h +0 -137
  270. package/native-src/src/crypto/hkdf.c +0 -266
  271. package/native-src/src/crypto/poly1305.h +0 -36
  272. package/native-src/src/dht.cpp +0 -3311
  273. package/native-src/src/dht.h +0 -717
  274. package/native-src/src/disk_io.cpp +0 -632
  275. package/native-src/src/disk_io.h +0 -315
  276. package/native-src/src/file_transfer.cpp +0 -1415
  277. package/native-src/src/file_transfer.h +0 -286
  278. package/native-src/src/fs.h +0 -108
  279. package/native-src/src/gossipsub.cpp +0 -1139
  280. package/native-src/src/gossipsub.h +0 -403
  281. package/native-src/src/ice.cpp +0 -893
  282. package/native-src/src/ice.h +0 -559
  283. package/native-src/src/json.hpp +0 -25526
  284. package/native-src/src/librats.cpp +0 -2378
  285. package/native-src/src/librats.h +0 -2324
  286. package/native-src/src/librats_bittorrent.cpp +0 -601
  287. package/native-src/src/librats_c.cpp +0 -1557
  288. package/native-src/src/librats_c.h +0 -323
  289. package/native-src/src/librats_discovery.cpp +0 -402
  290. package/native-src/src/librats_encryption.cpp +0 -275
  291. package/native-src/src/librats_file_transfer.cpp +0 -144
  292. package/native-src/src/librats_gossipsub.cpp +0 -289
  293. package/native-src/src/librats_ice.cpp +0 -213
  294. package/native-src/src/librats_log_macros.h +0 -36
  295. package/native-src/src/librats_logging.cpp +0 -173
  296. package/native-src/src/librats_mdns.cpp +0 -166
  297. package/native-src/src/librats_persistence.cpp +0 -796
  298. package/native-src/src/librats_portmap.cpp +0 -419
  299. package/native-src/src/librats_reconnection.cpp +0 -218
  300. package/native-src/src/librats_statistic.cpp +0 -105
  301. package/native-src/src/librats_storage.cpp +0 -189
  302. package/native-src/src/rats_export.h +0 -17
  303. package/native-src/src/receive_buffer.cpp +0 -82
  304. package/native-src/src/receive_buffer.h +0 -127
  305. package/native-src/src/socket.h +0 -228
  306. package/native-src/src/threadmanager.cpp +0 -105
  307. package/native-src/src/threadmanager.h +0 -53
  308. package/native-src/src/tracker.cpp +0 -1264
  309. package/native-src/src/tracker.h +0 -319
  310. package/native-src/src/turn.cpp +0 -762
  311. package/native-src/src/turn.h +0 -460
  312. package/native-src/src/wakeup_pipe.h +0 -60
  313. /package/native-src/src/{os.h → librats/util/os.h} +0 -0
@@ -0,0 +1,181 @@
1
+ #include "librats/peer/peer_book.h"
2
+ #include "librats/util/fs.h"
3
+ #include "librats/util/json.h"
4
+
5
+ #include <algorithm>
6
+ #include <unordered_set>
7
+
8
+ namespace librats {
9
+
10
+ namespace {
11
+
12
+ // "best first" ordering: returns true if a ranks strictly before b.
13
+ bool better(const PeerRecord& a, const PeerRecord& b) {
14
+ const bool ca = a.last_connected > 0, cb = b.last_connected > 0;
15
+ if (ca != cb) return ca; // ever-connected first
16
+ if (a.last_connected != b.last_connected) return a.last_connected > b.last_connected;
17
+ if (a.connect_count != b.connect_count) return a.connect_count > b.connect_count;
18
+ if (a.fail_streak != b.fail_streak) return a.fail_streak < b.fail_streak;
19
+ return a.last_seen > b.last_seen;
20
+ }
21
+
22
+ bool is_stale(const PeerRecord& r, uint64_t now, uint64_t max_age_secs) {
23
+ return max_age_secs > 0 && r.last_seen > 0 && now > r.last_seen && (now - r.last_seen) > max_age_secs;
24
+ }
25
+
26
+ } // namespace
27
+
28
+ void PeerBook::load() {
29
+ const std::string text = read_file_text_cpp(path_);
30
+ if (text.empty()) return;
31
+
32
+ // Non-throwing parse: a malformed/legacy file simply yields no records
33
+ // rather than crashing startup.
34
+ const Json root = Json::parse(text, nullptr, /*allow_exceptions=*/false);
35
+ if (!root.is_array()) return;
36
+
37
+ std::lock_guard<std::mutex> lock(mutex_);
38
+ for (const auto& j : root) {
39
+ if (!j.is_object()) continue;
40
+ const std::string ip = j.value("ip", std::string());
41
+ const uint16_t port = static_cast<uint16_t>(j.value("port", 0));
42
+ const auto ipaddr = IpAddress::parse(ip);
43
+ if (!ipaddr || port == 0) continue; // need a valid numeric ip + port
44
+
45
+ PeerRecord r;
46
+ r.address = Address{*ipaddr, port};
47
+ // Remaining metadata fields are optional; absent ones keep their defaults.
48
+ const std::string idhex = j.value("id", std::string());
49
+ if (!idhex.empty()) { if (auto id = PeerId::from_hex(idhex)) r.id = *id; }
50
+ r.first_seen = j.value("first_seen", uint64_t{0});
51
+ r.last_seen = j.value("last_seen", uint64_t{0});
52
+ r.last_connected = j.value("last_connected", uint64_t{0});
53
+ r.connect_count = j.value("connect_count", uint32_t{0});
54
+ r.fail_streak = j.value("fail_streak", uint32_t{0});
55
+ records_[r.address] = r;
56
+ }
57
+ }
58
+
59
+ void PeerBook::save() const {
60
+ Json arr = Json::array();
61
+ {
62
+ std::lock_guard<std::mutex> lock(mutex_);
63
+ for (const auto& [key, r] : records_) {
64
+ Json j = Json::object();
65
+ j["ip"] = r.address.ip.to_string();
66
+ j["port"] = r.address.port;
67
+ if (!r.id.is_zero()) j["id"] = r.id.to_hex(); // omitted when never connected
68
+ j["first_seen"] = r.first_seen;
69
+ j["last_seen"] = r.last_seen;
70
+ j["last_connected"] = r.last_connected;
71
+ j["connect_count"] = r.connect_count;
72
+ j["fail_streak"] = r.fail_streak;
73
+ arr.push_back(std::move(j));
74
+ }
75
+ }
76
+ create_file(path_.c_str(), arr.dump(2).c_str());
77
+ }
78
+
79
+ void PeerBook::note_connected(const Address& address, const PeerId& id, uint64_t now) {
80
+ std::lock_guard<std::mutex> lock(mutex_);
81
+ PeerRecord& r = records_[address];
82
+ if (r.first_seen == 0) r.first_seen = now;
83
+ r.address = address;
84
+ if (!id.is_zero()) r.id = id;
85
+ r.last_seen = now;
86
+ r.last_connected = now;
87
+ r.connect_count++;
88
+ r.fail_streak = 0;
89
+ }
90
+
91
+ void PeerBook::note_failure(const Address& address, uint64_t /*now*/) {
92
+ std::lock_guard<std::mutex> lock(mutex_);
93
+ auto it = records_.find(address);
94
+ if (it == records_.end()) return; // only track failures for peers we already know
95
+ // Deliberately does NOT refresh last_seen: a peer that only ever fails must
96
+ // still age out (otherwise repeated give-ups would keep it forever "fresh").
97
+ it->second.fail_streak++;
98
+ }
99
+
100
+ void PeerBook::note_seen(const Address& address, uint64_t now) {
101
+ std::lock_guard<std::mutex> lock(mutex_);
102
+ PeerRecord& r = records_[address];
103
+ if (r.first_seen == 0) r.first_seen = now;
104
+ r.address = address;
105
+ r.last_seen = now;
106
+ }
107
+
108
+ bool PeerBook::remove(const Address& address) {
109
+ std::lock_guard<std::mutex> lock(mutex_);
110
+ return records_.erase(address) > 0;
111
+ }
112
+
113
+ std::vector<Address> PeerBook::best(size_t n, uint64_t now, uint64_t max_age_secs) const {
114
+ std::lock_guard<std::mutex> lock(mutex_);
115
+ std::vector<const PeerRecord*> recs;
116
+ recs.reserve(records_.size());
117
+ for (const auto& [key, r] : records_)
118
+ if (!is_stale(r, now, max_age_secs)) recs.push_back(&r);
119
+
120
+ std::sort(recs.begin(), recs.end(),
121
+ [](const PeerRecord* a, const PeerRecord* b) { return better(*a, *b); });
122
+
123
+ std::vector<Address> out;
124
+ for (const PeerRecord* r : recs) {
125
+ if (out.size() >= n) break;
126
+ out.push_back(r->address);
127
+ }
128
+ return out;
129
+ }
130
+
131
+ size_t PeerBook::prune(uint64_t now, uint64_t max_age_secs, size_t max_size) {
132
+ std::lock_guard<std::mutex> lock(mutex_);
133
+ const size_t before = records_.size();
134
+
135
+ if (max_age_secs > 0) {
136
+ for (auto it = records_.begin(); it != records_.end();) {
137
+ if (is_stale(it->second, now, max_age_secs)) it = records_.erase(it);
138
+ else ++it;
139
+ }
140
+ }
141
+
142
+ if (max_size > 0 && records_.size() > max_size) {
143
+ std::vector<const PeerRecord*> recs;
144
+ recs.reserve(records_.size());
145
+ for (const auto& [key, r] : records_) recs.push_back(&r);
146
+ std::sort(recs.begin(), recs.end(),
147
+ [](const PeerRecord* a, const PeerRecord* b) { return better(*a, *b); });
148
+ std::unordered_set<Address> keep;
149
+ keep.reserve(max_size);
150
+ for (size_t i = 0; i < max_size; ++i) keep.insert(recs[i]->address);
151
+ for (auto it = records_.begin(); it != records_.end();) {
152
+ if (keep.count(it->first)) ++it;
153
+ else it = records_.erase(it);
154
+ }
155
+ }
156
+
157
+ return before - records_.size();
158
+ }
159
+
160
+ std::vector<Address> PeerBook::all() const {
161
+ std::lock_guard<std::mutex> lock(mutex_);
162
+ std::vector<Address> out;
163
+ out.reserve(records_.size());
164
+ for (const auto& [key, r] : records_) out.push_back(r.address);
165
+ return out;
166
+ }
167
+
168
+ std::vector<PeerRecord> PeerBook::records() const {
169
+ std::lock_guard<std::mutex> lock(mutex_);
170
+ std::vector<PeerRecord> out;
171
+ out.reserve(records_.size());
172
+ for (const auto& [key, r] : records_) out.push_back(r);
173
+ return out;
174
+ }
175
+
176
+ size_t PeerBook::size() const {
177
+ std::lock_guard<std::mutex> lock(mutex_);
178
+ return records_.size();
179
+ }
180
+
181
+ } // namespace librats
@@ -0,0 +1,88 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file peer_book.h
5
+ * @brief Persistent address book of peers worth reconnecting to.
6
+ *
7
+ * Records every peer we have successfully connected to (and addresses we have
8
+ * learned), with light metadata: when we first/last saw it, when we last
9
+ * connected, how many times, and the current consecutive-failure streak. One
10
+ * ranked store serves BOTH needs at once — the "reconnect to recent peers"
11
+ * working set and the "remember everyone we ever met" archive. Callers query
12
+ * best() for the most promising addresses and rely on prune() to age out the
13
+ * long tail.
14
+ *
15
+ * Time is passed in (unix seconds) rather than read here, so the book stays a
16
+ * pure, testable data structure; the wall clock lives at the call site.
17
+ *
18
+ * Thread-safe. Backed by a human-readable JSON file: an array of objects, one
19
+ * per record, e.g.
20
+ * [
21
+ * {"ip":"127.0.0.1","port":4001,"id":"ab..","first_seen":1000,
22
+ * "last_seen":2000,"last_connected":2000,"connect_count":2,"fail_streak":0}
23
+ * ]
24
+ * The "id" field is omitted for peers we have only ever seen, never connected to.
25
+ */
26
+
27
+ #include "librats/util/rats_export.h"
28
+ #include "librats/core/address.h"
29
+ #include "librats/peer/peer_id.h"
30
+
31
+ #include <cstdint>
32
+ #include <mutex>
33
+ #include <string>
34
+ #include <unordered_map>
35
+ #include <vector>
36
+
37
+ namespace librats {
38
+
39
+ struct PeerRecord {
40
+ Address address;
41
+ PeerId id; ///< last known id (is_zero() if we never connected)
42
+ uint64_t first_seen = 0; ///< unix seconds first added/seen
43
+ uint64_t last_seen = 0; ///< unix seconds last seen (added / learned / connected)
44
+ uint64_t last_connected = 0; ///< unix seconds of last successful connection (0 = never)
45
+ uint32_t connect_count = 0; ///< successful connections
46
+ uint32_t fail_streak = 0; ///< consecutive failed dials since the last success
47
+ };
48
+
49
+ class RATS_API PeerBook {
50
+ public:
51
+ explicit PeerBook(std::string path) : path_(std::move(path)) {}
52
+
53
+ /// Load records from the backing file (no-op if it does not exist).
54
+ void load();
55
+ /// Persist the current records to the backing file.
56
+ void save() const;
57
+
58
+ /// A successful connection: refresh timestamps, bump connect_count, clear fails.
59
+ void note_connected(const Address& address, const PeerId& id, uint64_t now);
60
+ /// A failed dial to a known peer: bump its failure streak. Does NOT refresh
61
+ /// last_seen (so a peer that only ever fails still ages out). No-op if unknown.
62
+ void note_failure(const Address& address, uint64_t now);
63
+ /// Learned / manually added an address without (yet) connecting to it.
64
+ void note_seen(const Address& address, uint64_t now);
65
+
66
+ /// Remove an address entirely. Returns true if it was present.
67
+ bool remove(const Address& address);
68
+
69
+ /// Up to n most promising addresses (last seen within max_age_secs, or all if
70
+ /// max_age_secs == 0), best first: ever-connected before never-connected, then
71
+ /// most-recently-connected, then most connects, then fewest recent failures.
72
+ std::vector<Address> best(size_t n, uint64_t now, uint64_t max_age_secs) const;
73
+
74
+ /// Drop records last seen longer ago than max_age_secs (0 = no aging), then cap
75
+ /// to max_size (0 = no cap) keeping the best. Returns the number removed.
76
+ size_t prune(uint64_t now, uint64_t max_age_secs, size_t max_size);
77
+
78
+ std::vector<Address> all() const;
79
+ std::vector<PeerRecord> records() const;
80
+ size_t size() const;
81
+
82
+ private:
83
+ std::string path_;
84
+ mutable std::mutex mutex_;
85
+ std::unordered_map<Address, PeerRecord> records_; ///< keyed by the peer Address
86
+ };
87
+
88
+ } // namespace librats
@@ -0,0 +1,72 @@
1
+ #include "librats/peer/peer_id.h"
2
+
3
+ extern "C" {
4
+ #include "librats/crypto/sha256.h"
5
+ }
6
+
7
+ #include <cstring>
8
+
9
+ namespace librats {
10
+
11
+ namespace {
12
+ constexpr char kHexDigits[] = "0123456789abcdef";
13
+
14
+ int hex_value(char c) {
15
+ if (c >= '0' && c <= '9') return c - '0';
16
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
17
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
18
+ return -1;
19
+ }
20
+ } // namespace
21
+
22
+ PeerId PeerId::from_public_key(const uint8_t* key, size_t len) {
23
+ PeerId id;
24
+ rats_sha256_hash(id.bytes_.data(), key, len);
25
+ return id;
26
+ }
27
+
28
+ std::optional<PeerId> PeerId::from_bytes(ByteView raw) {
29
+ if (raw.size() != kSize) return std::nullopt;
30
+ PeerId id;
31
+ std::memcpy(id.bytes_.data(), raw.data(), kSize);
32
+ return id;
33
+ }
34
+
35
+ std::optional<PeerId> PeerId::from_hex(std::string_view hex) {
36
+ if (hex.size() != kSize * 2) return std::nullopt;
37
+ PeerId id;
38
+ for (size_t i = 0; i < kSize; ++i) {
39
+ const int hi = hex_value(hex[2 * i]);
40
+ const int lo = hex_value(hex[2 * i + 1]);
41
+ if (hi < 0 || lo < 0) return std::nullopt;
42
+ id.bytes_[i] = static_cast<uint8_t>((hi << 4) | lo);
43
+ }
44
+ return id;
45
+ }
46
+
47
+ std::string PeerId::to_hex() const {
48
+ std::string out(kSize * 2, '0');
49
+ for (size_t i = 0; i < kSize; ++i) {
50
+ out[2 * i] = kHexDigits[bytes_[i] >> 4];
51
+ out[2 * i + 1] = kHexDigits[bytes_[i] & 0x0F];
52
+ }
53
+ return out;
54
+ }
55
+
56
+ std::string PeerId::short_hex() const {
57
+ return to_hex().substr(0, 8);
58
+ }
59
+
60
+ bool PeerId::is_zero() const noexcept {
61
+ for (uint8_t b : bytes_) if (b != 0) return false;
62
+ return true;
63
+ }
64
+
65
+ size_t PeerId::Hash::operator()(const PeerId& id) const noexcept {
66
+ // The bytes are already a uniform hash; fold the first word into size_t.
67
+ size_t h = 0;
68
+ std::memcpy(&h, id.bytes_.data(), sizeof(h));
69
+ return h;
70
+ }
71
+
72
+ } // namespace librats
@@ -0,0 +1,62 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file peer_id.h
5
+ * @brief Self-certifying peer identity.
6
+ *
7
+ * A PeerId is the SHA-256 of a peer's static (Noise) public key. Because the
8
+ * Noise XX handshake proves possession of that key, a completed handshake also
9
+ * proves the remote's PeerId — identity is cryptographically bound to the key
10
+ * and cannot be forged (libp2p uses the same self-certifying scheme).
11
+ *
12
+ * This is a value type: cheap to copy, hashable, and ordered, so it slots
13
+ * straight into unordered_map / set as a key.
14
+ */
15
+
16
+ #include "librats/util/rats_export.h"
17
+ #include "librats/core/bytes.h"
18
+
19
+ #include <array>
20
+ #include <cstdint>
21
+ #include <optional>
22
+ #include <string>
23
+ #include <string_view>
24
+
25
+ namespace librats {
26
+
27
+ class RATS_API PeerId {
28
+ public:
29
+ static constexpr size_t kSize = 32; ///< SHA-256 digest length
30
+
31
+ PeerId() = default;
32
+
33
+ /// Derive the PeerId from a raw static public key (SHA-256 of the key).
34
+ static PeerId from_public_key(const uint8_t* key, size_t len);
35
+ static PeerId from_public_key(ByteView key) { return from_public_key(key.data(), key.size()); }
36
+
37
+ /// Wrap raw 32 id-bytes verbatim (NOT hashed). nullopt unless exactly kSize.
38
+ static std::optional<PeerId> from_bytes(ByteView raw);
39
+
40
+ /// Parse a 64-char lowercase/uppercase hex string. nullopt if malformed.
41
+ static std::optional<PeerId> from_hex(std::string_view hex);
42
+
43
+ std::string to_hex() const;
44
+ std::string short_hex() const; ///< first 8 hex chars, for logs
45
+
46
+ const std::array<uint8_t, kSize>& bytes() const noexcept { return bytes_; }
47
+ bool is_zero() const noexcept;
48
+
49
+ bool operator==(const PeerId& o) const noexcept { return bytes_ == o.bytes_; }
50
+ bool operator!=(const PeerId& o) const noexcept { return bytes_ != o.bytes_; }
51
+ bool operator<(const PeerId& o) const noexcept { return bytes_ < o.bytes_; }
52
+
53
+ /// Hash functor for use as an unordered_map/set key.
54
+ struct Hash {
55
+ size_t operator()(const PeerId& id) const noexcept;
56
+ };
57
+
58
+ private:
59
+ std::array<uint8_t, kSize> bytes_{};
60
+ };
61
+
62
+ } // namespace librats
@@ -0,0 +1,37 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file peer_info.h
5
+ * @brief Addressing/metadata for a peer — the shareable, persistable identity.
6
+ *
7
+ * PeerInfo is the "where / what" of a peer, kept separate from the live
8
+ * transport (Connection) and from the raw identity (PeerId). It is a value type:
9
+ * snapshotted into callbacks and into the PeerTable without touching live state.
10
+ */
11
+
12
+ #include "librats/core/types.h" // ConnRole
13
+ #include "librats/core/address.h"
14
+ #include "librats/peer/peer_id.h"
15
+
16
+ #include <vector>
17
+
18
+ namespace librats {
19
+
20
+ /// Transports a peer says it accepts, as a bitmask (see IdentifyMessage). Kept
21
+ /// separate from `transport`, which is the one this connection actually uses.
22
+ enum PeerTransports : uint8_t {
23
+ PeerTransportNone = 0,
24
+ PeerTransportTcp = 1 << 0,
25
+ PeerTransportUdp = 1 << 1,
26
+ };
27
+
28
+ struct PeerInfo {
29
+ PeerId id;
30
+ std::vector<Address> addresses; ///< known dialable addresses
31
+ ConnRole direction = ConnRole::Outbound;
32
+ TransportKind transport = TransportKind::Tcp; ///< wire this connection runs on
33
+ uint8_t supported_transports = PeerTransportNone; ///< what the peer advertised
34
+ std::string agent_version; ///< optional remote agent string
35
+ };
36
+
37
+ } // namespace librats
@@ -0,0 +1,137 @@
1
+ #include "librats/peer/peer_table.h"
2
+
3
+ #include <algorithm> // std::find
4
+ #include <mutex> // std::unique_lock (shared_lock comes from <shared_mutex>)
5
+
6
+ namespace librats {
7
+
8
+ PeerTable::AddOutcome PeerTable::add(const PeerInfo& info, PeerRoute route,
9
+ bool prefer_outbound) {
10
+ std::unique_lock<std::shared_mutex> lock(mutex_);
11
+ auto [it, inserted] = peers_.try_emplace(info.id, Entry{info, route});
12
+ if (inserted) {
13
+ count_.fetch_add(1, std::memory_order_relaxed);
14
+ return {AddResult::NewPeer, std::nullopt};
15
+ }
16
+
17
+ Entry& cur = it->second;
18
+ if (cur.route == route) { // same connection re-registering: refresh only
19
+ cur.info = info;
20
+ return {AddResult::Updated, std::nullopt};
21
+ }
22
+
23
+ // A different connection already serves this peer, and exactly one may survive.
24
+ // Both ends resolve the same pair, so every rule here must read something that
25
+ // looks identical from either side. Timing does not qualify — "whichever arrived
26
+ // first" gets a different answer at each end, and two ends that each keep the
27
+ // link the other just dropped are left with no link at all.
28
+ //
29
+ // opposite roles ⇒ cross-connect: keep the link started by the smaller id
30
+ // (prefer_outbound, opposite at each end by construction).
31
+ // different wires ⇒ a dial race whose attempts both got through. Both ends
32
+ // see the transports, and there is a right answer: the
33
+ // datagram link (one socket, one NAT mapping, a source
34
+ // port that can be dialed back — see dialer.h). "Is it
35
+ // UDP" ranks the pair only because there are exactly two
36
+ // wires; a third would need an explicit order, or the two
37
+ // ends could rank the same pair differently.
38
+ // same role and wire ⇒ a reconnect, not a simultaneous pair: the old link is
39
+ // stale or already dead, so the newcomer is the live one.
40
+ bool keep_new = true;
41
+ if (cur.info.direction != info.direction) {
42
+ const ConnRole survivor = prefer_outbound ? ConnRole::Outbound : ConnRole::Inbound;
43
+ keep_new = (info.direction == survivor);
44
+ } else if (cur.info.transport != info.transport) {
45
+ keep_new = (info.transport == TransportKind::Udp);
46
+ }
47
+
48
+ if (keep_new) {
49
+ const PeerRoute loser = cur.route;
50
+ cur = Entry{info, route};
51
+ return {AddResult::Replaced, loser};
52
+ }
53
+ return {AddResult::Rejected, route}; // existing wins; caller closes the new connection
54
+ }
55
+
56
+ std::vector<Address> PeerTable::add_addresses(const PeerId& id, PeerRoute route,
57
+ const std::vector<Address>& addresses) {
58
+ std::vector<Address> added;
59
+ std::unique_lock<std::shared_mutex> lock(mutex_);
60
+ auto it = peers_.find(id);
61
+ if (it == peers_.end() || it->second.route != route) return added;
62
+
63
+ std::vector<Address>& known = it->second.info.addresses;
64
+ for (const Address& addr : addresses) {
65
+ if (known.size() >= kMaxAddressesPerPeer) break;
66
+ if (std::find(known.begin(), known.end(), addr) != known.end()) continue; // dedup
67
+ known.push_back(addr);
68
+ added.push_back(addr);
69
+ }
70
+ return added;
71
+ }
72
+
73
+ std::optional<PeerTable::Destination> PeerTable::destination(const PeerId& id) const {
74
+ std::shared_lock<std::shared_mutex> lock(mutex_);
75
+ auto it = peers_.find(id);
76
+ if (it == peers_.end()) return std::nullopt;
77
+ return Destination{it->second.route, it->second.writable, it->second.owed};
78
+ }
79
+
80
+ bool PeerTable::all_writable() const {
81
+ std::shared_lock<std::shared_mutex> lock(mutex_);
82
+ for (const auto& [id, entry] : peers_)
83
+ if (!entry.writable) return false;
84
+ return true;
85
+ }
86
+
87
+ void PeerTable::set_writable(const PeerId& id, PeerRoute route, bool writable) {
88
+ std::unique_lock<std::shared_mutex> lock(mutex_);
89
+ auto it = peers_.find(id);
90
+ if (it == peers_.end() || it->second.route != route) return;
91
+ it->second.writable = writable;
92
+ }
93
+
94
+ void PeerTable::set_supported_transports(const PeerId& id, PeerRoute route, uint8_t mask) {
95
+ std::unique_lock<std::shared_mutex> lock(mutex_);
96
+ auto it = peers_.find(id);
97
+ if (it == peers_.end() || it->second.route != route) return;
98
+ it->second.info.supported_transports = mask;
99
+ }
100
+
101
+ bool PeerTable::remove(const PeerId& id, PeerRoute route) {
102
+ std::unique_lock<std::shared_mutex> lock(mutex_);
103
+ auto it = peers_.find(id);
104
+ if (it == peers_.end() || it->second.route != route) return false;
105
+ peers_.erase(it);
106
+ count_.fetch_sub(1, std::memory_order_relaxed);
107
+ return true;
108
+ }
109
+
110
+ std::optional<PeerRoute> PeerTable::route(const PeerId& id) const {
111
+ std::shared_lock<std::shared_mutex> lock(mutex_);
112
+ auto it = peers_.find(id);
113
+ if (it == peers_.end()) return std::nullopt;
114
+ return it->second.route;
115
+ }
116
+
117
+ std::optional<PeerInfo> PeerTable::info(const PeerId& id) const {
118
+ std::shared_lock<std::shared_mutex> lock(mutex_);
119
+ auto it = peers_.find(id);
120
+ if (it == peers_.end()) return std::nullopt;
121
+ return it->second.info;
122
+ }
123
+
124
+ bool PeerTable::contains(const PeerId& id) const {
125
+ std::shared_lock<std::shared_mutex> lock(mutex_);
126
+ return peers_.find(id) != peers_.end();
127
+ }
128
+
129
+ std::vector<PeerInfo> PeerTable::snapshot() const {
130
+ std::shared_lock<std::shared_mutex> lock(mutex_);
131
+ std::vector<PeerInfo> out;
132
+ out.reserve(peers_.size());
133
+ for (const auto& [id, entry] : peers_) out.push_back(entry.info);
134
+ return out;
135
+ }
136
+
137
+ } // namespace librats