librats 0.5.0 → 0.5.1

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 (63) hide show
  1. package/binding.gyp +1 -0
  2. package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
  3. package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
  4. package/native-src/CMakeLists.txt +360 -0
  5. package/native-src/LICENSE +21 -0
  6. package/native-src/src/bencode.cpp +485 -0
  7. package/native-src/src/bencode.h +145 -0
  8. package/native-src/src/bittorrent.cpp +3682 -0
  9. package/native-src/src/bittorrent.h +731 -0
  10. package/native-src/src/dht.cpp +2342 -0
  11. package/native-src/src/dht.h +501 -0
  12. package/native-src/src/encrypted_socket.cpp +817 -0
  13. package/native-src/src/encrypted_socket.h +239 -0
  14. package/native-src/src/file_transfer.cpp +1808 -0
  15. package/native-src/src/file_transfer.h +567 -0
  16. package/native-src/src/fs.cpp +639 -0
  17. package/native-src/src/fs.h +108 -0
  18. package/native-src/src/gossipsub.cpp +1137 -0
  19. package/native-src/src/gossipsub.h +403 -0
  20. package/native-src/src/ice.cpp +1386 -0
  21. package/native-src/src/ice.h +328 -0
  22. package/native-src/src/json.hpp +25526 -0
  23. package/native-src/src/krpc.cpp +558 -0
  24. package/native-src/src/krpc.h +145 -0
  25. package/native-src/src/librats.cpp +2715 -0
  26. package/native-src/src/librats.h +1729 -0
  27. package/native-src/src/librats_bittorrent.cpp +167 -0
  28. package/native-src/src/librats_c.cpp +1317 -0
  29. package/native-src/src/librats_c.h +237 -0
  30. package/native-src/src/librats_encryption.cpp +123 -0
  31. package/native-src/src/librats_file_transfer.cpp +226 -0
  32. package/native-src/src/librats_gossipsub.cpp +293 -0
  33. package/native-src/src/librats_ice.cpp +515 -0
  34. package/native-src/src/librats_logging.cpp +158 -0
  35. package/native-src/src/librats_mdns.cpp +171 -0
  36. package/native-src/src/librats_nat.cpp +571 -0
  37. package/native-src/src/librats_persistence.cpp +815 -0
  38. package/native-src/src/logger.h +412 -0
  39. package/native-src/src/mdns.cpp +1178 -0
  40. package/native-src/src/mdns.h +253 -0
  41. package/native-src/src/network_utils.cpp +598 -0
  42. package/native-src/src/network_utils.h +162 -0
  43. package/native-src/src/noise.cpp +981 -0
  44. package/native-src/src/noise.h +227 -0
  45. package/native-src/src/os.cpp +371 -0
  46. package/native-src/src/os.h +40 -0
  47. package/native-src/src/rats_export.h +17 -0
  48. package/native-src/src/sha1.cpp +163 -0
  49. package/native-src/src/sha1.h +42 -0
  50. package/native-src/src/socket.cpp +1376 -0
  51. package/native-src/src/socket.h +309 -0
  52. package/native-src/src/stun.cpp +484 -0
  53. package/native-src/src/stun.h +349 -0
  54. package/native-src/src/threadmanager.cpp +105 -0
  55. package/native-src/src/threadmanager.h +53 -0
  56. package/native-src/src/tracker.cpp +1110 -0
  57. package/native-src/src/tracker.h +268 -0
  58. package/native-src/src/version.cpp +24 -0
  59. package/native-src/src/version.h.in +45 -0
  60. package/native-src/version.rc.in +31 -0
  61. package/package.json +2 -8
  62. package/scripts/build-librats.js +59 -12
  63. package/scripts/prepare-package.js +133 -37
