librats 0.5.0 → 0.5.2
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.
- package/README.md +1 -1
- package/binding.gyp +1 -0
- package/lib/index.d.ts +2 -1
- package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
- package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
- package/native-src/CMakeLists.txt +360 -0
- package/native-src/LICENSE +21 -0
- package/native-src/src/bencode.cpp +485 -0
- package/native-src/src/bencode.h +145 -0
- package/native-src/src/bittorrent.cpp +3682 -0
- package/native-src/src/bittorrent.h +731 -0
- package/native-src/src/dht.cpp +2460 -0
- package/native-src/src/dht.h +508 -0
- package/native-src/src/encrypted_socket.cpp +817 -0
- package/native-src/src/encrypted_socket.h +239 -0
- package/native-src/src/file_transfer.cpp +1808 -0
- package/native-src/src/file_transfer.h +567 -0
- package/native-src/src/fs.cpp +639 -0
- package/native-src/src/fs.h +108 -0
- package/native-src/src/gossipsub.cpp +1137 -0
- package/native-src/src/gossipsub.h +403 -0
- package/native-src/src/ice.cpp +1386 -0
- package/native-src/src/ice.h +328 -0
- package/native-src/src/json.hpp +25526 -0
- package/native-src/src/krpc.cpp +558 -0
- package/native-src/src/krpc.h +145 -0
- package/native-src/src/librats.cpp +2735 -0
- package/native-src/src/librats.h +1732 -0
- package/native-src/src/librats_bittorrent.cpp +167 -0
- package/native-src/src/librats_c.cpp +1333 -0
- package/native-src/src/librats_c.h +239 -0
- package/native-src/src/librats_encryption.cpp +123 -0
- package/native-src/src/librats_file_transfer.cpp +226 -0
- package/native-src/src/librats_gossipsub.cpp +293 -0
- package/native-src/src/librats_ice.cpp +515 -0
- package/native-src/src/librats_logging.cpp +158 -0
- package/native-src/src/librats_mdns.cpp +171 -0
- package/native-src/src/librats_nat.cpp +571 -0
- package/native-src/src/librats_persistence.cpp +815 -0
- package/native-src/src/logger.h +412 -0
- package/native-src/src/mdns.cpp +1178 -0
- package/native-src/src/mdns.h +253 -0
- package/native-src/src/network_utils.cpp +598 -0
- package/native-src/src/network_utils.h +162 -0
- package/native-src/src/noise.cpp +981 -0
- package/native-src/src/noise.h +227 -0
- package/native-src/src/os.cpp +371 -0
- package/native-src/src/os.h +40 -0
- package/native-src/src/rats_export.h +17 -0
- package/native-src/src/sha1.cpp +163 -0
- package/native-src/src/sha1.h +42 -0
- package/native-src/src/socket.cpp +1376 -0
- package/native-src/src/socket.h +309 -0
- package/native-src/src/stun.cpp +484 -0
- package/native-src/src/stun.h +349 -0
- package/native-src/src/threadmanager.cpp +105 -0
- package/native-src/src/threadmanager.h +53 -0
- package/native-src/src/tracker.cpp +1110 -0
- package/native-src/src/tracker.h +268 -0
- package/native-src/src/version.cpp +24 -0
- package/native-src/src/version.h.in +45 -0
- package/native-src/version.rc.in +31 -0
- package/package.json +2 -8
- package/scripts/build-librats.js +59 -12
- package/scripts/prepare-package.js +133 -37
- package/src/librats_node.cpp +46 -1
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
#include "librats.h"
|
|
2
|
+
#include "ice.h"
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <random>
|
|
5
|
+
|
|
6
|
+
// Logging macros for ICE operations
|
|
7
|
+
#ifdef TESTING
|
|
8
|
+
#define LOG_ICE_DEBUG(message) LOG_DEBUG("ice", "[pointer: " << this << "] " << message)
|
|
9
|
+
#define LOG_ICE_INFO(message) LOG_INFO("ice", "[pointer: " << this << "] " << message)
|
|
10
|
+
#define LOG_ICE_WARN(message) LOG_WARN("ice", "[pointer: " << this << "] " << message)
|
|
11
|
+
#define LOG_ICE_ERROR(message) LOG_ERROR("ice", "[pointer: " << this << "] " << message)
|
|
12
|
+
#else
|
|
13
|
+
#define LOG_ICE_DEBUG(message) LOG_DEBUG("ice", message)
|
|
14
|
+
#define LOG_ICE_INFO(message) LOG_INFO("ice", message)
|
|
15
|
+
#define LOG_ICE_WARN(message) LOG_WARN("ice", message)
|
|
16
|
+
#define LOG_ICE_ERROR(message) LOG_ERROR("ice", message)
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
namespace librats {
|
|
20
|
+
|
|
21
|
+
//=============================================================================
|
|
22
|
+
// ICE Connection Attempts
|
|
23
|
+
//=============================================================================
|
|
24
|
+
|
|
25
|
+
bool RatsClient::attempt_ice_connection(const std::string& host, int port, ConnectionAttemptResult& result) {
|
|
26
|
+
LOG_ICE_DEBUG("Attempting ICE connection to " << host << ":" << port);
|
|
27
|
+
|
|
28
|
+
if (!ice_agent_ || !ice_agent_->is_running()) {
|
|
29
|
+
result.error_message = "ICE agent not available or not running";
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// For now, use direct connection and enhance with ICE coordination later
|
|
35
|
+
// Full ICE implementation would require signaling channel for candidate exchange
|
|
36
|
+
LOG_ICE_INFO("ICE coordination requires signaling channel - using enhanced direct connection");
|
|
37
|
+
|
|
38
|
+
bool success = attempt_direct_connection(host, port, result);
|
|
39
|
+
if (success) {
|
|
40
|
+
result.method = "ice_direct";
|
|
41
|
+
// In a full implementation, we would:
|
|
42
|
+
// 1. Gather local candidates
|
|
43
|
+
// 2. Exchange candidates via signaling
|
|
44
|
+
// 3. Perform connectivity checks
|
|
45
|
+
// 4. Establish connection on best candidate pair
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return success;
|
|
49
|
+
|
|
50
|
+
} catch (const std::exception& e) {
|
|
51
|
+
result.error_message = "ICE connection failed: " + std::string(e.what());
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//=============================================================================
|
|
57
|
+
// ICE Offer/Answer Processing
|
|
58
|
+
//=============================================================================
|
|
59
|
+
|
|
60
|
+
nlohmann::json RatsClient::create_ice_offer(const std::string& peer_id) {
|
|
61
|
+
LOG_ICE_INFO("Creating ICE offer for peer " << peer_id);
|
|
62
|
+
|
|
63
|
+
nlohmann::json offer;
|
|
64
|
+
offer["type"] = "ice_offer";
|
|
65
|
+
offer["peer_id"] = get_our_peer_id();
|
|
66
|
+
offer["target_peer_id"] = peer_id;
|
|
67
|
+
|
|
68
|
+
if (ice_agent_ && ice_agent_->is_running()) {
|
|
69
|
+
// Get local credentials
|
|
70
|
+
auto credentials = ice_agent_->get_local_credentials();
|
|
71
|
+
offer["ice_ufrag"] = credentials.first;
|
|
72
|
+
offer["ice_pwd"] = credentials.second;
|
|
73
|
+
|
|
74
|
+
// Get local candidates
|
|
75
|
+
auto candidates = ice_agent_->get_local_candidates();
|
|
76
|
+
nlohmann::json candidates_array = nlohmann::json::array();
|
|
77
|
+
|
|
78
|
+
for (const auto& candidate : candidates) {
|
|
79
|
+
nlohmann::json cand_json;
|
|
80
|
+
cand_json["ip"] = candidate.ip;
|
|
81
|
+
cand_json["port"] = candidate.port;
|
|
82
|
+
cand_json["type"] = static_cast<int>(candidate.type);
|
|
83
|
+
cand_json["priority"] = candidate.priority;
|
|
84
|
+
cand_json["foundation"] = candidate.foundation;
|
|
85
|
+
candidates_array.push_back(cand_json);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
offer["candidates"] = candidates_array;
|
|
89
|
+
} else {
|
|
90
|
+
LOG_ICE_WARN("ICE agent not available for ICE offer");
|
|
91
|
+
offer["error"] = "ICE agent not available";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return offer;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
bool RatsClient::connect_with_ice(const std::string& peer_id, const nlohmann::json& ice_offer) {
|
|
98
|
+
LOG_ICE_INFO("Connecting with ICE to peer " << peer_id);
|
|
99
|
+
|
|
100
|
+
if (!ice_agent_ || !ice_agent_->is_running()) {
|
|
101
|
+
LOG_ICE_ERROR("ICE agent not available for ICE connection");
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Extract ICE credentials
|
|
107
|
+
std::string remote_ufrag = ice_offer.value("ice_ufrag", "");
|
|
108
|
+
std::string remote_pwd = ice_offer.value("ice_pwd", "");
|
|
109
|
+
|
|
110
|
+
if (remote_ufrag.empty() || remote_pwd.empty()) {
|
|
111
|
+
LOG_ICE_ERROR("Invalid ICE offer - missing credentials");
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Set remote credentials
|
|
116
|
+
ice_agent_->set_remote_credentials(remote_ufrag, remote_pwd);
|
|
117
|
+
|
|
118
|
+
// Add remote candidates
|
|
119
|
+
if (ice_offer.contains("candidates")) {
|
|
120
|
+
for (const auto& cand_json : ice_offer["candidates"]) {
|
|
121
|
+
IceCandidate candidate;
|
|
122
|
+
candidate.ip = cand_json.value("ip", "");
|
|
123
|
+
candidate.port = cand_json.value("port", 0);
|
|
124
|
+
candidate.type = static_cast<IceCandidateType>(cand_json.value("type", 0));
|
|
125
|
+
candidate.priority = cand_json.value("priority", 0);
|
|
126
|
+
candidate.foundation = cand_json.value("foundation", "");
|
|
127
|
+
|
|
128
|
+
if (!candidate.ip.empty() && candidate.port > 0) {
|
|
129
|
+
ice_agent_->add_remote_candidate(candidate);
|
|
130
|
+
LOG_ICE_DEBUG("Added remote ICE candidate: " << candidate.ip << ":" << candidate.port);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Start connectivity checks
|
|
136
|
+
ice_agent_->start_connectivity_checks();
|
|
137
|
+
|
|
138
|
+
LOG_ICE_INFO("ICE connectivity checks started for peer " << peer_id);
|
|
139
|
+
return true;
|
|
140
|
+
|
|
141
|
+
} catch (const std::exception& e) {
|
|
142
|
+
LOG_ICE_ERROR("ICE connection failed: " << e.what());
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
bool RatsClient::handle_ice_answer(const std::string& peer_id, const nlohmann::json& ice_answer) {
|
|
148
|
+
LOG_ICE_INFO("Handling ICE answer from peer " << peer_id);
|
|
149
|
+
|
|
150
|
+
// ICE answer handling is similar to ICE offer processing
|
|
151
|
+
return connect_with_ice(peer_id, ice_answer);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//=============================================================================
|
|
155
|
+
// ICE Coordination and Initiation
|
|
156
|
+
//=============================================================================
|
|
157
|
+
|
|
158
|
+
void RatsClient::initiate_ice_with_peer(const std::string& peer_id, const std::string& host, int port) {
|
|
159
|
+
if (!ice_agent_ || !ice_agent_->is_running()) {
|
|
160
|
+
LOG_ICE_WARN("ICE agent not available for ICE initiation");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
LOG_ICE_INFO("Initiating ICE coordination with peer " << peer_id);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
// Gather local candidates if not already done
|
|
168
|
+
ice_agent_->gather_candidates();
|
|
169
|
+
|
|
170
|
+
// Create ICE offer
|
|
171
|
+
nlohmann::json ice_offer = create_ice_offer(peer_id);
|
|
172
|
+
|
|
173
|
+
// Send ICE offer via message system
|
|
174
|
+
nlohmann::json offer_message = create_rats_message("ice_offer", ice_offer, get_our_peer_id());
|
|
175
|
+
|
|
176
|
+
// Find peer socket to send offer
|
|
177
|
+
socket_t peer_socket = get_peer_socket_by_id(peer_id);
|
|
178
|
+
|
|
179
|
+
if (is_valid_socket(peer_socket)) {
|
|
180
|
+
if (send_json_to_peer(peer_socket, offer_message)) {
|
|
181
|
+
LOG_ICE_INFO("Sent ICE offer to peer " << peer_id);
|
|
182
|
+
} else {
|
|
183
|
+
LOG_ICE_ERROR("Failed to send ICE offer to peer " << peer_id);
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
LOG_ICE_ERROR("Cannot send ICE offer - peer socket not found for " << peer_id);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
} catch (const std::exception& e) {
|
|
190
|
+
LOG_ICE_ERROR("Failed to initiate ICE with peer: " << e.what());
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//=============================================================================
|
|
195
|
+
// ICE Message Handlers
|
|
196
|
+
//=============================================================================
|
|
197
|
+
|
|
198
|
+
void RatsClient::handle_ice_offer_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload) {
|
|
199
|
+
LOG_ICE_INFO("Received ICE offer from peer " << peer_hash_id);
|
|
200
|
+
|
|
201
|
+
if (!ice_agent_ || !ice_agent_->is_running()) {
|
|
202
|
+
LOG_ICE_WARN("ICE agent not available - cannot handle ICE offer");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
// Extract offer details
|
|
208
|
+
std::string target_peer_id = payload.value("target_peer_id", "");
|
|
209
|
+
std::string offer_peer_id = payload.value("peer_id", "");
|
|
210
|
+
|
|
211
|
+
// Verify this offer is for us
|
|
212
|
+
if (target_peer_id != get_our_peer_id()) {
|
|
213
|
+
LOG_ICE_WARN("ICE offer not intended for us (target: " << target_peer_id << ", us: " << get_our_peer_id() << ")");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Update peer with ICE information
|
|
218
|
+
update_peer_ice_info(socket, payload);
|
|
219
|
+
|
|
220
|
+
// Process the ICE offer
|
|
221
|
+
connect_with_ice(offer_peer_id, payload);
|
|
222
|
+
|
|
223
|
+
// Create and send ICE answer
|
|
224
|
+
nlohmann::json ice_answer = create_ice_offer(offer_peer_id); // Create our offer as answer
|
|
225
|
+
ice_answer["type"] = "ice_answer";
|
|
226
|
+
ice_answer["target_peer_id"] = offer_peer_id;
|
|
227
|
+
ice_answer["peer_id"] = get_our_peer_id();
|
|
228
|
+
|
|
229
|
+
nlohmann::json answer_message = create_rats_message("ice_answer", ice_answer, get_our_peer_id());
|
|
230
|
+
|
|
231
|
+
if (send_json_to_peer(socket, answer_message)) {
|
|
232
|
+
LOG_ICE_INFO("Sent ICE answer to peer " << peer_hash_id);
|
|
233
|
+
} else {
|
|
234
|
+
LOG_ICE_ERROR("Failed to send ICE answer to peer " << peer_hash_id);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
} catch (const std::exception& e) {
|
|
238
|
+
LOG_ICE_ERROR("Failed to handle ICE offer: " << e.what());
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
void RatsClient::handle_ice_answer_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload) {
|
|
243
|
+
LOG_ICE_INFO("Received ICE answer from peer " << peer_hash_id);
|
|
244
|
+
|
|
245
|
+
if (!ice_agent_ || !ice_agent_->is_running()) {
|
|
246
|
+
LOG_ICE_WARN("ICE agent not available - cannot handle ICE answer");
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
std::string answer_peer_id = payload.value("peer_id", "");
|
|
252
|
+
|
|
253
|
+
// Update peer with ICE information
|
|
254
|
+
update_peer_ice_info(socket, payload);
|
|
255
|
+
|
|
256
|
+
// Set ICE state to checking
|
|
257
|
+
{
|
|
258
|
+
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
259
|
+
auto socket_it = socket_to_peer_id_.find(socket);
|
|
260
|
+
if (socket_it != socket_to_peer_id_.end()) {
|
|
261
|
+
auto peer_it = peers_.find(socket_it->second);
|
|
262
|
+
if (peer_it != peers_.end()) {
|
|
263
|
+
peer_it->second.ice_state = IceConnectionState::CHECKING;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Process the ICE answer
|
|
269
|
+
handle_ice_answer(answer_peer_id, payload);
|
|
270
|
+
|
|
271
|
+
} catch (const std::exception& e) {
|
|
272
|
+
LOG_ICE_ERROR("Failed to handle ICE answer: " << e.what());
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
void RatsClient::handle_ice_candidate_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload) {
|
|
277
|
+
LOG_ICE_DEBUG("Received ICE candidate from peer " << peer_hash_id);
|
|
278
|
+
|
|
279
|
+
if (!ice_agent_ || !ice_agent_->is_running()) {
|
|
280
|
+
LOG_ICE_WARN("ICE agent not available - cannot handle ICE candidate");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
// Extract candidate information
|
|
286
|
+
IceCandidate candidate;
|
|
287
|
+
candidate.ip = payload.value("ip", "");
|
|
288
|
+
candidate.port = payload.value("port", 0);
|
|
289
|
+
candidate.type = static_cast<IceCandidateType>(payload.value("type", 0));
|
|
290
|
+
candidate.priority = payload.value("priority", 0);
|
|
291
|
+
candidate.foundation = payload.value("foundation", "");
|
|
292
|
+
candidate.component_id = payload.value("component_id", 1);
|
|
293
|
+
|
|
294
|
+
if (!candidate.ip.empty() && candidate.port > 0) {
|
|
295
|
+
// Add candidate to ICE agent
|
|
296
|
+
ice_agent_->add_remote_candidate(candidate);
|
|
297
|
+
|
|
298
|
+
// Update peer candidate list
|
|
299
|
+
add_candidate_to_peer(socket, candidate);
|
|
300
|
+
|
|
301
|
+
LOG_ICE_DEBUG("Added remote ICE candidate: " << candidate.ip << ":" << candidate.port
|
|
302
|
+
<< " type=" << static_cast<int>(candidate.type));
|
|
303
|
+
} else {
|
|
304
|
+
LOG_ICE_WARN("Invalid ICE candidate received from peer " << peer_hash_id);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
} catch (const std::exception& e) {
|
|
308
|
+
LOG_ICE_ERROR("Failed to handle ICE candidate: " << e.what());
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
//=============================================================================
|
|
313
|
+
// ICE Helper Functions
|
|
314
|
+
//=============================================================================
|
|
315
|
+
|
|
316
|
+
void RatsClient::update_peer_ice_info(socket_t socket, const nlohmann::json& payload) {
|
|
317
|
+
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
318
|
+
auto socket_it = socket_to_peer_id_.find(socket);
|
|
319
|
+
if (socket_it != socket_to_peer_id_.end()) {
|
|
320
|
+
auto peer_it = peers_.find(socket_it->second);
|
|
321
|
+
if (peer_it != peers_.end()) {
|
|
322
|
+
peer_it->second.ice_enabled = true;
|
|
323
|
+
peer_it->second.ice_ufrag = payload.value("ice_ufrag", "");
|
|
324
|
+
peer_it->second.ice_pwd = payload.value("ice_pwd", "");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
void RatsClient::add_candidate_to_peer(socket_t socket, const IceCandidate& candidate) {
|
|
330
|
+
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
331
|
+
auto socket_it = socket_to_peer_id_.find(socket);
|
|
332
|
+
if (socket_it != socket_to_peer_id_.end()) {
|
|
333
|
+
auto peer_it = peers_.find(socket_it->second);
|
|
334
|
+
if (peer_it != peers_.end()) {
|
|
335
|
+
peer_it->second.ice_candidates.push_back(candidate);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
//=============================================================================
|
|
341
|
+
// ICE Coordination Management
|
|
342
|
+
//=============================================================================
|
|
343
|
+
|
|
344
|
+
bool RatsClient::should_initiate_ice_coordination(const std::string& peer_id) {
|
|
345
|
+
std::lock_guard<std::mutex> ice_lock(ice_coordination_mutex_);
|
|
346
|
+
return ice_coordination_in_progress_.find(peer_id) == ice_coordination_in_progress_.end();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
void RatsClient::mark_ice_coordination_in_progress(const std::string& peer_id) {
|
|
350
|
+
std::lock_guard<std::mutex> ice_lock(ice_coordination_mutex_);
|
|
351
|
+
ice_coordination_in_progress_.insert(peer_id);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
void RatsClient::remove_ice_coordination_tracking(const std::string& peer_id) {
|
|
355
|
+
std::lock_guard<std::mutex> ice_lock(ice_coordination_mutex_);
|
|
356
|
+
ice_coordination_in_progress_.erase(peer_id);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
void RatsClient::cleanup_ice_coordination_for_peer(const std::string& peer_id) {
|
|
360
|
+
try {
|
|
361
|
+
remove_ice_coordination_tracking(peer_id);
|
|
362
|
+
} catch (...) {
|
|
363
|
+
// Ignore cleanup errors to prevent recursive exceptions
|
|
364
|
+
LOG_ICE_DEBUG("Exception during ICE coordination cleanup for peer " << peer_id);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
//=============================================================================
|
|
369
|
+
// ICE Callbacks and State Management
|
|
370
|
+
//=============================================================================
|
|
371
|
+
|
|
372
|
+
void RatsClient::setup_ice_callbacks() {
|
|
373
|
+
if (!ice_agent_) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Set ICE candidate discovery callback
|
|
378
|
+
ice_agent_->set_candidate_callback([this](const IceCandidate& candidate) {
|
|
379
|
+
LOG_ICE_INFO("ICE candidate discovered: " << candidate.ip << ":" << candidate.port
|
|
380
|
+
<< " type=" << static_cast<int>(candidate.type)
|
|
381
|
+
<< " priority=" << candidate.priority);
|
|
382
|
+
|
|
383
|
+
if (ice_candidate_callback_) {
|
|
384
|
+
ice_candidate_callback_("", candidate); // Empty peer_id for local candidates
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// Set ICE state change callback
|
|
389
|
+
ice_agent_->set_state_change_callback([this](IceConnectionState state) {
|
|
390
|
+
LOG_ICE_INFO("ICE connection state changed: " << static_cast<int>(state));
|
|
391
|
+
|
|
392
|
+
if (nat_progress_callback_) {
|
|
393
|
+
std::string status = "ICE state: " + std::to_string(static_cast<int>(state));
|
|
394
|
+
nat_progress_callback_("", status); // Empty peer_id for general ICE state
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
void RatsClient::initialize_ice_agent() {
|
|
400
|
+
if (!nat_config_.enable_ice) {
|
|
401
|
+
LOG_ICE_DEBUG("ICE is disabled in configuration");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
LOG_ICE_INFO("Initializing ICE agent");
|
|
406
|
+
|
|
407
|
+
IceConfig ice_config;
|
|
408
|
+
ice_config.stun_servers = nat_config_.stun_servers;
|
|
409
|
+
ice_config.turn_servers = nat_config_.turn_servers;
|
|
410
|
+
ice_config.turn_usernames = nat_config_.turn_usernames;
|
|
411
|
+
ice_config.turn_passwords = nat_config_.turn_passwords;
|
|
412
|
+
ice_config.stun_timeout_ms = 5000;
|
|
413
|
+
ice_config.turn_timeout_ms = nat_config_.turn_allocation_timeout_ms;
|
|
414
|
+
ice_config.connectivity_check_timeout_ms = nat_config_.ice_connectivity_timeout_ms;
|
|
415
|
+
ice_config.enable_host_candidates = true;
|
|
416
|
+
ice_config.enable_server_reflexive_candidates = true;
|
|
417
|
+
ice_config.enable_relay_candidates = nat_config_.enable_turn_relay;
|
|
418
|
+
|
|
419
|
+
ice_agent_ = std::make_unique<IceAgent>(IceRole::CONTROLLING, ice_config);
|
|
420
|
+
|
|
421
|
+
// Setup callbacks
|
|
422
|
+
setup_ice_callbacks();
|
|
423
|
+
|
|
424
|
+
LOG_ICE_INFO("ICE agent initialized successfully");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
//=============================================================================
|
|
428
|
+
// ICE Statistics and Information
|
|
429
|
+
//=============================================================================
|
|
430
|
+
|
|
431
|
+
nlohmann::json RatsClient::get_ice_statistics() const {
|
|
432
|
+
nlohmann::json stats;
|
|
433
|
+
|
|
434
|
+
if (ice_agent_) {
|
|
435
|
+
stats["available"] = true;
|
|
436
|
+
stats["running"] = ice_agent_->is_running();
|
|
437
|
+
stats["state"] = static_cast<int>(ice_agent_->get_connection_state());
|
|
438
|
+
|
|
439
|
+
if (ice_agent_->is_running()) {
|
|
440
|
+
auto local_candidates = ice_agent_->get_local_candidates();
|
|
441
|
+
stats["local_candidates"] = local_candidates.size();
|
|
442
|
+
|
|
443
|
+
// Get candidate details
|
|
444
|
+
nlohmann::json candidates_array = nlohmann::json::array();
|
|
445
|
+
for (const auto& candidate : local_candidates) {
|
|
446
|
+
nlohmann::json cand_json;
|
|
447
|
+
cand_json["ip"] = candidate.ip;
|
|
448
|
+
cand_json["port"] = candidate.port;
|
|
449
|
+
cand_json["type"] = static_cast<int>(candidate.type);
|
|
450
|
+
cand_json["priority"] = candidate.priority;
|
|
451
|
+
candidates_array.push_back(cand_json);
|
|
452
|
+
}
|
|
453
|
+
stats["local_candidates_details"] = candidates_array;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Get peer ICE information
|
|
457
|
+
nlohmann::json ice_peers = nlohmann::json::array();
|
|
458
|
+
{
|
|
459
|
+
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
460
|
+
for (const auto& pair : peers_) {
|
|
461
|
+
const RatsPeer& peer = pair.second;
|
|
462
|
+
if (peer.ice_enabled) {
|
|
463
|
+
nlohmann::json peer_ice;
|
|
464
|
+
peer_ice["peer_id"] = peer.peer_id;
|
|
465
|
+
peer_ice["ice_state"] = static_cast<int>(peer.ice_state);
|
|
466
|
+
peer_ice["ufrag"] = peer.ice_ufrag;
|
|
467
|
+
peer_ice["candidates_count"] = peer.ice_candidates.size();
|
|
468
|
+
ice_peers.push_back(peer_ice);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
stats["ice_peers"] = ice_peers;
|
|
473
|
+
|
|
474
|
+
} else {
|
|
475
|
+
stats["available"] = false;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return stats;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
bool RatsClient::is_ice_enabled() const {
|
|
482
|
+
return ice_agent_ && ice_agent_->is_running();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
bool RatsClient::is_peer_ice_connected(const std::string& peer_id) const {
|
|
486
|
+
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
487
|
+
auto it = peers_.find(peer_id);
|
|
488
|
+
if (it != peers_.end()) {
|
|
489
|
+
return it->second.is_ice_connected();
|
|
490
|
+
}
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
//=============================================================================
|
|
495
|
+
// ICE Cleanup and Shutdown
|
|
496
|
+
//=============================================================================
|
|
497
|
+
|
|
498
|
+
void RatsClient::cleanup_ice_resources() {
|
|
499
|
+
LOG_ICE_DEBUG("Cleaning up ICE resources");
|
|
500
|
+
|
|
501
|
+
// Stop ICE agent
|
|
502
|
+
if (ice_agent_ && ice_agent_->is_running()) {
|
|
503
|
+
ice_agent_->stop();
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Clear ICE coordination tracking
|
|
507
|
+
{
|
|
508
|
+
std::lock_guard<std::mutex> ice_lock(ice_coordination_mutex_);
|
|
509
|
+
ice_coordination_in_progress_.clear();
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
LOG_ICE_DEBUG("ICE resources cleaned up");
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
} // namespace librats
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#include "librats.h"
|
|
2
|
+
#include "logger.h"
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
|
|
5
|
+
// Logging macros for RatsClient
|
|
6
|
+
#define LOG_CLIENT_DEBUG(message) LOG_DEBUG("client", message)
|
|
7
|
+
#define LOG_CLIENT_INFO(message) LOG_INFO("client", message)
|
|
8
|
+
#define LOG_CLIENT_WARN(message) LOG_WARN("client", message)
|
|
9
|
+
#define LOG_CLIENT_ERROR(message) LOG_ERROR("client", message)
|
|
10
|
+
|
|
11
|
+
namespace librats {
|
|
12
|
+
|
|
13
|
+
//=============================================================================
|
|
14
|
+
// Logging Control API Implementation
|
|
15
|
+
//=============================================================================
|
|
16
|
+
|
|
17
|
+
void RatsClient::set_logging_enabled(bool enabled) {
|
|
18
|
+
LOG_CLIENT_INFO("Setting file logging " << (enabled ? "enabled" : "disabled"));
|
|
19
|
+
|
|
20
|
+
Logger& logger = Logger::getInstance();
|
|
21
|
+
|
|
22
|
+
if (enabled) {
|
|
23
|
+
// Set default log file path if not already set
|
|
24
|
+
if (logger.get_log_file_path().empty()) {
|
|
25
|
+
logger.set_log_file_path("rats.log");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
logger.set_file_logging_enabled(enabled);
|
|
30
|
+
|
|
31
|
+
if (enabled) {
|
|
32
|
+
LOG_CLIENT_INFO("File logging enabled - logs will be written to: " << logger.get_log_file_path());
|
|
33
|
+
} else {
|
|
34
|
+
LOG_CLIENT_INFO("File logging disabled");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
bool RatsClient::is_logging_enabled() const {
|
|
39
|
+
return Logger::getInstance().is_file_logging_enabled();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void RatsClient::set_log_file_path(const std::string& file_path) {
|
|
43
|
+
Logger& logger = Logger::getInstance();
|
|
44
|
+
logger.set_log_file_path(file_path);
|
|
45
|
+
LOG_CLIENT_INFO("Log file path set to: " << file_path);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
std::string RatsClient::get_log_file_path() const {
|
|
49
|
+
return Logger::getInstance().get_log_file_path();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void RatsClient::set_log_level(LogLevel level) {
|
|
53
|
+
Logger::getInstance().set_log_level(level);
|
|
54
|
+
LOG_CLIENT_INFO("Log level set to: " << static_cast<int>(level));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
void RatsClient::set_log_level(const std::string& level_str) {
|
|
58
|
+
LogLevel level;
|
|
59
|
+
|
|
60
|
+
std::string upper_level = level_str;
|
|
61
|
+
std::transform(upper_level.begin(), upper_level.end(), upper_level.begin(), ::toupper);
|
|
62
|
+
|
|
63
|
+
if (upper_level == "DEBUG") {
|
|
64
|
+
level = LogLevel::DEBUG;
|
|
65
|
+
} else if (upper_level == "INFO") {
|
|
66
|
+
level = LogLevel::INFO;
|
|
67
|
+
} else if (upper_level == "WARN" || upper_level == "WARNING") {
|
|
68
|
+
level = LogLevel::WARN;
|
|
69
|
+
} else if (upper_level == "ERROR") {
|
|
70
|
+
level = LogLevel::ERROR;
|
|
71
|
+
} else {
|
|
72
|
+
LOG_CLIENT_WARN("Invalid log level string: " << level_str << " - using INFO as default");
|
|
73
|
+
level = LogLevel::INFO;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
set_log_level(level);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
LogLevel RatsClient::get_log_level() const {
|
|
80
|
+
// Note: Logger doesn't expose get_log_level method, so we'll use a workaround
|
|
81
|
+
// by checking which level actually outputs. This is a limitation of the current Logger design.
|
|
82
|
+
Logger& logger = Logger::getInstance();
|
|
83
|
+
|
|
84
|
+
// Try to determine current level by checking if debug messages would be shown
|
|
85
|
+
// This is a simple approximation - for a more accurate implementation,
|
|
86
|
+
// the Logger class would need a get_log_level() method
|
|
87
|
+
|
|
88
|
+
// For now, we'll return INFO as a reasonable default
|
|
89
|
+
// In a production system, the Logger class should be enhanced to track and return the current level
|
|
90
|
+
return LogLevel::INFO;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
void RatsClient::set_log_colors_enabled(bool enabled) {
|
|
94
|
+
Logger::getInstance().set_colors_enabled(enabled);
|
|
95
|
+
LOG_CLIENT_INFO("Log colors " << (enabled ? "enabled" : "disabled"));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
bool RatsClient::is_log_colors_enabled() const {
|
|
99
|
+
// Note: Logger doesn't expose a getter for colors_enabled
|
|
100
|
+
// This is another limitation of the current Logger design
|
|
101
|
+
// For now, return true as a reasonable default
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
void RatsClient::set_log_timestamps_enabled(bool enabled) {
|
|
106
|
+
Logger::getInstance().set_timestamps_enabled(enabled);
|
|
107
|
+
LOG_CLIENT_INFO("Log timestamps " << (enabled ? "enabled" : "disabled"));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
bool RatsClient::is_log_timestamps_enabled() const {
|
|
111
|
+
// Note: Logger doesn't expose a getter for timestamps_enabled
|
|
112
|
+
// This is another limitation of the current Logger design
|
|
113
|
+
// For now, return true as a reasonable default
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
void RatsClient::set_log_rotation_size(size_t max_size_bytes) {
|
|
118
|
+
Logger::getInstance().set_log_rotation_size(max_size_bytes);
|
|
119
|
+
LOG_CLIENT_INFO("Log rotation size set to: " << max_size_bytes << " bytes");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
void RatsClient::set_log_retention_count(int count) {
|
|
123
|
+
Logger::getInstance().set_log_retention_count(count);
|
|
124
|
+
LOG_CLIENT_INFO("Log retention count set to: " << count << " files");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
void RatsClient::clear_log_file() {
|
|
128
|
+
Logger& logger = Logger::getInstance();
|
|
129
|
+
std::string log_path = logger.get_log_file_path();
|
|
130
|
+
|
|
131
|
+
if (log_path.empty()) {
|
|
132
|
+
LOG_CLIENT_WARN("No log file path set - cannot clear log file");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
// Temporarily disable file logging
|
|
138
|
+
bool was_enabled = logger.is_file_logging_enabled();
|
|
139
|
+
if (was_enabled) {
|
|
140
|
+
logger.set_file_logging_enabled(false);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Delete the log file
|
|
144
|
+
std::remove(log_path.c_str());
|
|
145
|
+
|
|
146
|
+
// Re-enable file logging if it was enabled
|
|
147
|
+
if (was_enabled) {
|
|
148
|
+
logger.set_file_logging_enabled(true);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
LOG_CLIENT_INFO("Log file cleared: " << log_path);
|
|
152
|
+
|
|
153
|
+
} catch (const std::exception& e) {
|
|
154
|
+
LOG_CLIENT_ERROR("Failed to clear log file: " << e.what());
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
}
|