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.
- package/binding.gyp +1 -0
- 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 +2342 -0
- package/native-src/src/dht.h +501 -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 +2715 -0
- package/native-src/src/librats.h +1729 -0
- package/native-src/src/librats_bittorrent.cpp +167 -0
- package/native-src/src/librats_c.cpp +1317 -0
- package/native-src/src/librats_c.h +237 -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
|
@@ -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
|