@@ -0,0 +1,1729 @@
1
+ #pragma once
2
+
3
+ #include "socket.h"
4
+ #include "dht.h"
5
+ #include "stun.h"
6
+ #include "mdns.h"
7
+ #include "ice.h"
8
+ #include "logger.h"
9
+ #include "encrypted_socket.h"
10
+ #include "threadmanager.h"
11
+ #include "gossipsub.h" // For ValidationResult enum and GossipSub types
12
+ #include "file_transfer.h" // File transfer functionality
13
+ #ifdef RATS_SEARCH_FEATURES
14
+ #include "bittorrent.h" // BitTorrent functionality (optional, requires RATS_SEARCH_FEATURES)
15
+ #endif
16
+ #include "json.hpp" // nlohmann::json
17
+ #include <string>
18
+ #include <functional>
19
+ #include <thread>
20
+ #include <vector>
21
+ #include <mutex>
22
+ #include <atomic>
23
+ #include <unordered_map>
24
+ #include <memory>
25
+ #include <chrono>
26
+ #include <condition_variable>
27
+ #include <unordered_set> // Added for unordered_set
28
+ #include <cstdint>
29
+ #include <cstring>
30
+ #include "rats_export.h"
31
+
32
+ namespace librats {
33
+
34
+ // Forward declarations
35
+ class IceAgent;
36
+
37
+ /**
38
+ * RatsPeer struct - comprehensive information about a connected rats peer
39
+ */
40
+ struct RatsPeer {
41
+ std::string peer_id; // Unique hash ID for the peer
42
+ std::string ip; // IP address
43
+ uint16_t port; // Port number
44
+ socket_t socket; // Socket handle
45
+ std::string normalized_address; // Normalized address for duplicate detection (ip:port)
46
+ std::chrono::steady_clock::time_point connected_at; // Connection timestamp
47
+ bool is_outgoing; // True if we initiated the connection, false if incoming
48
+
49
+ // Handshake-related fields
50
+ enum class HandshakeState {
51
+ PENDING, // Handshake not started
52
+ SENT, // Handshake sent, waiting for response
53
+ COMPLETED, // Handshake completed successfully
54
+ FAILED // Handshake failed
55
+ };
56
+
57
+ HandshakeState handshake_state; // Current handshake state
58
+ std::string version; // Protocol version of remote peer
59
+ int peer_count; // Number of peers connected to remote peer
60
+ std::chrono::steady_clock::time_point handshake_start_time; // When handshake started
61
+
62
+ // Encryption-related fields
63
+ bool encryption_enabled; // Whether encryption is enabled for this peer
64
+ bool noise_handshake_completed; // Whether noise handshake is completed
65
+ NoiseKey remote_static_key; // Remote peer's static public key (after handshake)
66
+
67
+ // NAT traversal fields
68
+ bool ice_enabled; // Whether ICE is enabled for this peer
69
+ std::string ice_ufrag; // ICE username fragment
70
+ std::string ice_pwd; // ICE password
71
+ std::vector<IceCandidate> ice_candidates; // ICE candidates for this peer
72
+ IceConnectionState ice_state; // Current ICE connection state
73
+ NatType detected_nat_type; // Detected NAT type for this peer
74
+ std::string connection_method; // How connection was established (direct, stun, turn, ice)
75
+
76
+ // Connection quality metrics
77
+ uint32_t rtt_ms; // Round-trip time in milliseconds
78
+ uint32_t packet_loss_percent; // Packet loss percentage
79
+ std::string transport_protocol; // UDP, TCP, etc.
80
+
81
+ RatsPeer() : handshake_state(HandshakeState::PENDING),
82
+ peer_count(0), encryption_enabled(false), noise_handshake_completed(false),
83
+ ice_enabled(false), ice_state(IceConnectionState::NEW),
84
+ detected_nat_type(NatType::UNKNOWN), rtt_ms(0), packet_loss_percent(0),
85
+ transport_protocol("UDP") {
86
+ connected_at = std::chrono::steady_clock::now();
87
+ handshake_start_time = connected_at;
88
+ }
89
+
90
+ RatsPeer(const std::string& id, const std::string& peer_ip, uint16_t peer_port,
91
+ socket_t sock, const std::string& norm_addr, bool outgoing)
92
+ : peer_id(id), ip(peer_ip), port(peer_port), socket(sock),
93
+ normalized_address(norm_addr), is_outgoing(outgoing),
94
+ handshake_state(HandshakeState::PENDING), peer_count(0),
95
+ encryption_enabled(false), noise_handshake_completed(false),
96
+ ice_enabled(false), ice_state(IceConnectionState::NEW),
97
+ detected_nat_type(NatType::UNKNOWN), rtt_ms(0), packet_loss_percent(0),
98
+ transport_protocol("UDP") {
99
+ connected_at = std::chrono::steady_clock::now();
100
+ handshake_start_time = connected_at;
101
+ }
102
+
103
+ // Helper methods
104
+ bool is_handshake_completed() const { return handshake_state == HandshakeState::COMPLETED; }
105
+ bool is_handshake_failed() const { return handshake_state == HandshakeState::FAILED; }
106
+ bool is_ice_connected() const {
107
+ return ice_state == IceConnectionState::CONNECTED ||
108
+ ice_state == IceConnectionState::COMPLETED;
109
+ }
110
+ bool is_fully_connected() const {
111
+ return is_handshake_completed() && (!ice_enabled || is_ice_connected());
112
+ }
113
+ };
114
+
115
+ // NAT Traversal Configuration
116
+ struct NatTraversalConfig {
117
+ bool enable_ice; // Enable ICE for NAT traversal
118
+ bool enable_upnp; // Enable UPnP for port mapping
119
+ bool enable_hole_punching; // Enable UDP/TCP hole punching
120
+ bool enable_turn_relay; // Enable TURN relay as last resort
121
+ bool prefer_ipv6; // Prefer IPv6 connections when available
122
+
123
+ // ICE configuration
124
+ std::vector<std::string> stun_servers;
125
+ std::vector<std::string> turn_servers;
126
+ std::vector<std::string> turn_usernames;
127
+ std::vector<std::string> turn_passwords;
128
+
129
+ // Timeouts and limits
130
+ int ice_gathering_timeout_ms;
131
+ int ice_connectivity_timeout_ms;
132
+ int hole_punch_attempts;
133
+ int turn_allocation_timeout_ms;
134
+
135
+ // Priority settings
136
+ int host_candidate_priority;
137
+ int server_reflexive_priority;
138
+ int relay_candidate_priority;
139
+
140
+ NatTraversalConfig()
141
+ : enable_ice(true), enable_upnp(false), enable_hole_punching(true),
142
+ enable_turn_relay(true), prefer_ipv6(false),
143
+ ice_gathering_timeout_ms(10000), ice_connectivity_timeout_ms(30000),
144
+ hole_punch_attempts(5), turn_allocation_timeout_ms(10000),
145
+ host_candidate_priority(65535), server_reflexive_priority(65534),
146
+ relay_candidate_priority(65533) {
147
+
148
+ // Default STUN servers
149
+ stun_servers.push_back("stun.l.google.com:19302");
150
+ stun_servers.push_back("stun1.l.google.com:19302");
151
+ stun_servers.push_back("stun.stunprotocol.org:3478");
152
+ }
153
+ };
154
+
155
+ // Connection establishment strategies
156
+ enum class ConnectionStrategy {
157
+ DIRECT_ONLY, // Try direct connection only
158
+ STUN_ASSISTED, // Use STUN for public IP discovery
159
+ ICE_FULL, // Full ICE with candidate gathering
160
+ TURN_RELAY, // Force TURN relay usage
161
+ AUTO_ADAPTIVE // Automatically choose best strategy
162
+ };
163
+
164
+ // Connection attempt result
165
+ struct ConnectionAttemptResult {
166
+ bool success;
167
+ std::string method; // "direct", "stun", "ice", "turn", "hole_punch"
168
+ std::chrono::milliseconds duration;
169
+ std::string error_message;
170
+ NatType local_nat_type;
171
+ NatType remote_nat_type;
172
+ std::vector<IceCandidate> used_candidates;
173
+ };
174
+
175
+ // Enhanced connection callbacks
176
+ using AdvancedConnectionCallback = std::function<void(socket_t, const std::string&, const ConnectionAttemptResult&)>;
177
+ using NatTraversalProgressCallback = std::function<void(const std::string&, const std::string&)>; // peer_id, status
178
+ using IceCandidateDiscoveredCallback = std::function<void(const std::string&, const IceCandidate&)>; // peer_id, candidate
179
+
180
+ /**
181
+ * Message data types for librats message headers
182
+ */
183
+ enum class MessageDataType : uint8_t {
184
+ BINARY = 0x01, // Raw binary data
185
+ STRING = 0x02, // UTF-8 string data
186
+ JSON = 0x03 // JSON formatted data
187
+ };
188
+
189
+ /**
190
+ * Message header structure for librats messages
191
+ * Fixed 8-byte header format:
192
+ * [0-3]: Magic number "RATS" (4 bytes)
193
+ * [4]: Message data type (1 byte)
194
+ * [5-7]: Reserved for future use (3 bytes)
195
+ */
196
+ struct MessageHeader {
197
+ static constexpr uint32_t MAGIC_NUMBER = 0x52415453; // "RATS" in ASCII
198
+ static constexpr size_t HEADER_SIZE = 8;
199
+
200
+ uint32_t magic; // Magic number for validation
201
+ MessageDataType type; // Message data type
202
+ uint8_t reserved[3]; // Reserved bytes for future use
203
+
204
+ MessageHeader(MessageDataType data_type) : magic(MAGIC_NUMBER), type(data_type) {
205
+ reserved[0] = reserved[1] = reserved[2] = 0;
206
+ }
207
+
208
+ MessageHeader() : magic(MAGIC_NUMBER), type(MessageDataType::BINARY) {
209
+ reserved[0] = reserved[1] = reserved[2] = 0;
210
+ }
211
+
212
+ // Serialize header to bytes
213
+ std::vector<uint8_t> serialize() const {
214
+ std::vector<uint8_t> data(HEADER_SIZE);
215
+ uint32_t network_magic = htonl(magic);
216
+ memcpy(data.data(), &network_magic, 4);
217
+ data[4] = static_cast<uint8_t>(type);
218
+ data[5] = reserved[0];
219
+ data[6] = reserved[1];
220
+ data[7] = reserved[2];
221
+ return data;
222
+ }
223
+
224
+ // Deserialize header from bytes
225
+ static bool deserialize(const std::vector<uint8_t>& data, MessageHeader& header) {
226
+ if (data.size() < HEADER_SIZE) {
227
+ return false;
228
+ }
229
+
230
+ uint32_t network_magic;
231
+ memcpy(&network_magic, data.data(), 4);
232
+ header.magic = ntohl(network_magic);
233
+
234
+ if (header.magic != MAGIC_NUMBER) {
235
+ return false;
236
+ }
237
+
238
+ header.type = static_cast<MessageDataType>(data[4]);
239
+ header.reserved[0] = data[5];
240
+ header.reserved[1] = data[6];
241
+ header.reserved[2] = data[7];
242
+
243
+ return true;
244
+ }
245
+
246
+ // Validate data type
247
+ bool is_valid_type() const {
248
+ return type == MessageDataType::BINARY ||
249
+ type == MessageDataType::STRING ||
250
+ type == MessageDataType::JSON;
251
+ }
252
+ };
253
+
254
+ /**
255
+ * Enhanced RatsClient with comprehensive NAT traversal capabilities
256
+ */
257
+ class RATS_API RatsClient : public ThreadManager {
258
+ public:
259
+ // =========================================================================
260
+ // Type Definitions and Callbacks
261
+ // =========================================================================
262
+ using ConnectionCallback = std::function<void(socket_t, const std::string&)>;
263
+ using BinaryDataCallback = std::function<void(socket_t, const std::string&, const std::vector<uint8_t>&)>;
264
+ using StringDataCallback = std::function<void(socket_t, const std::string&, const std::string&)>;
265
+ using JsonDataCallback = std::function<void(socket_t, const std::string&, const nlohmann::json&)>;
266
+ using DisconnectCallback = std::function<void(socket_t, const std::string&)>;
267
+ using MessageCallback = std::function<void(const std::string&, const nlohmann::json&)>;
268
+ using SendCallback = std::function<void(bool, const std::string&)>;
269
+
270
+ // =========================================================================
271
+ // Constructor and Destructor
272
+ // =========================================================================
273
+
274
+ /**
275
+ * Constructor
276
+ * @param listen_port Port to listen on for incoming connections
277
+ * @param max_peers Maximum number of concurrent peers (default: 10)
278
+ * @param nat_config NAT traversal configuration
279
+ * @param bind_address Interface IP address to bind to (empty for all interfaces)
280
+ */
281
+ RatsClient(int listen_port, int max_peers = 10, const NatTraversalConfig& nat_config = NatTraversalConfig(), const std::string& bind_address = "");
282
+
283
+ /**
284
+ * Destructor
285
+ */
286
+ ~RatsClient();
287
+
288
+ // =========================================================================
289
+ // Core Lifecycle Management
290
+ // =========================================================================
291
+
292
+ /**
293
+ * Start the RatsClient and begin listening for connections
294
+ * @return true if successful, false otherwise
295
+ */
296
+ bool start();
297
+
298
+ /**
299
+ * Stop the RatsClient and close all connections
300
+ */
301
+ void stop();
302
+
303
+ /**
304
+ * Shutdown all background threads
305
+ */
306
+ void shutdown_all_threads();
307
+
308
+ /**
309
+ * Check if the client is currently running
310
+ * @return true if running, false otherwise
311
+ */
312
+ bool is_running() const;
313
+
314
+
315
+ // =========================================================================
316
+ // Utility Methods
317
+ // =========================================================================
318
+
319
+ int get_listen_port() const;
320
+
321
+ /**
322
+ * Get the bind address being used
323
+ * @return Bind address (empty string if binding to all interfaces)
324
+ */
325
+ std::string get_bind_address() const;
326
+
327
+ // =========================================================================
328
+ // Connection Management
329
+ // =========================================================================
330
+
331
+ /**
332
+ * Connect to a peer with automatic NAT traversal
333
+ * @param host Target host/IP address
334
+ * @param port Target port
335
+ * @param strategy Connection strategy to use
336
+ * @return true if connection initiated successfully
337
+ */
338
+ bool connect_to_peer(const std::string& host, int port,
339
+ ConnectionStrategy strategy = ConnectionStrategy::AUTO_ADAPTIVE);
340
+
341
+ /**
342
+ * Connect to a peer using ICE coordination
343
+ * @param peer_id Target peer ID
344
+ * @param ice_offer ICE offer from remote peer
345
+ * @return true if ICE connection initiated successfully
346
+ */
347
+ bool connect_with_ice(const std::string& peer_id, const nlohmann::json& ice_offer);
348
+
349
+ /**
350
+ * Create ICE offer for a peer
351
+ * @param peer_id Target peer ID
352
+ * @return ICE offer JSON that can be sent to the peer
353
+ */
354
+ nlohmann::json create_ice_offer(const std::string& peer_id);
355
+
356
+ /**
357
+ * Handle ICE answer from a peer
358
+ * @param peer_id Source peer ID
359
+ * @param ice_answer ICE answer from the peer
360
+ * @return true if successfully processed
361
+ */
362
+ bool handle_ice_answer(const std::string& peer_id, const nlohmann::json& ice_answer);
363
+
364
+ /**
365
+ * Disconnect from a specific peer
366
+ * @param socket Peer socket to disconnect
367
+ */
368
+ void disconnect_peer(socket_t socket);
369
+
370
+ /**
371
+ * Disconnect from a peer by peer_id (preferred)
372
+ * @param peer_id Peer ID to disconnect
373
+ */
374
+ void disconnect_peer_by_id(const std::string& peer_id);
375
+
376
+ // =========================================================================
377
+ // Data Transmission Methods
378
+ // =========================================================================
379
+
380
+ // Send to specific peer by socket
381
+ /**
382
+ * Send binary data to a specific peer (primary method)
383
+ * @param socket Target peer socket
384
+ * @param data Binary data to send
385
+ * @param message_type Type of message data (BINARY, STRING, JSON)
386
+ * @return true if sent successfully
387
+ */
388
+ bool send_binary_to_peer(socket_t socket, const std::vector<uint8_t>& data, MessageDataType message_type = MessageDataType::BINARY);
389
+
390
+ /**
391
+ * Send string data to a specific peer
392
+ * @param socket Target peer socket
393
+ * @param data String data to send
394
+ * @return true if sent successfully
395
+ */
396
+ bool send_string_to_peer(socket_t socket, const std::string& data);
397
+
398
+ /**
399
+ * Send JSON data to a specific peer
400
+ * @param socket Target peer socket
401
+ * @param data JSON data to send
402
+ * @return true if sent successfully
403
+ */
404
+ bool send_json_to_peer(socket_t socket, const nlohmann::json& data);
405
+
406
+ // Send to specific peer by ID
407
+ /**
408
+ * Send binary data to a peer by peer_id (preferred)
409
+ * @param peer_id Target peer ID
410
+ * @param data Binary data to send
411
+ * @param message_type Type of message data (BINARY, STRING, JSON)
412
+ * @return true if sent successfully
413
+ */
414
+ bool send_binary_to_peer_id(const std::string& peer_id, const std::vector<uint8_t>& data, MessageDataType message_type = MessageDataType::BINARY);
415
+
416
+ /**
417
+ * Send string data to a peer by peer_id (preferred)
418
+ * @param peer_id Target peer ID
419
+ * @param data String data to send
420
+ * @return true if sent successfully
421
+ */
422
+ bool send_string_to_peer_id(const std::string& peer_id, const std::string& data);
423
+
424
+ /**
425
+ * Send JSON data to a peer by peer_id (preferred)
426
+ * @param peer_id Target peer ID
427
+ * @param data JSON data to send
428
+ * @return true if sent successfully
429
+ */
430
+ bool send_json_to_peer_id(const std::string& peer_id, const nlohmann::json& data);
431
+
432
+ // Broadcast to all peers
433
+ /**
434
+ * Broadcast binary data to all connected peers (primary method)
435
+ * @param data Binary data to broadcast
436
+ * @param message_type Type of message data (BINARY, STRING, JSON)
437
+ * @return Number of peers the data was sent to
438
+ */
439
+ int broadcast_binary_to_peers(const std::vector<uint8_t>& data, MessageDataType message_type = MessageDataType::BINARY);
440
+
441
+ /**
442
+ * Broadcast string data to all connected peers
443
+ * @param data String data to broadcast
444
+ * @return Number of peers the data was sent to
445
+ */
446
+ int broadcast_string_to_peers(const std::string& data);
447
+
448
+ /**
449
+ * Broadcast JSON data to all connected peers
450
+ * @param data JSON data to broadcast
451
+ * @return Number of peers the data was sent to
452
+ */
453
+ int broadcast_json_to_peers(const nlohmann::json& data);
454
+
455
+ // =========================================================================
456
+ // Peer Information and Management
457
+ // =========================================================================
458
+
459
+ /**
460
+ * Get the number of currently connected peers
461
+ * @return Number of connected peers
462
+ */
463
+ int get_peer_count() const;
464
+
465
+
466
+ /**
467
+ * Get peer_id for a peer by socket (preferred)
468
+ * @param socket Peer socket
469
+ * @return Peer ID or empty string if not found
470
+ */
471
+ std::string get_peer_id(socket_t socket) const;
472
+
473
+ /**
474
+ * Get socket for a peer by peer_id (preferred)
475
+ * @param peer_id Peer ID
476
+ * @return Peer socket or INVALID_SOCKET_VALUE if not found
477
+ */
478
+ socket_t get_peer_socket_by_id(const std::string& peer_id) const;
479
+
480
+ /**
481
+ * Get our own peer ID
482
+ * @return Our persistent peer ID
483
+ */
484
+ std::string get_our_peer_id() const;
485
+
486
+ /**
487
+ * Get all connected peers
488
+ * @return Vector of RatsPeer objects
489
+ */
490
+ std::vector<RatsPeer> get_all_peers() const;
491
+
492
+ /**
493
+ * Get all peers that have completed handshake
494
+ * @return Vector of RatsPeer objects with completed handshake
495
+ */
496
+ std::vector<RatsPeer> get_validated_peers() const;
497
+
498
+ /**
499
+ * Get peer information by peer ID
500
+ * @param peer_id The peer ID to look up
501
+ * @return Pointer to RatsPeer object, or nullptr if not found
502
+ */
503
+ const RatsPeer* get_peer_by_id(const std::string& peer_id) const;
504
+
505
+ /**
506
+ * Get peer information by socket
507
+ * @param socket The socket handle to look up
508
+ * @return Pointer to RatsPeer object, or nullptr if not found
509
+ */
510
+ const RatsPeer* get_peer_by_socket(socket_t socket) const;
511
+
512
+ /**
513
+ * Get maximum number of peers
514
+ * @return Maximum peer count
515
+ */
516
+ int get_max_peers() const;
517
+
518
+ /**
519
+ * Set maximum number of peers
520
+ * @param max_peers New maximum peer count
521
+ */
522
+ void set_max_peers(int max_peers);
523
+
524
+ /**
525
+ * Check if peer limit has been reached
526
+ * @return true if at limit, false otherwise
527
+ */
528
+ bool is_peer_limit_reached() const;
529
+
530
+ // =========================================================================
531
+ // Callback Registration
532
+ // =========================================================================
533
+
534
+ /**
535
+ * Set connection callback (called when a new peer connects)
536
+ * @param callback Function to call on new connections
537
+ */
538
+ void set_connection_callback(ConnectionCallback callback);
539
+
540
+ /**
541
+ * Set advanced connection callback with NAT traversal info
542
+ * @param callback Function to call on new connections with detailed info
543
+ */
544
+ void set_advanced_connection_callback(AdvancedConnectionCallback callback);
545
+
546
+ /**
547
+ * Set binary data callback (called when binary data is received)
548
+ * @param callback Function to call when binary data is received
549
+ */
550
+ void set_binary_data_callback(BinaryDataCallback callback);
551
+
552
+ /**
553
+ * Set string data callback (called when string data is received)
554
+ * @param callback Function to call when string data is received
555
+ */
556
+ void set_string_data_callback(StringDataCallback callback);
557
+
558
+ /**
559
+ * Set JSON data callback (called when JSON data is received)
560
+ * @param callback Function to call when JSON data is received
561
+ */
562
+ void set_json_data_callback(JsonDataCallback callback);
563
+
564
+ /**
565
+ * Set disconnect callback (called when a peer disconnects)
566
+ * @param callback Function to call on disconnections
567
+ */
568
+ void set_disconnect_callback(DisconnectCallback callback);
569
+
570
+ /**
571
+ * Set NAT traversal progress callback
572
+ * @param callback Function to call with NAT traversal progress updates
573
+ */
574
+ void set_nat_traversal_progress_callback(NatTraversalProgressCallback callback);
575
+
576
+ /**
577
+ * Set ICE candidate discovered callback
578
+ * @param callback Function to call when ICE candidates are discovered
579
+ */
580
+ void set_ice_candidate_callback(IceCandidateDiscoveredCallback callback);
581
+
582
+ // =========================================================================
583
+ // Peer Discovery Methods
584
+ // =========================================================================
585
+
586
+ // DHT Discovery
587
+ /**
588
+ * Start DHT discovery on specified port
589
+ * @param dht_port Port for DHT communication (default: 6881)
590
+ * @return true if started successfully
591
+ */
592
+ bool start_dht_discovery(int dht_port = 6881);
593
+
594
+ /**
595
+ * Stop DHT discovery
596
+ */
597
+ void stop_dht_discovery();
598
+
599
+ /**
600
+ * Find peers by content hash using DHT
601
+ * @param content_hash Hash to search for (40-character hex string)
602
+ * @param callback Function to call with discovered peers
603
+ * @return true if search initiated successfully
604
+ */
605
+ bool find_peers_by_hash(const std::string& content_hash,
606
+ std::function<void(const std::vector<std::string>&)> callback);
607
+
608
+ /**
609
+ * Announce our presence for a content hash
610
+ * @param content_hash Hash to announce for (40-character hex string)
611
+ * @param port Port to announce (default: our listen port)
612
+ * @return true if announced successfully
613
+ */
614
+ bool announce_for_hash(const std::string& content_hash, uint16_t port = 0);
615
+
616
+ /**
617
+ * Check if DHT is currently running
618
+ * @return true if DHT is running
619
+ */
620
+ bool is_dht_running() const;
621
+
622
+ /**
623
+ * Get the size of the DHT routing table
624
+ * @return Number of nodes in routing table
625
+ */
626
+ size_t get_dht_routing_table_size() const;
627
+
628
+ // mDNS Discovery
629
+ /**
630
+ * Start mDNS service discovery and announcement
631
+ * @param service_instance_name Service instance name (optional)
632
+ * @param txt_records Additional TXT records for service announcement
633
+ * @return true if started successfully
634
+ */
635
+ bool start_mdns_discovery(const std::string& service_instance_name = "",
636
+ const std::map<std::string, std::string>& txt_records = {});
637
+
638
+ /**
639
+ * Stop mDNS discovery
640
+ */
641
+ void stop_mdns_discovery();
642
+
643
+ /**
644
+ * Check if mDNS is currently running
645
+ * @return true if mDNS is running
646
+ */
647
+ bool is_mdns_running() const;
648
+
649
+ /**
650
+ * Set mDNS service discovery callback
651
+ * @param callback Function to call when services are discovered
652
+ */
653
+ void set_mdns_callback(std::function<void(const std::string&, int, const std::string&)> callback);
654
+
655
+ /**
656
+ * Get recently discovered mDNS services
657
+ * @return Vector of discovered services
658
+ */
659
+ std::vector<MdnsService> get_mdns_services() const;
660
+
661
+ /**
662
+ * Manually query for mDNS services
663
+ * @return true if query sent successfully
664
+ */
665
+ bool query_mdns_services();
666
+
667
+ // Automatic Discovery
668
+ /**
669
+ * Start automatic peer discovery
670
+ */
671
+ void start_automatic_peer_discovery();
672
+
673
+ /**
674
+ * Stop automatic peer discovery
675
+ */
676
+ void stop_automatic_peer_discovery();
677
+
678
+ /**
679
+ * Check if automatic discovery is running
680
+ * @return true if automatic discovery is running
681
+ */
682
+ bool is_automatic_discovery_running() const;
683
+
684
+ /**
685
+ * Get the discovery hash for current protocol configuration
686
+ * @return Discovery hash based on current protocol name and version
687
+ */
688
+ std::string get_discovery_hash() const;
689
+
690
+ /**
691
+ * Get the well-known RATS peer discovery hash
692
+ * @return Standard RATS discovery hash
693
+ */
694
+ static std::string get_rats_peer_discovery_hash();
695
+
696
+ // =========================================================================
697
+ // NAT Traversal and STUN Functionality
698
+ // =========================================================================
699
+
700
+ /**
701
+ * Discover public IP address using STUN and add to ignore list
702
+ * @param stun_server STUN server hostname (default: Google STUN)
703
+ * @param stun_port STUN server port (default: 19302)
704
+ * @return true if successful, false otherwise
705
+ */
706
+ bool discover_and_ignore_public_ip(const std::string& stun_server = "stun.l.google.com", int stun_port = 19302);
707
+
708
+ /**
709
+ * Detect NAT type using STUN servers
710
+ * @return Detected NAT type
711
+ */
712
+ NatType detect_nat_type();
713
+
714
+ /**
715
+ * Get detailed NAT characteristics
716
+ * @return Detailed NAT information
717
+ */
718
+ NatTypeInfo get_nat_characteristics();
719
+
720
+ /**
721
+ * Get the discovered public IP address
722
+ * @return Public IP address string or empty if not discovered
723
+ */
724
+ std::string get_public_ip() const;
725
+
726
+ /**
727
+ * Add an IP address to the ignore list
728
+ * @param ip_address IP address to ignore
729
+ */
730
+ void add_ignored_address(const std::string& ip_address);
731
+
732
+ /**
733
+ * Perform coordinated hole punching with a peer
734
+ * @param peer_ip Peer IP address
735
+ * @param peer_port Peer port
736
+ * @param coordination_data Coordination data from peer
737
+ * @return true if successful
738
+ */
739
+ bool coordinate_hole_punching(const std::string& peer_ip, uint16_t peer_port,
740
+ const nlohmann::json& coordination_data);
741
+
742
+ /**
743
+ * Get NAT traversal statistics
744
+ * @return JSON object with NAT traversal statistics
745
+ */
746
+ nlohmann::json get_nat_traversal_statistics() const;
747
+
748
+ // =========================================================================
749
+ // Protocol Configuration
750
+ // =========================================================================
751
+
752
+ /**
753
+ * Set custom protocol name for handshakes and DHT discovery
754
+ * @param protocol_name Custom protocol name (default: "rats")
755
+ */
756
+ void set_protocol_name(const std::string& protocol_name);
757
+
758
+ /**
759
+ * Set custom protocol version for handshakes
760
+ * @param protocol_version Custom protocol version (default: "1.0")
761
+ */
762
+ void set_protocol_version(const std::string& protocol_version);
763
+
764
+ /**
765
+ * Get current protocol name
766
+ * @return Current protocol name
767
+ */
768
+ std::string get_protocol_name() const;
769
+
770
+ /**
771
+ * Get current protocol version
772
+ * @return Current protocol version
773
+ */
774
+ std::string get_protocol_version() const;
775
+
776
+ // =========================================================================
777
+ // Message Exchange API
778
+ // =========================================================================
779
+
780
+ /**
781
+ * Register a persistent message handler
782
+ * @param message_type Type of message to handle
783
+ * @param callback Function to call when message is received
784
+ */
785
+ void on(const std::string& message_type, MessageCallback callback);
786
+
787
+ /**
788
+ * Register a one-time message handler
789
+ * @param message_type Type of message to handle
790
+ * @param callback Function to call when message is received (once only)
791
+ */
792
+ void once(const std::string& message_type, MessageCallback callback);
793
+
794
+ /**
795
+ * Remove all handlers for a message type
796
+ * @param message_type Type of message to stop handling
797
+ */
798
+ void off(const std::string& message_type);
799
+
800
+ /**
801
+ * Send a message to all peers
802
+ * @param message_type Type of message
803
+ * @param data Message data
804
+ * @param callback Optional callback for send result
805
+ */
806
+ void send(const std::string& message_type, const nlohmann::json& data, SendCallback callback = nullptr);
807
+
808
+ /**
809
+ * Send a message to a specific peer
810
+ * @param peer_id Target peer ID
811
+ * @param message_type Type of message
812
+ * @param data Message data
813
+ * @param callback Optional callback for send result
814
+ */
815
+ void send(const std::string& peer_id, const std::string& message_type, const nlohmann::json& data, SendCallback callback = nullptr);
816
+
817
+ /**
818
+ * Parse a JSON message
819
+ * @param message Raw message string
820
+ * @param out_json Parsed JSON output
821
+ * @return true if parsed successfully
822
+ */
823
+ bool parse_json_message(const std::string& message, nlohmann::json& out_json);
824
+
825
+ // =========================================================================
826
+ // Encryption Functionality
827
+ // =========================================================================
828
+
829
+ /**
830
+ * Initialize encryption system
831
+ * @param enable Whether to enable encryption
832
+ * @return true if successful
833
+ */
834
+ bool initialize_encryption(bool enable);
835
+
836
+ /**
837
+ * Set encryption enabled/disabled
838
+ * @param enabled Whether encryption should be enabled
839
+ */
840
+ void set_encryption_enabled(bool enabled);
841
+
842
+ /**
843
+ * Check if encryption is enabled
844
+ * @return true if encryption is enabled
845
+ */
846
+ bool is_encryption_enabled() const;
847
+
848
+ /**
849
+ * Get the encryption key as hex string
850
+ * @return Encryption key in hex format
851
+ */
852
+ std::string get_encryption_key() const;
853
+
854
+ /**
855
+ * Set encryption key from hex string
856
+ * @param key_hex Encryption key in hex format
857
+ * @return true if key was valid and set
858
+ */
859
+ bool set_encryption_key(const std::string& key_hex);
860
+
861
+ /**
862
+ * Generate a new encryption key
863
+ * @return New encryption key in hex format
864
+ */
865
+ std::string generate_new_encryption_key();
866
+
867
+ /**
868
+ * Check if a peer connection is encrypted
869
+ * @param peer_id Peer ID to check
870
+ * @return true if peer connection is encrypted
871
+ */
872
+ bool is_peer_encrypted(const std::string& peer_id) const;
873
+
874
+ // =========================================================================
875
+ // Configuration Persistence
876
+ // =========================================================================
877
+
878
+ /**
879
+ * Load configuration from files
880
+ * @return true if successful, false otherwise
881
+ */
882
+ bool load_configuration();
883
+
884
+ /**
885
+ * Save configuration to files
886
+ * @return true if successful, false otherwise
887
+ */
888
+ bool save_configuration();
889
+
890
+ /**
891
+ * Set directory where data files will be stored
892
+ * @param directory_path Path to directory (default: current folder)
893
+ * @return true if directory is accessible, false otherwise
894
+ */
895
+ bool set_data_directory(const std::string& directory_path);
896
+
897
+ /**
898
+ * Get current data directory path
899
+ * @return Current data directory path
900
+ */
901
+ std::string get_data_directory() const;
902
+
903
+ /**
904
+ * Load saved peers and attempt to reconnect
905
+ * @return Number of connection attempts made
906
+ */
907
+ int load_and_reconnect_peers();
908
+
909
+ /**
910
+ * Load historical peers from a file
911
+ * @return true if successful, false otherwise
912
+ */
913
+ bool load_historical_peers();
914
+
915
+ /**
916
+ * Save current peers to a historical file
917
+ * @return true if successful, false otherwise
918
+ */
919
+ bool save_historical_peers();
920
+
921
+ /**
922
+ * Clear all historical peers
923
+ */
924
+ void clear_historical_peers();
925
+
926
+ /**
927
+ * Get all historical peers
928
+ * @return Vector of RatsPeer objects
929
+ */
930
+ std::vector<RatsPeer> get_historical_peers() const;
931
+
932
+ // =========================================================================
933
+ // Statistics and Information
934
+ // =========================================================================
935
+
936
+ /**
937
+ * Get connection statistics
938
+ * @return JSON object with detailed statistics
939
+ */
940
+ nlohmann::json get_connection_statistics() const;
941
+
942
+ // =========================================================================
943
+ // GossipSub Functionality
944
+ // =========================================================================
945
+
946
+ /**
947
+ * Get GossipSub instance for publish-subscribe messaging
948
+ * @return Reference to GossipSub instance
949
+ */
950
+ GossipSub& get_gossipsub();
951
+
952
+ /**
953
+ * Check if GossipSub is available
954
+ * @return true if GossipSub is initialized
955
+ */
956
+ bool is_gossipsub_available() const;
957
+
958
+ // Topic Management
959
+ /**
960
+ * Subscribe to a GossipSub topic
961
+ * @param topic Topic name to subscribe to
962
+ * @return true if subscription successful
963
+ */
964
+ bool subscribe_to_topic(const std::string& topic);
965
+
966
+ /**
967
+ * Unsubscribe from a GossipSub topic
968
+ * @param topic Topic name to unsubscribe from
969
+ * @return true if unsubscription successful
970
+ */
971
+ bool unsubscribe_from_topic(const std::string& topic);
972
+
973
+ /**
974
+ * Check if subscribed to a GossipSub topic
975
+ * @param topic Topic name to check
976
+ * @return true if subscribed
977
+ */
978
+ bool is_subscribed_to_topic(const std::string& topic) const;
979
+
980
+ /**
981
+ * Get list of subscribed GossipSub topics
982
+ * @return Vector of topic names
983
+ */
984
+ std::vector<std::string> get_subscribed_topics() const;
985
+
986
+ // Publishing
987
+ /**
988
+ * Publish a message to a GossipSub topic
989
+ * @param topic Topic to publish to
990
+ * @param message Message content
991
+ * @return true if published successfully
992
+ */
993
+ bool publish_to_topic(const std::string& topic, const std::string& message);
994
+
995
+ /**
996
+ * Publish a JSON message to a GossipSub topic
997
+ * @param topic Topic to publish to
998
+ * @param message JSON message content
999
+ * @return true if published successfully
1000
+ */
1001
+ bool publish_json_to_topic(const std::string& topic, const nlohmann::json& message);
1002
+
1003
+ // Event Handlers (Unified API)
1004
+ /**
1005
+ * Set a message handler for a GossipSub topic using unified event API pattern
1006
+ * @param topic Topic name
1007
+ * @param callback Function to call when messages are received (peer_id, topic, message_content)
1008
+ */
1009
+ void on_topic_message(const std::string& topic, std::function<void(const std::string&, const std::string&, const std::string&)> callback);
1010
+
1011
+ /**
1012
+ * Set a JSON message handler for a GossipSub topic using unified event API pattern
1013
+ * @param topic Topic name
1014
+ * @param callback Function to call when JSON messages are received (peer_id, topic, json_message)
1015
+ */
1016
+ void on_topic_json_message(const std::string& topic, std::function<void(const std::string&, const std::string&, const nlohmann::json&)> callback);
1017
+
1018
+ /**
1019
+ * Set a peer joined handler for a GossipSub topic using unified event API pattern
1020
+ * @param topic Topic name
1021
+ * @param callback Function to call when peers join the topic
1022
+ */
1023
+ void on_topic_peer_joined(const std::string& topic, std::function<void(const std::string&, const std::string&)> callback);
1024
+
1025
+ /**
1026
+ * Set a peer left handler for a GossipSub topic using unified event API pattern
1027
+ * @param topic Topic name
1028
+ * @param callback Function to call when peers leave the topic
1029
+ */
1030
+ void on_topic_peer_left(const std::string& topic, std::function<void(const std::string&, const std::string&)> callback);
1031
+
1032
+ /**
1033
+ * Set a message validator for a GossipSub topic
1034
+ * @param topic Topic name (empty for global validator)
1035
+ * @param validator Validation function returning ACCEPT, REJECT, or IGNORE_MSG
1036
+ */
1037
+ void set_topic_message_validator(const std::string& topic, std::function<ValidationResult(const std::string&, const std::string&, const std::string&)> validator);
1038
+
1039
+ /**
1040
+ * Remove all event handlers for a GossipSub topic
1041
+ * @param topic Topic name
1042
+ */
1043
+ void off_topic(const std::string& topic);
1044
+
1045
+ // Information
1046
+ /**
1047
+ * Get peers subscribed to a GossipSub topic
1048
+ * @param topic Topic name
1049
+ * @return Vector of peer IDs
1050
+ */
1051
+ std::vector<std::string> get_topic_peers(const std::string& topic) const;
1052
+
1053
+ /**
1054
+ * Get mesh peers for a GossipSub topic
1055
+ * @param topic Topic name
1056
+ * @return Vector of peer IDs in the mesh
1057
+ */
1058
+ std::vector<std::string> get_topic_mesh_peers(const std::string& topic) const;
1059
+
1060
+ /**
1061
+ * Get GossipSub statistics
1062
+ * @return JSON object with comprehensive GossipSub statistics
1063
+ */
1064
+ nlohmann::json get_gossipsub_statistics() const;
1065
+
1066
+ /**
1067
+ * Check if GossipSub is running
1068
+ * @return true if GossipSub service is active
1069
+ */
1070
+ bool is_gossipsub_running() const;
1071
+
1072
+ // =========================================================================
1073
+ // Logging Control API
1074
+ // =========================================================================
1075
+
1076
+ /**
1077
+ * Enable or disable file logging
1078
+ * When enabled, logs will be written to "rats.log" by default
1079
+ * @param enabled Whether to enable file logging
1080
+ */
1081
+ void set_logging_enabled(bool enabled);
1082
+
1083
+ /**
1084
+ * Check if file logging is currently enabled
1085
+ * @return true if file logging is enabled
1086
+ */
1087
+ bool is_logging_enabled() const;
1088
+
1089
+ /**
1090
+ * Set the log file path
1091
+ * @param file_path Path to the log file (default: "rats.log")
1092
+ */
1093
+ void set_log_file_path(const std::string& file_path);
1094
+
1095
+ /**
1096
+ * Get the current log file path
1097
+ * @return Current log file path
1098
+ */
1099
+ std::string get_log_file_path() const;
1100
+
1101
+ /**
1102
+ * Set the minimum log level
1103
+ * @param level Minimum log level (DEBUG=0, INFO=1, WARN=2, ERROR=3)
1104
+ */
1105
+ void set_log_level(LogLevel level);
1106
+
1107
+ /**
1108
+ * Set the minimum log level using string
1109
+ * @param level_str Log level as string ("DEBUG", "INFO", "WARN", "ERROR")
1110
+ */
1111
+ void set_log_level(const std::string& level_str);
1112
+
1113
+ /**
1114
+ * Get the current log level
1115
+ * @return Current minimum log level
1116
+ */
1117
+ LogLevel get_log_level() const;
1118
+
1119
+ /**
1120
+ * Enable or disable colored log output
1121
+ * @param enabled Whether to enable colored output
1122
+ */
1123
+ void set_log_colors_enabled(bool enabled);
1124
+
1125
+ /**
1126
+ * Check if colored log output is enabled
1127
+ * @return true if colors are enabled
1128
+ */
1129
+ bool is_log_colors_enabled() const;
1130
+
1131
+ /**
1132
+ * Enable or disable timestamps in log output
1133
+ * @param enabled Whether to enable timestamps
1134
+ */
1135
+ void set_log_timestamps_enabled(bool enabled);
1136
+
1137
+ /**
1138
+ * Check if timestamps are enabled in log output
1139
+ * @return true if timestamps are enabled
1140
+ */
1141
+ bool is_log_timestamps_enabled() const;
1142
+
1143
+ /**
1144
+ * Set log file rotation size
1145
+ * @param max_size_bytes Maximum size in bytes before log rotation (default: 10MB)
1146
+ */
1147
+ void set_log_rotation_size(size_t max_size_bytes);
1148
+
1149
+ /**
1150
+ * Set the number of log files to retain during rotation
1151
+ * @param count Number of old log files to keep (default: 5)
1152
+ */
1153
+ void set_log_retention_count(int count);
1154
+
1155
+ /**
1156
+ * Clear/reset the current log file
1157
+ */
1158
+ void clear_log_file();
1159
+
1160
+ // =========================================================================
1161
+ // File Transfer API
1162
+ // =========================================================================
1163
+
1164
+ /**
1165
+ * Get the file transfer manager instance
1166
+ * @return Reference to the file transfer manager
1167
+ */
1168
+ FileTransferManager& get_file_transfer_manager();
1169
+
1170
+ /**
1171
+ * Check if file transfer is available
1172
+ * @return true if file transfer manager is initialized
1173
+ */
1174
+ bool is_file_transfer_available() const;
1175
+
1176
+ // Sending and Requesting
1177
+ /**
1178
+ * Send a file to a peer
1179
+ * @param peer_id Target peer ID
1180
+ * @param file_path Local file path to send
1181
+ * @param remote_filename Optional remote filename (default: use local name)
1182
+ * @return Transfer ID if successful, empty string if failed
1183
+ */
1184
+ std::string send_file(const std::string& peer_id, const std::string& file_path,
1185
+ const std::string& remote_filename = "");
1186
+
1187
+ /**
1188
+ * Send an entire directory to a peer
1189
+ * @param peer_id Target peer ID
1190
+ * @param directory_path Local directory path to send
1191
+ * @param remote_directory_name Optional remote directory name
1192
+ * @param recursive Whether to include subdirectories (default: true)
1193
+ * @return Transfer ID if successful, empty string if failed
1194
+ */
1195
+ std::string send_directory(const std::string& peer_id, const std::string& directory_path,
1196
+ const std::string& remote_directory_name = "", bool recursive = true);
1197
+
1198
+ /**
1199
+ * Request a file from a remote peer
1200
+ * @param peer_id Target peer ID
1201
+ * @param remote_file_path Path to file on remote peer
1202
+ * @param local_path Local path where file should be saved
1203
+ * @return Transfer ID if successful, empty string if failed
1204
+ */
1205
+ std::string request_file(const std::string& peer_id, const std::string& remote_file_path,
1206
+ const std::string& local_path);
1207
+
1208
+ /**
1209
+ * Request a directory from a remote peer
1210
+ * @param peer_id Target peer ID
1211
+ * @param remote_directory_path Path to directory on remote peer
1212
+ * @param local_directory_path Local path where directory should be saved
1213
+ * @param recursive Whether to include subdirectories (default: true)
1214
+ * @return Transfer ID if successful, empty string if failed
1215
+ */
1216
+ std::string request_directory(const std::string& peer_id, const std::string& remote_directory_path,
1217
+ const std::string& local_directory_path, bool recursive = true);
1218
+
1219
+ // Accept/Reject Operations
1220
+ /**
1221
+ * Accept an incoming file transfer
1222
+ * @param transfer_id Transfer identifier from request
1223
+ * @param local_path Local path where file should be saved
1224
+ * @return true if accepted successfully
1225
+ */
1226
+ bool accept_file_transfer(const std::string& transfer_id, const std::string& local_path);
1227
+
1228
+ /**
1229
+ * Reject an incoming file transfer
1230
+ * @param transfer_id Transfer identifier from request
1231
+ * @param reason Optional reason for rejection
1232
+ * @return true if rejected successfully
1233
+ */
1234
+ bool reject_file_transfer(const std::string& transfer_id, const std::string& reason = "");
1235
+
1236
+ /**
1237
+ * Accept an incoming directory transfer
1238
+ * @param transfer_id Transfer identifier from request
1239
+ * @param local_path Local path where directory should be saved
1240
+ * @return true if accepted successfully
1241
+ */
1242
+ bool accept_directory_transfer(const std::string& transfer_id, const std::string& local_path);
1243
+
1244
+ /**
1245
+ * Reject an incoming directory transfer
1246
+ * @param transfer_id Transfer identifier from request
1247
+ * @param reason Optional reason for rejection
1248
+ * @return true if rejected successfully
1249
+ */
1250
+ bool reject_directory_transfer(const std::string& transfer_id, const std::string& reason = "");
1251
+
1252
+ // Transfer Control
1253
+ /**
1254
+ * Pause an active file transfer
1255
+ * @param transfer_id Transfer to pause
1256
+ * @return true if paused successfully
1257
+ */
1258
+ bool pause_file_transfer(const std::string& transfer_id);
1259
+
1260
+ /**
1261
+ * Resume a paused file transfer
1262
+ * @param transfer_id Transfer to resume
1263
+ * @return true if resumed successfully
1264
+ */
1265
+ bool resume_file_transfer(const std::string& transfer_id);
1266
+
1267
+ /**
1268
+ * Cancel an active or paused file transfer
1269
+ * @param transfer_id Transfer to cancel
1270
+ * @return true if cancelled successfully
1271
+ */
1272
+ bool cancel_file_transfer(const std::string& transfer_id);
1273
+
1274
+ // Information and Monitoring
1275
+ /**
1276
+ * Get file transfer progress information
1277
+ * @param transfer_id Transfer to query
1278
+ * @return Progress information or nullptr if not found
1279
+ */
1280
+ std::shared_ptr<FileTransferProgress> get_file_transfer_progress(const std::string& transfer_id) const;
1281
+
1282
+ /**
1283
+ * Get all active file transfers
1284
+ * @return Vector of transfer progress objects
1285
+ */
1286
+ std::vector<std::shared_ptr<FileTransferProgress>> get_active_file_transfers() const;
1287
+
1288
+ /**
1289
+ * Get file transfer statistics
1290
+ * @return JSON object with transfer statistics
1291
+ */
1292
+ nlohmann::json get_file_transfer_statistics() const;
1293
+
1294
+ /**
1295
+ * Set file transfer configuration
1296
+ * @param config Transfer configuration settings
1297
+ */
1298
+ void set_file_transfer_config(const FileTransferConfig& config);
1299
+
1300
+ /**
1301
+ * Get current file transfer configuration
1302
+ * @return Current configuration settings
1303
+ */
1304
+ const FileTransferConfig& get_file_transfer_config() const;
1305
+
1306
+ // Event Handlers
1307
+ /**
1308
+ * Set file transfer progress callback
1309
+ * @param callback Function to call with progress updates
1310
+ */
1311
+ void on_file_transfer_progress(FileTransferProgressCallback callback);
1312
+
1313
+ /**
1314
+ * Set file transfer completion callback
1315
+ * @param callback Function to call when transfers complete
1316
+ */
1317
+ void on_file_transfer_completed(FileTransferCompletedCallback callback);
1318
+
1319
+ /**
1320
+ * Set incoming file transfer request callback
1321
+ * @param callback Function to call when receiving transfer requests
1322
+ */
1323
+ void on_file_transfer_request(FileTransferRequestCallback callback);
1324
+
1325
+ /**
1326
+ * Set directory transfer progress callback
1327
+ * @param callback Function to call with directory transfer progress
1328
+ */
1329
+ void on_directory_transfer_progress(DirectoryTransferProgressCallback callback);
1330
+
1331
+ /**
1332
+ * Set file request callback (called when receiving file requests)
1333
+ * @param callback Function to call when receiving file requests
1334
+ */
1335
+ void on_file_request(FileRequestCallback callback);
1336
+
1337
+ /**
1338
+ * Set directory request callback (called when receiving directory requests)
1339
+ * @param callback Function to call when receiving directory requests
1340
+ */
1341
+ void on_directory_request(DirectoryRequestCallback callback);
1342
+
1343
+ #ifdef RATS_SEARCH_FEATURES
1344
+ // =========================================================================
1345
+ // BitTorrent API (requires RATS_SEARCH_FEATURES)
1346
+ // =========================================================================
1347
+
1348
+ /**
1349
+ * Enable BitTorrent functionality
1350
+ * @param listen_port Port to listen for BitTorrent connections (default: 6881)
1351
+ * @return true if BitTorrent was successfully enabled
1352
+ */
1353
+ bool enable_bittorrent(int listen_port = 6881);
1354
+
1355
+ /**
1356
+ * Disable BitTorrent functionality
1357
+ */
1358
+ void disable_bittorrent();
1359
+
1360
+ /**
1361
+ * Check if BitTorrent is enabled
1362
+ * @return true if BitTorrent is active
1363
+ */
1364
+ bool is_bittorrent_enabled() const;
1365
+
1366
+ /**
1367
+ * Add a torrent from a file
1368
+ * @param torrent_file Path to the .torrent file
1369
+ * @param download_path Directory where files will be downloaded
1370
+ * @return Shared pointer to TorrentDownload object, or nullptr on failure
1371
+ */
1372
+ std::shared_ptr<TorrentDownload> add_torrent(const std::string& torrent_file,
1373
+ const std::string& download_path);
1374
+
1375
+ /**
1376
+ * Add a torrent from TorrentInfo
1377
+ * @param torrent_info TorrentInfo object with torrent metadata
1378
+ * @param download_path Directory where files will be downloaded
1379
+ * @return Shared pointer to TorrentDownload object, or nullptr on failure
1380
+ */
1381
+ std::shared_ptr<TorrentDownload> add_torrent(const TorrentInfo& torrent_info,
1382
+ const std::string& download_path);
1383
+
1384
+ /**
1385
+ * Add a torrent by info hash (magnet link style - uses DHT to find peers)
1386
+ * @param info_hash Info hash of the torrent
1387
+ * @param download_path Directory where files will be downloaded
1388
+ * @return Shared pointer to TorrentDownload object, or nullptr on failure
1389
+ * @note Requires DHT to be running. Will discover peers via DHT.
1390
+ */
1391
+ std::shared_ptr<TorrentDownload> add_torrent_by_hash(const InfoHash& info_hash,
1392
+ const std::string& download_path);
1393
+
1394
+ /**
1395
+ * Add a torrent by info hash hex string (magnet link style - uses DHT to find peers)
1396
+ * @param info_hash_hex Info hash as 40-character hex string
1397
+ * @param download_path Directory where files will be downloaded
1398
+ * @return Shared pointer to TorrentDownload object, or nullptr on failure
1399
+ * @note Requires DHT to be running. Will discover peers via DHT.
1400
+ */
1401
+ std::shared_ptr<TorrentDownload> add_torrent_by_hash(const std::string& info_hash_hex,
1402
+ const std::string& download_path);
1403
+
1404
+ /**
1405
+ * Remove a torrent by info hash
1406
+ * @param info_hash Info hash of the torrent to remove
1407
+ * @return true if torrent was removed successfully
1408
+ */
1409
+ bool remove_torrent(const InfoHash& info_hash);
1410
+
1411
+ /**
1412
+ * Get a torrent by info hash
1413
+ * @param info_hash Info hash of the torrent
1414
+ * @return Shared pointer to TorrentDownload object, or nullptr if not found
1415
+ */
1416
+ std::shared_ptr<TorrentDownload> get_torrent(const InfoHash& info_hash);
1417
+
1418
+ /**
1419
+ * Get all active torrents
1420
+ * @return Vector of all active torrent downloads
1421
+ */
1422
+ std::vector<std::shared_ptr<TorrentDownload>> get_all_torrents();
1423
+
1424
+ /**
1425
+ * Get the number of active torrents
1426
+ * @return Number of active torrents
1427
+ */
1428
+ size_t get_active_torrents_count() const;
1429
+
1430
+ /**
1431
+ * Get BitTorrent statistics (downloaded and uploaded bytes)
1432
+ * @return Pair of (downloaded_bytes, uploaded_bytes)
1433
+ */
1434
+ std::pair<uint64_t, uint64_t> get_bittorrent_stats() const;
1435
+
1436
+ /**
1437
+ * Get torrent metadata without downloading (requires DHT to be running)
1438
+ * @param info_hash Info hash of the torrent
1439
+ * @param callback Function called when metadata is retrieved (torrent_info, success, error_message)
1440
+ * @note This only retrieves metadata via BEP 9, it does not start downloading
1441
+ */
1442
+ void get_torrent_metadata(const InfoHash& info_hash,
1443
+ std::function<void(const TorrentInfo&, bool, const std::string&)> callback);
1444
+
1445
+ /**
1446
+ * Get torrent metadata without downloading by hex string (requires DHT to be running)
1447
+ * @param info_hash_hex Info hash as 40-character hex string
1448
+ * @param callback Function called when metadata is retrieved (torrent_info, success, error_message)
1449
+ * @note This only retrieves metadata via BEP 9, it does not start downloading
1450
+ */
1451
+ void get_torrent_metadata(const std::string& info_hash_hex,
1452
+ std::function<void(const TorrentInfo&, bool, const std::string&)> callback);
1453
+ #endif // RATS_SEARCH_FEATURES
1454
+
1455
+ private:
1456
+ int listen_port_;
1457
+ std::string bind_address_;
1458
+ int max_peers_;
1459
+ socket_t server_socket_;
1460
+ std::atomic<bool> running_;
1461
+
1462
+ // NAT traversal configuration
1463
+ NatTraversalConfig nat_config_;
1464
+
1465
+ // =========================================================================
1466
+ // MUTEX LOCKING ORDER - CRITICAL FOR DEADLOCK PREVENTION
1467
+ // =========================================================================
1468
+ // When acquiring multiple mutexes, ALWAYS follow this strict order:
1469
+ //
1470
+ // 1. config_mutex_ (Configuration and peer ID)
1471
+ // 2. protocol_config_mutex_ (Protocol name and version)
1472
+ // 3. encryption_mutex_ (Encryption settings and keys)
1473
+ // 4. nat_mutex_ (NAT detection and characteristics)
1474
+ // 5. public_ip_mutex_ (Public IP address)
1475
+ // 6. local_addresses_mutex_ (Local interface addresses)
1476
+ // 7. ice_coordination_mutex_ (ICE coordination tracking)
1477
+ // 8. connection_attempts_mutex_ (Connection attempt history)
1478
+ // 9. peers_mutex_ (Peer management - most frequently locked)
1479
+ // 10. socket_send_mutexes_mutex_ (Socket send mutex management)
1480
+ // 11. message_handlers_mutex_ (Message handler registration)
1481
+ // =========================================================================
1482
+
1483
+ // Configuration persistence
1484
+ std::string our_peer_id_; // Our persistent peer ID
1485
+ std::string data_directory_; // Directory where data files are stored
1486
+ mutable std::mutex config_mutex_; // [1] Protects configuration data
1487
+ static const std::string CONFIG_FILE_NAME; // "config.json"
1488
+ static const std::string PEERS_FILE_NAME; // "peers.rats"
1489
+ static const std::string PEERS_EVER_FILE_NAME; // "peers_ever.rats"
1490
+
1491
+ // Encryption state
1492
+ NoiseKey static_encryption_key_; // Our static encryption key
1493
+ bool encryption_enabled_; // Whether encryption is enabled
1494
+ mutable std::mutex encryption_mutex_; // [3] Protects encryption state
1495
+
1496
+ // ICE and NAT traversal
1497
+ std::unique_ptr<IceAgent> ice_agent_; // ICE agent for NAT traversal
1498
+ std::unique_ptr<AdvancedNatDetector> nat_detector_; // Advanced NAT type detection
1499
+ NatType detected_nat_type_; // Our detected NAT type
1500
+ NatTypeInfo nat_characteristics_; // Detailed NAT information
1501
+ mutable std::mutex nat_mutex_; // [4] Protects NAT-related data
1502
+
1503
+ // ICE coordination tracking to prevent duplicate attempts
1504
+ std::unordered_set<std::string> ice_coordination_in_progress_; // Set of peer_ids having ICE coordination
1505
+ mutable std::mutex ice_coordination_mutex_; // [7] Protects ICE coordination state
1506
+
1507
+ // Connection attempt tracking
1508
+ std::unordered_map<std::string, std::vector<ConnectionAttemptResult>> connection_attempts_;
1509
+ mutable std::mutex connection_attempts_mutex_; // [8] Protects connection attempts
1510
+
1511
+ // Organized peer management using RatsPeer struct
1512
+ mutable std::mutex peers_mutex_; // [9] Protects peer data (most frequently locked)
1513
+ std::unordered_map<std::string, RatsPeer> peers_; // keyed by peer_id
1514
+ std::unordered_map<socket_t, std::string> socket_to_peer_id_; // for quick socket->peer_id lookup
1515
+ std::unordered_map<std::string, std::string> address_to_peer_id_; // for duplicate detection (normalized_address->peer_id)
1516
+
1517
+ // Per-socket synchronization for thread-safe message sending
1518
+ mutable std::mutex socket_send_mutexes_mutex_; // [10] Protects socket send mutex map
1519
+ std::unordered_map<socket_t, std::shared_ptr<std::mutex>> socket_send_mutexes_;
1520
+
1521
+ // Server and client management
1522
+ std::thread server_thread_;
1523
+ std::thread management_thread_;
1524
+
1525
+ ConnectionCallback connection_callback_;
1526
+ AdvancedConnectionCallback advanced_connection_callback_;
1527
+ BinaryDataCallback binary_data_callback_;
1528
+ StringDataCallback string_data_callback_;
1529
+ JsonDataCallback json_data_callback_;
1530
+ DisconnectCallback disconnect_callback_;
1531
+ NatTraversalProgressCallback nat_progress_callback_;
1532
+ IceCandidateDiscoveredCallback ice_candidate_callback_;
1533
+
1534
+ // DHT client for peer discovery
1535
+ std::unique_ptr<DhtClient> dht_client_;
1536
+
1537
+ // STUN client for public IP discovery
1538
+ std::unique_ptr<StunClient> stun_client_;
1539
+ std::string public_ip_;
1540
+ mutable std::mutex public_ip_mutex_; // [5] Protects public IP address
1541
+
1542
+ // mDNS client for local network discovery
1543
+ std::unique_ptr<MdnsClient> mdns_client_;
1544
+ std::function<void(const std::string&, int, const std::string&)> mdns_callback_;
1545
+
1546
+ // GossipSub for publish-subscribe messaging
1547
+ std::unique_ptr<GossipSub> gossipsub_;
1548
+
1549
+ // File transfer manager
1550
+ std::unique_ptr<FileTransferManager> file_transfer_manager_;
1551
+
1552
+ #ifdef RATS_SEARCH_FEATURES
1553
+ // BitTorrent client (optional, requires RATS_SEARCH_FEATURES)
1554
+ std::unique_ptr<BitTorrentClient> bittorrent_client_;
1555
+ #endif
1556
+
1557
+ void initialize_modules();
1558
+ void destroy_modules();
1559
+
1560
+ void server_loop();
1561
+ void management_loop();
1562
+ void handle_client(socket_t client_socket, const std::string& peer_hash_id);
1563
+ void remove_peer(socket_t socket);
1564
+ std::string generate_peer_hash_id(socket_t socket, const std::string& connection_info);
1565
+ void handle_dht_peer_discovery(const std::vector<Peer>& peers, const InfoHash& info_hash);
1566
+ void handle_mdns_service_discovery(const MdnsService& service, bool is_new);
1567
+
1568
+ // Message header helpers
1569
+ std::vector<uint8_t> create_message_with_header(const std::vector<uint8_t>& payload, MessageDataType type);
1570
+ bool parse_message_with_header(const std::vector<uint8_t>& message, MessageHeader& header, std::vector<uint8_t>& payload) const;
1571
+
1572
+ // Enhanced connection establishment
1573
+ bool attempt_direct_connection(const std::string& host, int port, ConnectionAttemptResult& result);
1574
+ bool attempt_stun_assisted_connection(const std::string& host, int port, ConnectionAttemptResult& result);
1575
+ bool attempt_ice_connection(const std::string& host, int port, ConnectionAttemptResult& result);
1576
+ bool attempt_turn_relay_connection(const std::string& host, int port, ConnectionAttemptResult& result);
1577
+ bool attempt_hole_punch_connection(const std::string& host, int port, ConnectionAttemptResult& result);
1578
+
1579
+ // ICE coordination helpers
1580
+ void handle_ice_candidate_discovered(const std::string& peer_id, const IceCandidate& candidate);
1581
+ void handle_ice_connection_state_change(const std::string& peer_id, IceConnectionState state);
1582
+ void initiate_ice_with_peer(const std::string& peer_id, const std::string& host, int port);
1583
+
1584
+ // NAT traversal message handlers
1585
+ void handle_ice_offer_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload);
1586
+ void handle_ice_answer_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload);
1587
+ void handle_ice_candidate_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload);
1588
+ void handle_hole_punch_coordination_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload);
1589
+ void handle_nat_info_exchange_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload);
1590
+ void send_nat_info_to_peer(socket_t socket, const std::string& peer_id);
1591
+
1592
+ // New peer management methods using RatsPeer
1593
+ void add_peer(const RatsPeer& peer);
1594
+ void add_peer_unlocked(const RatsPeer& peer); // Assumes peers_mutex_ is already locked
1595
+ void remove_peer_by_id(const std::string& peer_id);
1596
+ void remove_peer_by_id_unlocked(const std::string& peer_id); // Assumes peers_mutex_ is already locked
1597
+ bool is_already_connected_to_address(const std::string& normalized_address) const;
1598
+ std::string normalize_peer_address(const std::string& ip, int port) const;
1599
+
1600
+ // Local interface address blocking (ignore list)
1601
+ std::vector<std::string> local_interface_addresses_;
1602
+ mutable std::mutex local_addresses_mutex_; // [6] Protects local interface addresses
1603
+ void initialize_local_addresses();
1604
+ void refresh_local_addresses();
1605
+ bool is_blocked_address(const std::string& ip_address) const;
1606
+ bool should_ignore_peer(const std::string& ip, int port) const;
1607
+ static bool parse_address_string(const std::string& address_str, std::string& out_ip, int& out_port);
1608
+
1609
+ // Helper functions that assume mutex is already locked
1610
+ int get_peer_count_unlocked() const; // Helper that assumes peers_mutex_ is already locked
1611
+
1612
+ // Handshake protocol
1613
+ static constexpr const char* RATS_PROTOCOL_VERSION = "1.0";
1614
+ static constexpr int HANDSHAKE_TIMEOUT_SECONDS = 10;
1615
+
1616
+ // Custom protocol configuration
1617
+ std::string custom_protocol_name_; // Custom protocol name (default: "rats")
1618
+ std::string custom_protocol_version_; // Custom protocol version (default: "1.0")
1619
+ mutable std::mutex protocol_config_mutex_; // [2] Protects protocol configuration
1620
+
1621
+ struct HandshakeMessage {
1622
+ std::string protocol;
1623
+ std::string version;
1624
+ std::string peer_id;
1625
+ std::string message_type;
1626
+ int64_t timestamp;
1627
+ };
1628
+
1629
+ std::string create_handshake_message(const std::string& message_type, const std::string& our_peer_id) const;
1630
+ bool parse_handshake_message(const std::string& message, HandshakeMessage& out_msg) const;
1631
+ bool validate_handshake_message(const HandshakeMessage& msg) const;
1632
+ bool is_handshake_message(const std::string& message) const;
1633
+ bool send_handshake(socket_t socket, const std::string& our_peer_id);
1634
+ bool send_handshake_unlocked(socket_t socket, const std::string& our_peer_id);
1635
+ bool handle_handshake_message(socket_t socket, const std::string& peer_hash_id, const std::string& message);
1636
+ void check_handshake_timeouts();
1637
+ void log_handshake_completion(const RatsPeer& peer);
1638
+ void log_handshake_completion_unlocked(const RatsPeer& peer);
1639
+
1640
+ // Automatic discovery
1641
+ std::atomic<bool> auto_discovery_running_;
1642
+ std::thread auto_discovery_thread_;
1643
+ void automatic_discovery_loop();
1644
+ void announce_rats_peer();
1645
+ void search_rats_peers();
1646
+
1647
+ // Message handling system
1648
+ nlohmann::json create_rats_message(const std::string& type, const nlohmann::json& payload, const std::string& sender_peer_id);
1649
+ void handle_rats_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& message);
1650
+
1651
+ // Specific message handlers
1652
+ void handle_peer_exchange_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload);
1653
+ void handle_peers_request_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload);
1654
+ void handle_peers_response_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload);
1655
+
1656
+ // Message creation and broadcasting
1657
+ nlohmann::json create_peer_exchange_message(const RatsPeer& peer);
1658
+ void broadcast_peer_exchange_message(const RatsPeer& new_peer);
1659
+ nlohmann::json create_peers_request_message(const std::string& sender_peer_id);
1660
+ nlohmann::json create_peers_response_message(const std::vector<RatsPeer>& peers, const std::string& sender_peer_id);
1661
+ std::vector<RatsPeer> get_random_peers(int max_count, const std::string& exclude_peer_id = "") const;
1662
+ void send_peers_request(socket_t socket, const std::string& our_peer_id);
1663
+
1664
+ int broadcast_rats_message(const nlohmann::json& message, const std::string& exclude_peer_id = "");
1665
+ int broadcast_rats_message_to_validated_peers(const nlohmann::json& message, const std::string& exclude_peer_id = "");
1666
+ // Message exchange API implementation
1667
+ struct MessageHandler {
1668
+ MessageCallback callback;
1669
+ bool is_once;
1670
+
1671
+ MessageHandler(MessageCallback cb, bool once) : callback(cb), is_once(once) {}
1672
+ };
1673
+
1674
+ std::unordered_map<std::string, std::vector<MessageHandler>> message_handlers_;
1675
+ mutable std::mutex message_handlers_mutex_; // [11] Protects message handlers
1676
+
1677
+ void call_message_handlers(const std::string& message_type, const std::string& peer_id, const nlohmann::json& data);
1678
+ void call_callback_safely(const MessageCallback& callback, const std::string& peer_id, const nlohmann::json& data);
1679
+ void remove_once_handlers(const std::string& message_type);
1680
+
1681
+ // Per-socket synchronization helpers
1682
+ std::shared_ptr<std::mutex> get_socket_send_mutex(socket_t socket);
1683
+ void cleanup_socket_send_mutex(socket_t socket);
1684
+
1685
+ // Configuration persistence helpers
1686
+ std::string generate_persistent_peer_id() const;
1687
+ nlohmann::json serialize_peer_for_persistence(const RatsPeer& peer) const;
1688
+ bool deserialize_peer_from_persistence(const nlohmann::json& json, std::string& ip, int& port, std::string& peer_id) const;
1689
+ std::string get_config_file_path() const;
1690
+ std::string get_peers_file_path() const;
1691
+ std::string get_peers_ever_file_path() const;
1692
+ bool save_peers_to_file();
1693
+ bool append_peer_to_historical_file(const RatsPeer& peer);
1694
+ int load_and_reconnect_historical_peers();
1695
+
1696
+ // NAT traversal helpers
1697
+ void initialize_nat_traversal();
1698
+ void detect_and_cache_nat_type();
1699
+ void update_connection_statistics(const std::string& peer_id, const ConnectionAttemptResult& result);
1700
+ std::string select_best_connection_strategy(const std::string& host, int port);
1701
+ NatType map_characteristics_to_nat_type(const NatTypeInfo& characteristics);
1702
+ void log_nat_detection_results();
1703
+ bool perform_tcp_connection(const std::string& host, int port, ConnectionAttemptResult& result);
1704
+
1705
+ // ICE coordination helpers
1706
+ void initialize_ice_agent();
1707
+ void setup_ice_callbacks();
1708
+ void update_peer_ice_info(socket_t socket, const nlohmann::json& payload);
1709
+ void add_candidate_to_peer(socket_t socket, const IceCandidate& candidate);
1710
+ bool should_initiate_ice_coordination(const std::string& peer_id);
1711
+ void mark_ice_coordination_in_progress(const std::string& peer_id);
1712
+ void remove_ice_coordination_tracking(const std::string& peer_id);
1713
+ void cleanup_ice_coordination_for_peer(const std::string& peer_id);
1714
+ nlohmann::json get_ice_statistics() const;
1715
+ bool is_ice_enabled() const;
1716
+ bool is_peer_ice_connected(const std::string& peer_id) const;
1717
+ void cleanup_ice_resources();
1718
+ };
1719
+
1720
+ // Utility functions
1721
+ std::unique_ptr<RatsClient> create_rats_client(int listen_port);
1722
+
1723
+ // Library version query (stable, binding-friendly)
1724
+ RATS_API const char* rats_get_library_version_string();
1725
+ RATS_API void rats_get_library_version(int* major, int* minor, int* patch, int* build);
1726
+ RATS_API const char* rats_get_library_git_describe();
1727
+ RATS_API uint32_t rats_get_library_abi(); // packed as (major<<16)|(minor<<8)|patch
1728
+
1729
+ } // namespace librats