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,558 @@
1
+ #include "krpc.h"
2
+ #include "network_utils.h"
3
+ #include "logger.h"
4
+ #include <random>
5
+ #include <chrono>
6
+ #include <sstream>
7
+ #include <iomanip>
8
+ #include <cstring>
9
+ #include <algorithm>
10
+
11
+ #ifdef _WIN32
12
+ #include <winsock2.h>
13
+ #include <ws2tcpip.h>
14
+ #else
15
+ #include <arpa/inet.h>
16
+ #include <netinet/in.h>
17
+ #endif
18
+
19
+ // KRPC module logging macros
20
+ #define LOG_KRPC_DEBUG(message) LOG_DEBUG("krpc", message)
21
+ #define LOG_KRPC_INFO(message) LOG_INFO("krpc", message)
22
+ #define LOG_KRPC_WARN(message) LOG_WARN("krpc", message)
23
+ #define LOG_KRPC_ERROR(message) LOG_ERROR("krpc", message)
24
+
25
+ namespace librats {
26
+
27
+ std::atomic<uint32_t> KrpcProtocol::transaction_counter_ = 0;
28
+
29
+ KrpcProtocol::KrpcProtocol() {}
30
+
31
+ KrpcProtocol::~KrpcProtocol() {}
32
+
33
+ // Create query messages
34
+ KrpcMessage KrpcProtocol::create_ping_query(const std::string& transaction_id, const NodeId& sender_id) {
35
+ KrpcMessage message;
36
+ message.type = KrpcMessageType::Query;
37
+ message.transaction_id = transaction_id;
38
+ message.query_type = KrpcQueryType::Ping;
39
+ message.sender_id = sender_id;
40
+ return message;
41
+ }
42
+
43
+ KrpcMessage KrpcProtocol::create_find_node_query(const std::string& transaction_id, const NodeId& sender_id, const NodeId& target_id) {
44
+ KrpcMessage message;
45
+ message.type = KrpcMessageType::Query;
46
+ message.transaction_id = transaction_id;
47
+ message.query_type = KrpcQueryType::FindNode;
48
+ message.sender_id = sender_id;
49
+ message.target_id = target_id;
50
+ return message;
51
+ }
52
+
53
+ KrpcMessage KrpcProtocol::create_get_peers_query(const std::string& transaction_id, const NodeId& sender_id, const InfoHash& info_hash) {
54
+ KrpcMessage message;
55
+ message.type = KrpcMessageType::Query;
56
+ message.transaction_id = transaction_id;
57
+ message.query_type = KrpcQueryType::GetPeers;
58
+ message.sender_id = sender_id;
59
+ message.info_hash = info_hash;
60
+ return message;
61
+ }
62
+
63
+ KrpcMessage KrpcProtocol::create_announce_peer_query(const std::string& transaction_id, const NodeId& sender_id, const InfoHash& info_hash, uint16_t port, const std::string& token) {
64
+ KrpcMessage message;
65
+ message.type = KrpcMessageType::Query;
66
+ message.transaction_id = transaction_id;
67
+ message.query_type = KrpcQueryType::AnnouncePeer;
68
+ message.sender_id = sender_id;
69
+ message.info_hash = info_hash;
70
+ message.port = port;
71
+ message.token = token;
72
+ return message;
73
+ }
74
+
75
+ // Create response messages
76
+ KrpcMessage KrpcProtocol::create_ping_response(const std::string& transaction_id, const NodeId& response_id) {
77
+ KrpcMessage message;
78
+ message.type = KrpcMessageType::Response;
79
+ message.transaction_id = transaction_id;
80
+ message.response_id = response_id;
81
+ return message;
82
+ }
83
+
84
+ KrpcMessage KrpcProtocol::create_find_node_response(const std::string& transaction_id, const NodeId& response_id, const std::vector<KrpcNode>& nodes) {
85
+ KrpcMessage message;
86
+ message.type = KrpcMessageType::Response;
87
+ message.transaction_id = transaction_id;
88
+ message.response_id = response_id;
89
+ message.nodes = nodes;
90
+ return message;
91
+ }
92
+
93
+ KrpcMessage KrpcProtocol::create_get_peers_response(const std::string& transaction_id, const NodeId& response_id, const std::vector<Peer>& peers, const std::string& token) {
94
+ KrpcMessage message;
95
+ message.type = KrpcMessageType::Response;
96
+ message.transaction_id = transaction_id;
97
+ message.response_id = response_id;
98
+ message.peers = peers;
99
+ message.token = token;
100
+ return message;
101
+ }
102
+
103
+ KrpcMessage KrpcProtocol::create_get_peers_response_with_nodes(const std::string& transaction_id, const NodeId& response_id, const std::vector<KrpcNode>& nodes, const std::string& token) {
104
+ KrpcMessage message;
105
+ message.type = KrpcMessageType::Response;
106
+ message.transaction_id = transaction_id;
107
+ message.response_id = response_id;
108
+ message.nodes = nodes;
109
+ message.token = token;
110
+ return message;
111
+ }
112
+
113
+ KrpcMessage KrpcProtocol::create_announce_peer_response(const std::string& transaction_id, const NodeId& response_id) {
114
+ KrpcMessage message;
115
+ message.type = KrpcMessageType::Response;
116
+ message.transaction_id = transaction_id;
117
+ message.response_id = response_id;
118
+ return message;
119
+ }
120
+
121
+ // Create error message
122
+ KrpcMessage KrpcProtocol::create_error(const std::string& transaction_id, KrpcErrorCode error_code, const std::string& error_message) {
123
+ KrpcMessage message;
124
+ message.type = KrpcMessageType::Error;
125
+ message.transaction_id = transaction_id;
126
+ message.error_code = error_code;
127
+ message.error_message = error_message;
128
+ return message;
129
+ }
130
+
131
+ // Encode messages
132
+ std::vector<uint8_t> KrpcProtocol::encode_message(const KrpcMessage& message) {
133
+ BencodeValue root;
134
+
135
+ switch (message.type) {
136
+ case KrpcMessageType::Query:
137
+ root = encode_query(message);
138
+ break;
139
+ case KrpcMessageType::Response:
140
+ root = encode_response(message);
141
+ break;
142
+ case KrpcMessageType::Error:
143
+ root = encode_error(message);
144
+ break;
145
+ default:
146
+ LOG_KRPC_ERROR("Unknown message type: " << static_cast<int>(message.type));
147
+ return {};
148
+ }
149
+
150
+ return root.encode();
151
+ }
152
+
153
+ BencodeValue KrpcProtocol::encode_query(const KrpcMessage& message) {
154
+ BencodeValue root = BencodeValue::create_dict();
155
+
156
+ // Common fields
157
+ root["t"] = BencodeValue(message.transaction_id);
158
+ root["y"] = BencodeValue("q");
159
+ root["q"] = BencodeValue(query_type_to_string(message.query_type));
160
+
161
+ // Arguments
162
+ BencodeValue args = BencodeValue::create_dict();
163
+ args["id"] = BencodeValue(node_id_to_string(message.sender_id));
164
+
165
+ switch (message.query_type) {
166
+ case KrpcQueryType::Ping:
167
+ // No additional arguments for ping
168
+ break;
169
+ case KrpcQueryType::FindNode:
170
+ args["target"] = BencodeValue(node_id_to_string(message.target_id));
171
+ break;
172
+ case KrpcQueryType::GetPeers:
173
+ args["info_hash"] = BencodeValue(node_id_to_string(message.info_hash));
174
+ break;
175
+ case KrpcQueryType::AnnouncePeer:
176
+ args["info_hash"] = BencodeValue(node_id_to_string(message.info_hash));
177
+ args["port"] = BencodeValue(static_cast<int64_t>(message.port));
178
+ args["token"] = BencodeValue(message.token);
179
+ break;
180
+ }
181
+
182
+ root["a"] = args;
183
+ return root;
184
+ }
185
+
186
+ BencodeValue KrpcProtocol::encode_response(const KrpcMessage& message) {
187
+ BencodeValue root = BencodeValue::create_dict();
188
+
189
+ // Common fields
190
+ root["t"] = BencodeValue(message.transaction_id);
191
+ root["y"] = BencodeValue("r");
192
+
193
+ // Response data
194
+ BencodeValue response = BencodeValue::create_dict();
195
+ response["id"] = BencodeValue(node_id_to_string(message.response_id));
196
+
197
+ // Add nodes if present
198
+ if (!message.nodes.empty()) {
199
+ std::string compact_nodes;
200
+ for (const auto& node : message.nodes) {
201
+ compact_nodes += compact_node_info(node);
202
+ }
203
+ response["nodes"] = BencodeValue(compact_nodes);
204
+ }
205
+
206
+ // Add peers if present
207
+ if (!message.peers.empty()) {
208
+ BencodeValue values = BencodeValue::create_list();
209
+ for (const auto& peer : message.peers) {
210
+ values.push_back(BencodeValue(compact_peer_info(peer)));
211
+ }
212
+ response["values"] = values;
213
+ }
214
+
215
+ // Add token if present
216
+ if (!message.token.empty()) {
217
+ response["token"] = BencodeValue(message.token);
218
+ }
219
+
220
+ root["r"] = response;
221
+ return root;
222
+ }
223
+
224
+ BencodeValue KrpcProtocol::encode_error(const KrpcMessage& message) {
225
+ BencodeValue root = BencodeValue::create_dict();
226
+
227
+ // Common fields
228
+ root["t"] = BencodeValue(message.transaction_id);
229
+ root["y"] = BencodeValue("e");
230
+
231
+ // Error data
232
+ BencodeValue error = BencodeValue::create_list();
233
+ error.push_back(BencodeValue(static_cast<int64_t>(message.error_code)));
234
+ error.push_back(BencodeValue(message.error_message));
235
+
236
+ root["e"] = error;
237
+ return root;
238
+ }
239
+
240
+ // Decode messages
241
+ std::unique_ptr<KrpcMessage> KrpcProtocol::decode_message(const std::vector<uint8_t>& data) {
242
+ try {
243
+ BencodeValue root = bencode::decode(data);
244
+
245
+ if (!root.is_dict()) {
246
+ LOG_KRPC_ERROR("Root is not a dictionary");
247
+ return nullptr;
248
+ }
249
+
250
+ if (!root.has_key("t") || !root.has_key("y")) {
251
+ LOG_KRPC_ERROR("Missing required fields 't' or 'y'");
252
+ return nullptr;
253
+ }
254
+
255
+ std::string transaction_id = root["t"].as_string();
256
+ std::string message_type = root["y"].as_string();
257
+
258
+ if (message_type == "q") {
259
+ return decode_query(root);
260
+ } else if (message_type == "r") {
261
+ return decode_response(root);
262
+ } else if (message_type == "e") {
263
+ return decode_error(root);
264
+ } else {
265
+ LOG_KRPC_ERROR("Unknown message type: " << message_type);
266
+ return nullptr;
267
+ }
268
+ } catch (const std::exception& e) {
269
+ LOG_KRPC_ERROR("Failed to decode KRPC message: " << e.what());
270
+ return nullptr;
271
+ }
272
+ }
273
+
274
+ std::unique_ptr<KrpcMessage> KrpcProtocol::decode_query(const BencodeValue& data) {
275
+ if (!data.has_key("q") || !data.has_key("a")) {
276
+ LOG_KRPC_ERROR("Missing required query fields 'q' or 'a'");
277
+ return nullptr;
278
+ }
279
+
280
+ auto message = std::make_unique<KrpcMessage>();
281
+ message->type = KrpcMessageType::Query;
282
+ message->transaction_id = data["t"].as_string();
283
+
284
+ std::string query_method = data["q"].as_string();
285
+ message->query_type = string_to_query_type(query_method);
286
+
287
+ const BencodeValue& args = data["a"];
288
+ if (!args.is_dict() || !args.has_key("id")) {
289
+ LOG_KRPC_ERROR("Invalid arguments in query");
290
+ return nullptr;
291
+ }
292
+
293
+ message->sender_id = string_to_node_id(args["id"].as_string());
294
+
295
+ switch (message->query_type) {
296
+ case KrpcQueryType::Ping:
297
+ // No additional arguments
298
+ break;
299
+ case KrpcQueryType::FindNode:
300
+ if (args.has_key("target")) {
301
+ message->target_id = string_to_node_id(args["target"].as_string());
302
+ }
303
+ break;
304
+ case KrpcQueryType::GetPeers:
305
+ if (args.has_key("info_hash")) {
306
+ message->info_hash = string_to_node_id(args["info_hash"].as_string());
307
+ }
308
+ break;
309
+ case KrpcQueryType::AnnouncePeer:
310
+ if (args.has_key("info_hash")) {
311
+ message->info_hash = string_to_node_id(args["info_hash"].as_string());
312
+ }
313
+ if (args.has_key("port")) {
314
+ message->port = static_cast<uint16_t>(args["port"].as_integer());
315
+ }
316
+ if (args.has_key("token")) {
317
+ message->token = args["token"].as_string();
318
+ }
319
+ break;
320
+ }
321
+
322
+ return message;
323
+ }
324
+
325
+ std::unique_ptr<KrpcMessage> KrpcProtocol::decode_response(const BencodeValue& data) {
326
+ if (!data.has_key("r")) {
327
+ LOG_KRPC_ERROR("Missing required response field 'r'");
328
+ return nullptr;
329
+ }
330
+
331
+ auto message = std::make_unique<KrpcMessage>();
332
+ message->type = KrpcMessageType::Response;
333
+ message->transaction_id = data["t"].as_string();
334
+
335
+ const BencodeValue& response = data["r"];
336
+ if (!response.is_dict() || !response.has_key("id")) {
337
+ LOG_KRPC_ERROR("Invalid response data");
338
+ return nullptr;
339
+ }
340
+
341
+ message->response_id = string_to_node_id(response["id"].as_string());
342
+
343
+ // Parse nodes if present
344
+ if (response.has_key("nodes")) {
345
+ std::string compact_nodes = response["nodes"].as_string();
346
+ message->nodes = parse_compact_node_info(compact_nodes);
347
+ }
348
+
349
+ // Parse peers if present
350
+ if (response.has_key("values")) {
351
+ const BencodeValue& values = response["values"];
352
+ if (values.is_list()) {
353
+ for (size_t i = 0; i < values.size(); ++i) {
354
+ std::string compact_peers = values[i].as_string();
355
+ auto peers = parse_compact_peer_info(compact_peers);
356
+ message->peers.insert(message->peers.end(), peers.begin(), peers.end());
357
+ }
358
+ }
359
+ }
360
+
361
+ // Parse token if present
362
+ if (response.has_key("token")) {
363
+ message->token = response["token"].as_string();
364
+ }
365
+
366
+ return message;
367
+ }
368
+
369
+ std::unique_ptr<KrpcMessage> KrpcProtocol::decode_error(const BencodeValue& data) {
370
+ if (!data.has_key("e")) {
371
+ LOG_KRPC_ERROR("Missing required error field 'e'");
372
+ return nullptr;
373
+ }
374
+
375
+ auto message = std::make_unique<KrpcMessage>();
376
+ message->type = KrpcMessageType::Error;
377
+ message->transaction_id = data["t"].as_string();
378
+
379
+ const BencodeValue& error = data["e"];
380
+ if (!error.is_list() || error.size() < 2) {
381
+ LOG_KRPC_ERROR("Invalid error data");
382
+ return nullptr;
383
+ }
384
+
385
+ message->error_code = static_cast<KrpcErrorCode>(error[0].as_integer());
386
+ message->error_message = error[1].as_string();
387
+
388
+ return message;
389
+ }
390
+
391
+ // Utility functions
392
+ std::string KrpcProtocol::generate_transaction_id() {
393
+ return std::to_string(++transaction_counter_);
394
+ }
395
+
396
+ std::string KrpcProtocol::node_id_to_string(const NodeId& id) {
397
+ return std::string(id.begin(), id.end());
398
+ }
399
+
400
+ NodeId KrpcProtocol::string_to_node_id(const std::string& str) {
401
+ NodeId id;
402
+ if (str.size() >= 20) {
403
+ std::copy_n(str.begin(), 20, id.begin());
404
+ } else {
405
+ std::fill(id.begin(), id.end(), 0);
406
+ std::copy(str.begin(), str.end(), id.begin());
407
+ }
408
+ return id;
409
+ }
410
+
411
+ std::string KrpcProtocol::compact_peer_info(const Peer& peer) {
412
+ std::string result;
413
+ result.reserve(6);
414
+
415
+ // Convert IP address to 4 bytes
416
+ if (network_utils::is_valid_ipv4(peer.ip)) {
417
+ struct in_addr addr;
418
+ if (inet_pton(AF_INET, peer.ip.c_str(), &addr) == 1) {
419
+ uint32_t ip = ntohl(addr.s_addr);
420
+ result += static_cast<char>((ip >> 24) & 0xFF);
421
+ result += static_cast<char>((ip >> 16) & 0xFF);
422
+ result += static_cast<char>((ip >> 8) & 0xFF);
423
+ result += static_cast<char>(ip & 0xFF);
424
+ } else {
425
+ // Invalid IP, use 0.0.0.0
426
+ result += "\x00\x00\x00\x00";
427
+ }
428
+ } else {
429
+ // Not IPv4, use 0.0.0.0
430
+ result += "\x00\x00\x00\x00";
431
+ }
432
+
433
+ // Convert port to 2 bytes (network byte order)
434
+ result += static_cast<char>((peer.port >> 8) & 0xFF);
435
+ result += static_cast<char>(peer.port & 0xFF);
436
+
437
+ return result;
438
+ }
439
+
440
+ std::string KrpcProtocol::compact_node_info(const KrpcNode& node) {
441
+ std::string result;
442
+ result.reserve(26);
443
+
444
+ // Node ID (20 bytes)
445
+ result += node_id_to_string(node.id);
446
+
447
+ // IP address (4 bytes)
448
+ if (network_utils::is_valid_ipv4(node.ip)) {
449
+ struct in_addr addr;
450
+ if (inet_pton(AF_INET, node.ip.c_str(), &addr) == 1) {
451
+ uint32_t ip = ntohl(addr.s_addr);
452
+ result += static_cast<char>((ip >> 24) & 0xFF);
453
+ result += static_cast<char>((ip >> 16) & 0xFF);
454
+ result += static_cast<char>((ip >> 8) & 0xFF);
455
+ result += static_cast<char>(ip & 0xFF);
456
+ } else {
457
+ // Invalid IP, use 0.0.0.0
458
+ result += "\x00\x00\x00\x00";
459
+ }
460
+ } else {
461
+ // Not IPv4, use 0.0.0.0
462
+ result += "\x00\x00\x00\x00";
463
+ }
464
+
465
+ // Port (2 bytes, network byte order)
466
+ result += static_cast<char>((node.port >> 8) & 0xFF);
467
+ result += static_cast<char>(node.port & 0xFF);
468
+
469
+ return result;
470
+ }
471
+
472
+ std::vector<Peer> KrpcProtocol::parse_compact_peer_info(const std::string& compact_info) {
473
+ std::vector<Peer> peers;
474
+
475
+ if (compact_info.size() % 6 != 0) {
476
+ LOG_KRPC_WARN("Invalid compact peer info size: " << compact_info.size());
477
+ return peers;
478
+ }
479
+
480
+ for (size_t i = 0; i < compact_info.size(); i += 6) {
481
+ // Extract IP address (4 bytes)
482
+ uint32_t ip = 0;
483
+ ip |= (static_cast<uint8_t>(compact_info[i]) << 24);
484
+ ip |= (static_cast<uint8_t>(compact_info[i + 1]) << 16);
485
+ ip |= (static_cast<uint8_t>(compact_info[i + 2]) << 8);
486
+ ip |= static_cast<uint8_t>(compact_info[i + 3]);
487
+
488
+ struct in_addr addr;
489
+ addr.s_addr = htonl(ip);
490
+ char ip_str[INET_ADDRSTRLEN];
491
+ inet_ntop(AF_INET, &addr, ip_str, INET_ADDRSTRLEN);
492
+
493
+ // Extract port (2 bytes)
494
+ uint16_t port = 0;
495
+ port |= (static_cast<uint8_t>(compact_info[i + 4]) << 8);
496
+ port |= static_cast<uint8_t>(compact_info[i + 5]);
497
+
498
+ peers.emplace_back(ip_str, port);
499
+ }
500
+
501
+ return peers;
502
+ }
503
+
504
+ std::vector<KrpcNode> KrpcProtocol::parse_compact_node_info(const std::string& compact_info) {
505
+ std::vector<KrpcNode> nodes;
506
+
507
+ if (compact_info.size() % 26 != 0) {
508
+ LOG_KRPC_WARN("Invalid compact node info size: " << compact_info.size());
509
+ return nodes;
510
+ }
511
+
512
+ for (size_t i = 0; i < compact_info.size(); i += 26) {
513
+ // Extract node ID (20 bytes)
514
+ NodeId node_id;
515
+ std::copy_n(compact_info.begin() + i, 20, node_id.begin());
516
+
517
+ // Extract IP address (4 bytes)
518
+ uint32_t ip = 0;
519
+ ip |= (static_cast<uint8_t>(compact_info[i + 20]) << 24);
520
+ ip |= (static_cast<uint8_t>(compact_info[i + 21]) << 16);
521
+ ip |= (static_cast<uint8_t>(compact_info[i + 22]) << 8);
522
+ ip |= static_cast<uint8_t>(compact_info[i + 23]);
523
+
524
+ struct in_addr addr;
525
+ addr.s_addr = htonl(ip);
526
+ char ip_str[INET_ADDRSTRLEN];
527
+ inet_ntop(AF_INET, &addr, ip_str, INET_ADDRSTRLEN);
528
+
529
+ // Extract port (2 bytes)
530
+ uint16_t port = 0;
531
+ port |= (static_cast<uint8_t>(compact_info[i + 24]) << 8);
532
+ port |= static_cast<uint8_t>(compact_info[i + 25]);
533
+
534
+ nodes.emplace_back(node_id, ip_str, port);
535
+ }
536
+
537
+ return nodes;
538
+ }
539
+
540
+ KrpcQueryType KrpcProtocol::string_to_query_type(const std::string& str) {
541
+ if (str == "ping") return KrpcQueryType::Ping;
542
+ if (str == "find_node") return KrpcQueryType::FindNode;
543
+ if (str == "get_peers") return KrpcQueryType::GetPeers;
544
+ if (str == "announce_peer") return KrpcQueryType::AnnouncePeer;
545
+ return KrpcQueryType::Ping; // Default
546
+ }
547
+
548
+ std::string KrpcProtocol::query_type_to_string(KrpcQueryType type) {
549
+ switch (type) {
550
+ case KrpcQueryType::Ping: return "ping";
551
+ case KrpcQueryType::FindNode: return "find_node";
552
+ case KrpcQueryType::GetPeers: return "get_peers";
553
+ case KrpcQueryType::AnnouncePeer: return "announce_peer";
554
+ default: return "ping";
555
+ }
556
+ }
557
+
558
+ } // namespace librats
@@ -0,0 +1,145 @@
1
+ #pragma once
2
+
3
+ #include "bencode.h"
4
+ #include "socket.h"
5
+ #include <string>
6
+ #include <vector>
7
+ #include <array>
8
+ #include <memory>
9
+ #include <atomic>
10
+
11
+ namespace librats {
12
+
13
+ using NodeId = std::array<uint8_t, 20>;
14
+ using InfoHash = std::array<uint8_t, 20>;
15
+
16
+ /**
17
+ * KRPC Message types
18
+ */
19
+ enum class KrpcMessageType {
20
+ Query,
21
+ Response,
22
+ Error
23
+ };
24
+
25
+ /**
26
+ * KRPC Query types
27
+ */
28
+ enum class KrpcQueryType {
29
+ Ping,
30
+ FindNode,
31
+ GetPeers,
32
+ AnnouncePeer
33
+ };
34
+
35
+ /**
36
+ * KRPC Error codes
37
+ */
38
+ enum class KrpcErrorCode {
39
+ GenericError = 201,
40
+ ServerError = 202,
41
+ ProtocolError = 203,
42
+ MethodUnknown = 204
43
+ };
44
+
45
+ /**
46
+ * KRPC Node information
47
+ */
48
+ struct KrpcNode {
49
+ NodeId id;
50
+ std::string ip;
51
+ uint16_t port;
52
+
53
+ KrpcNode() : port(0) {}
54
+ KrpcNode(const NodeId& node_id, const std::string& ip_addr, uint16_t port_num)
55
+ : id(node_id), ip(ip_addr), port(port_num) {}
56
+ };
57
+
58
+ /**
59
+ * KRPC Message structure
60
+ */
61
+ struct KrpcMessage {
62
+ KrpcMessageType type;
63
+ std::string transaction_id;
64
+
65
+ // For queries
66
+ KrpcQueryType query_type;
67
+ NodeId sender_id;
68
+ NodeId target_id;
69
+ InfoHash info_hash;
70
+ uint16_t port;
71
+ std::string token;
72
+
73
+ // For responses
74
+ NodeId response_id;
75
+ std::vector<KrpcNode> nodes;
76
+ std::vector<Peer> peers;
77
+
78
+ // For errors
79
+ KrpcErrorCode error_code;
80
+ std::string error_message;
81
+
82
+ KrpcMessage() : type(KrpcMessageType::Query), query_type(KrpcQueryType::Ping), port(0), error_code(KrpcErrorCode::GenericError) {}
83
+ };
84
+
85
+ /**
86
+ * KRPC Protocol implementation
87
+ */
88
+ class KrpcProtocol {
89
+ public:
90
+ KrpcProtocol();
91
+ ~KrpcProtocol();
92
+
93
+ /**
94
+ * Create KRPC messages
95
+ */
96
+ static KrpcMessage create_ping_query(const std::string& transaction_id, const NodeId& sender_id);
97
+ static KrpcMessage create_find_node_query(const std::string& transaction_id, const NodeId& sender_id, const NodeId& target_id);
98
+ static KrpcMessage create_get_peers_query(const std::string& transaction_id, const NodeId& sender_id, const InfoHash& info_hash);
99
+ static KrpcMessage create_announce_peer_query(const std::string& transaction_id, const NodeId& sender_id, const InfoHash& info_hash, uint16_t port, const std::string& token);
100
+
101
+ static KrpcMessage create_ping_response(const std::string& transaction_id, const NodeId& response_id);
102
+ static KrpcMessage create_find_node_response(const std::string& transaction_id, const NodeId& response_id, const std::vector<KrpcNode>& nodes);
103
+ static KrpcMessage create_get_peers_response(const std::string& transaction_id, const NodeId& response_id, const std::vector<Peer>& peers, const std::string& token);
104
+ static KrpcMessage create_get_peers_response_with_nodes(const std::string& transaction_id, const NodeId& response_id, const std::vector<KrpcNode>& nodes, const std::string& token);
105
+ static KrpcMessage create_announce_peer_response(const std::string& transaction_id, const NodeId& response_id);
106
+
107
+ static KrpcMessage create_error(const std::string& transaction_id, KrpcErrorCode error_code, const std::string& error_message);
108
+
109
+ /**
110
+ * Encode/decode KRPC messages
111
+ */
112
+ static std::vector<uint8_t> encode_message(const KrpcMessage& message);
113
+ static std::unique_ptr<KrpcMessage> decode_message(const std::vector<uint8_t>& data);
114
+
115
+ /**
116
+ * Generate transaction ID
117
+ */
118
+ static std::string generate_transaction_id();
119
+
120
+ /**
121
+ * Utility functions
122
+ */
123
+ static std::string node_id_to_string(const NodeId& id);
124
+ static NodeId string_to_node_id(const std::string& str);
125
+ static std::string compact_peer_info(const Peer& peer);
126
+ static std::string compact_node_info(const KrpcNode& node);
127
+ static std::vector<Peer> parse_compact_peer_info(const std::string& compact_info);
128
+ static std::vector<KrpcNode> parse_compact_node_info(const std::string& compact_info);
129
+
130
+ private:
131
+ static BencodeValue encode_query(const KrpcMessage& message);
132
+ static BencodeValue encode_response(const KrpcMessage& message);
133
+ static BencodeValue encode_error(const KrpcMessage& message);
134
+
135
+ static std::unique_ptr<KrpcMessage> decode_query(const BencodeValue& data);
136
+ static std::unique_ptr<KrpcMessage> decode_response(const BencodeValue& data);
137
+ static std::unique_ptr<KrpcMessage> decode_error(const BencodeValue& data);
138
+
139
+ static KrpcQueryType string_to_query_type(const std::string& str);
140
+ static std::string query_type_to_string(KrpcQueryType type);
141
+
142
+ static std::atomic<uint32_t> transaction_counter_;
143
+ };
144
+
145
+ } // namespace librats