mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Tests added and some fixes for the onion part.
This commit is contained in:
parent
7e2d21271a
commit
6cd1e7fb70
|
@ -12,6 +12,13 @@
|
||||||
#include "../toxcore/onion.h"
|
#include "../toxcore/onion.h"
|
||||||
#include "../toxcore/onion_announce.h"
|
#include "../toxcore/onion_announce.h"
|
||||||
|
|
||||||
|
#ifdef __WIN32__
|
||||||
|
#define c_sleep(x) Sleep(1*x)
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#define c_sleep(x) usleep(1000*x)
|
||||||
|
#endif
|
||||||
|
|
||||||
void do_onion(Onion *onion)
|
void do_onion(Onion *onion)
|
||||||
{
|
{
|
||||||
networking_poll(onion->net);
|
networking_poll(onion->net);
|
||||||
|
@ -46,6 +53,38 @@ static int handle_test_2(void *object, IP_Port source, uint8_t *packet, uint32_t
|
||||||
handled_test_2 = 1;
|
handled_test_2 = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
void print_client_id(uint8_t *client_id, uint32_t length)
|
||||||
|
{
|
||||||
|
uint32_t j;
|
||||||
|
|
||||||
|
for (j = 0; j < length; j++) {
|
||||||
|
printf("%02hhX", client_id[j]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
static int handled_test_3;
|
||||||
|
uint8_t test_3_pub_key[crypto_box_PUBLICKEYBYTES];
|
||||||
|
uint8_t test_3_ping_id[crypto_hash_sha256_BYTES];
|
||||||
|
static int handle_test_3(void *object, IP_Port source, uint8_t *packet, uint32_t length)
|
||||||
|
{
|
||||||
|
Onion *onion = object;
|
||||||
|
|
||||||
|
if (length != (1 + crypto_box_NONCEBYTES + crypto_hash_sha256_BYTES + crypto_box_MACBYTES))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
//print_client_id(packet, length);
|
||||||
|
int len = decrypt_data(test_3_pub_key, onion->dht->c->self_secret_key, packet + 1, packet + 1 + crypto_box_NONCEBYTES,
|
||||||
|
crypto_hash_sha256_BYTES + crypto_box_MACBYTES, test_3_ping_id);
|
||||||
|
|
||||||
|
if (len == -1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
//print_client_id(test_3_ping_id, sizeof(test_3_ping_id));
|
||||||
|
handled_test_3 = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(test_basic)
|
START_TEST(test_basic)
|
||||||
{
|
{
|
||||||
|
@ -72,7 +111,7 @@ START_TEST(test_basic)
|
||||||
nodes[1] = n2;
|
nodes[1] = n2;
|
||||||
nodes[2] = n1;
|
nodes[2] = n1;
|
||||||
nodes[3] = n2;
|
nodes[3] = n2;
|
||||||
int ret = send_onion_packet(onion1, nodes, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
|
int ret = send_onion_packet(onion1->dht, nodes, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
|
||||||
ck_assert_msg(ret == 0, "Failed to create/send onion packet.");
|
ck_assert_msg(ret == 0, "Failed to create/send onion packet.");
|
||||||
|
|
||||||
handled_test_1 = 0;
|
handled_test_1 = 0;
|
||||||
|
@ -89,13 +128,69 @@ START_TEST(test_basic)
|
||||||
do_onion(onion1);
|
do_onion(onion1);
|
||||||
do_onion(onion2);
|
do_onion(onion2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Onion_Announce *onion1_a = new_onion_announce(onion1->dht);
|
||||||
|
Onion_Announce *onion2_a = new_onion_announce(onion2->dht);
|
||||||
|
networking_registerhandler(onion1->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_test_3, onion1);
|
||||||
|
ck_assert_msg((onion1_a != NULL) && (onion2_a != NULL), "Onion_Announce failed initializing.");
|
||||||
|
uint8_t zeroes[64] = {0};
|
||||||
|
memcpy(test_3_pub_key, nodes[3].client_id, crypto_box_PUBLICKEYBYTES);
|
||||||
|
ret = send_announce_request(onion1->dht, nodes, onion1->dht->c->self_public_key, onion1->dht->c->self_secret_key,
|
||||||
|
zeroes, onion1->dht->c->self_public_key);
|
||||||
|
ck_assert_msg(ret == 0, "Failed to create/send onion announce_request packet.");
|
||||||
|
handled_test_3 = 0;
|
||||||
|
|
||||||
|
while (handled_test_3 == 0) {
|
||||||
|
do_onion(onion1);
|
||||||
|
do_onion(onion2);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_announce_request(onion1->dht, nodes, onion1->dht->c->self_public_key, onion1->dht->c->self_secret_key,
|
||||||
|
test_3_ping_id, onion1->dht->c->self_public_key);
|
||||||
|
|
||||||
|
while (memcmp(onion2_a->entries[ONION_ANNOUNCE_MAX_ENTRIES - 1].public_key, onion1->dht->c->self_public_key,
|
||||||
|
crypto_box_PUBLICKEYBYTES) != 0) {
|
||||||
|
do_onion(onion1);
|
||||||
|
do_onion(onion2);
|
||||||
|
c_sleep(50);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Onion *onion;
|
||||||
|
Onion_Announce *onion_a;
|
||||||
|
} Onions;
|
||||||
|
|
||||||
|
Onions *new_onions(uint16_t port)
|
||||||
|
{
|
||||||
|
IP ip;
|
||||||
|
ip_init(&ip, 1);
|
||||||
|
ip.ip6.uint8[15] = 1;
|
||||||
|
Onions *on = malloc(sizeof(Onions));
|
||||||
|
DHT *dht = new_DHT(new_net_crypto(new_networking(ip, port)));
|
||||||
|
on->onion = new_onion(dht);
|
||||||
|
on->onion_a = new_onion_announce(dht);
|
||||||
|
|
||||||
|
if (on->onion && on->onion_a)
|
||||||
|
return on;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NUM_ONIONS 64
|
||||||
|
|
||||||
START_TEST(test_announce)
|
START_TEST(test_announce)
|
||||||
{
|
{
|
||||||
|
uint32_t i;
|
||||||
|
Onions *onions[NUM_ONIONS];
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_ONIONS; ++i) {
|
||||||
|
onions[i] = new_onions(i + 34655);
|
||||||
|
ck_assert_msg(onions[i] != 0, "Failed to create onions. %u");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
|
@ -226,6 +226,7 @@ int sendpacket(Networking_Core *net, IP_Port ip_port, uint8_t *data, uint32_t le
|
||||||
*/
|
*/
|
||||||
static int receivepacket(sock_t sock, IP_Port *ip_port, uint8_t *data, uint32_t *length)
|
static int receivepacket(sock_t sock, IP_Port *ip_port, uint8_t *data, uint32_t *length)
|
||||||
{
|
{
|
||||||
|
memset(ip_port, 0, sizeof(IP_Port));
|
||||||
struct sockaddr_storage addr;
|
struct sockaddr_storage addr;
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
int addrlen = sizeof(addr);
|
int addrlen = sizeof(addr);
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
* return -1 on failure.
|
* return -1 on failure.
|
||||||
* return 0 on success.
|
* return 0 on success.
|
||||||
*/
|
*/
|
||||||
int send_onion_packet(Onion *onion, Node_format *nodes, uint8_t *data, uint32_t length)
|
int send_onion_packet(DHT *dht, Node_format *nodes, uint8_t *data, uint32_t length)
|
||||||
{
|
{
|
||||||
if (1 + length + SEND_1 > MAX_ONION_SIZE || length == 0)
|
if (1 + length + SEND_1 > MAX_ONION_SIZE || length == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -82,15 +82,15 @@ int send_onion_packet(Onion *onion, Node_format *nodes, uint8_t *data, uint32_t
|
||||||
uint8_t packet[1 + length + SEND_1];
|
uint8_t packet[1 + length + SEND_1];
|
||||||
packet[0] = NET_PACKET_ONION_SEND_INITIAL;
|
packet[0] = NET_PACKET_ONION_SEND_INITIAL;
|
||||||
memcpy(packet + 1, nonce, crypto_box_NONCEBYTES);
|
memcpy(packet + 1, nonce, crypto_box_NONCEBYTES);
|
||||||
memcpy(packet + 1 + crypto_box_NONCEBYTES, onion->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
|
memcpy(packet + 1 + crypto_box_NONCEBYTES, dht->self_public_key, crypto_box_PUBLICKEYBYTES);
|
||||||
|
|
||||||
len = encrypt_data(nodes[0].client_id, onion->dht->self_secret_key, nonce,
|
len = encrypt_data(nodes[0].client_id, dht->self_secret_key, nonce,
|
||||||
step3, sizeof(step3), packet + 1 + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES);
|
step3, sizeof(step3), packet + 1 + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES);
|
||||||
|
|
||||||
if ((uint32_t)len != sizeof(IP_Port) + SEND_BASE * 2 + length + crypto_box_MACBYTES)
|
if ((uint32_t)len != sizeof(IP_Port) + SEND_BASE * 2 + length + crypto_box_MACBYTES)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ((uint32_t)sendpacket(onion->net, nodes[0].ip_port, packet, sizeof(packet)) != sizeof(packet))
|
if ((uint32_t)sendpacket(dht->c->lossless_udp->net, nodes[0].ip_port, packet, sizeof(packet)) != sizeof(packet))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -49,7 +49,7 @@ typedef struct {
|
||||||
* return -1 on failure.
|
* return -1 on failure.
|
||||||
* return 0 on success.
|
* return 0 on success.
|
||||||
*/
|
*/
|
||||||
int send_onion_packet(Onion *onion, Node_format *nodes, uint8_t *data, uint32_t length);
|
int send_onion_packet(DHT *dht, Node_format *nodes, uint8_t *data, uint32_t length);
|
||||||
|
|
||||||
/* Create and send a onion response sent initially to dest with.
|
/* Create and send a onion response sent initially to dest with.
|
||||||
*
|
*
|
||||||
|
|
|
@ -30,22 +30,57 @@
|
||||||
#define PING_ID_SIZE crypto_hash_sha256_BYTES
|
#define PING_ID_SIZE crypto_hash_sha256_BYTES
|
||||||
#define PING_ID_TIMEOUT 10
|
#define PING_ID_TIMEOUT 10
|
||||||
|
|
||||||
#define ANNOUNCE_REQUEST_SIZE (1 + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + PING_ID_SIZE + crypto_box_PUBLICKEYBYTES + crypto_box_MACBYTES + ONION_RETURN_3)
|
#define ANNOUNCE_REQUEST_SIZE (1 + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + PING_ID_SIZE + crypto_box_PUBLICKEYBYTES + crypto_box_MACBYTES)
|
||||||
|
#define ANNOUNCE_REQUEST_SIZE_RECV (ANNOUNCE_REQUEST_SIZE + ONION_RETURN_3)
|
||||||
#define ANNOUNCE_RESPONSE_MIN_SIZE (1 + crypto_box_NONCEBYTES + PING_ID_SIZE + crypto_box_MACBYTES)
|
#define ANNOUNCE_RESPONSE_MIN_SIZE (1 + crypto_box_NONCEBYTES + PING_ID_SIZE + crypto_box_MACBYTES)
|
||||||
#define ANNOUNCE_RESPONSE_MAX_SIZE (ANNOUNCE_RESPONSE_MIN_SIZE + sizeof(Node_format)*MAX_SENT_NODES)
|
#define ANNOUNCE_RESPONSE_MAX_SIZE (ANNOUNCE_RESPONSE_MIN_SIZE + sizeof(Node_format)*MAX_SENT_NODES)
|
||||||
|
|
||||||
#define DATA_REQUEST_MIN_SIZE (1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + crypto_box_MACBYTES + ONION_RETURN_3)
|
#define DATA_REQUEST_MIN_SIZE (1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + crypto_box_MACBYTES + ONION_RETURN_3)
|
||||||
|
|
||||||
|
/* Create and send an onion announce request packet.
|
||||||
|
*
|
||||||
|
* nodes is a list of 4 nodes, the packet will route through nodes 0, 1, 2 and the data
|
||||||
|
* with length length will arrive at 3.
|
||||||
|
*
|
||||||
|
* public_key and secret_key is the kepair which will be used to encrypt the request.
|
||||||
|
* ping_id is the ping id that will be sent in the request.
|
||||||
|
* client_id is the client id of the node we are searching for.
|
||||||
|
*
|
||||||
|
* return -1 on failure.
|
||||||
|
* return 0 on success.
|
||||||
|
*/
|
||||||
|
int send_announce_request(DHT *dht, Node_format *nodes, uint8_t *public_key, uint8_t *secret_key, uint8_t *ping_id,
|
||||||
|
uint8_t *client_id)
|
||||||
|
{
|
||||||
|
uint8_t plain[PING_ID_SIZE + crypto_box_PUBLICKEYBYTES];
|
||||||
|
memcpy(plain, ping_id, PING_ID_SIZE);
|
||||||
|
memcpy(plain + PING_ID_SIZE, client_id, crypto_box_PUBLICKEYBYTES);
|
||||||
|
|
||||||
|
uint8_t packet[ANNOUNCE_REQUEST_SIZE];
|
||||||
|
packet[0] = NET_PACKET_ANNOUNCE_REQUEST;
|
||||||
|
new_nonce(packet + 1);
|
||||||
|
|
||||||
|
int len = encrypt_data(nodes[3].client_id, secret_key, packet + 1, plain, sizeof(plain),
|
||||||
|
packet + 1 + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES);
|
||||||
|
|
||||||
|
if ((uint32_t)len + 1 + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES != ANNOUNCE_REQUEST_SIZE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memcpy(packet + 1 + crypto_box_NONCEBYTES, public_key, crypto_box_PUBLICKEYBYTES);
|
||||||
|
|
||||||
|
return send_onion_packet(dht, nodes, packet, sizeof(packet));
|
||||||
|
}
|
||||||
|
|
||||||
/* Generate a ping_id and put it in ping_id */
|
/* Generate a ping_id and put it in ping_id */
|
||||||
static void generate_ping_id(Onion_Announce *onion_a, uint64_t time, uint8_t *public_key, uint8_t *ret,
|
static void generate_ping_id(Onion_Announce *onion_a, uint64_t time, uint8_t *public_key, IP_Port ret_ip_port,
|
||||||
uint8_t *ping_id)
|
uint8_t *ping_id)
|
||||||
{
|
{
|
||||||
time /= PING_ID_TIMEOUT;
|
time /= PING_ID_TIMEOUT;
|
||||||
uint8_t data[crypto_secretbox_KEYBYTES + sizeof(time) + crypto_box_PUBLICKEYBYTES + ONION_RETURN_3];
|
uint8_t data[crypto_secretbox_KEYBYTES + sizeof(time) + crypto_box_PUBLICKEYBYTES + sizeof(ret_ip_port)];
|
||||||
memcpy(data, onion_a->secret_bytes, crypto_secretbox_KEYBYTES);
|
memcpy(data, onion_a->secret_bytes, crypto_secretbox_KEYBYTES);
|
||||||
memcpy(data + crypto_secretbox_KEYBYTES, &time, sizeof(time));
|
memcpy(data + crypto_secretbox_KEYBYTES, &time, sizeof(time));
|
||||||
memcpy(data + crypto_secretbox_KEYBYTES + sizeof(time), public_key, crypto_box_PUBLICKEYBYTES);
|
memcpy(data + crypto_secretbox_KEYBYTES + sizeof(time), public_key, crypto_box_PUBLICKEYBYTES);
|
||||||
memcpy(data + crypto_secretbox_KEYBYTES + sizeof(time) + crypto_box_PUBLICKEYBYTES, ret, ONION_RETURN_3);
|
memcpy(data + crypto_secretbox_KEYBYTES + sizeof(time) + crypto_box_PUBLICKEYBYTES, &ret_ip_port, sizeof(ret_ip_port));
|
||||||
crypto_hash_sha256(ping_id, data, sizeof(data));
|
crypto_hash_sha256(ping_id, data, sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +173,7 @@ static int handle_announce_request(void *object, IP_Port source, uint8_t *packet
|
||||||
{
|
{
|
||||||
Onion_Announce *onion_a = object;
|
Onion_Announce *onion_a = object;
|
||||||
|
|
||||||
if (length != ANNOUNCE_REQUEST_SIZE)
|
if (length != ANNOUNCE_REQUEST_SIZE_RECV)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
uint8_t plain[PING_ID_SIZE + crypto_box_PUBLICKEYBYTES];
|
uint8_t plain[PING_ID_SIZE + crypto_box_PUBLICKEYBYTES];
|
||||||
|
@ -150,18 +185,16 @@ static int handle_announce_request(void *object, IP_Port source, uint8_t *packet
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
uint8_t ping_id1[PING_ID_SIZE];
|
uint8_t ping_id1[PING_ID_SIZE];
|
||||||
generate_ping_id(onion_a, unix_time(), packet + 1 + crypto_box_NONCEBYTES,
|
generate_ping_id(onion_a, unix_time(), packet + 1 + crypto_box_NONCEBYTES, source, ping_id1);
|
||||||
packet + (ANNOUNCE_REQUEST_SIZE - ONION_RETURN_3), ping_id1);
|
|
||||||
|
|
||||||
uint8_t ping_id2[PING_ID_SIZE];
|
uint8_t ping_id2[PING_ID_SIZE];
|
||||||
generate_ping_id(onion_a, unix_time() + PING_ID_TIMEOUT, packet + 1 + crypto_box_NONCEBYTES,
|
generate_ping_id(onion_a, unix_time() + PING_ID_TIMEOUT, packet + 1 + crypto_box_NONCEBYTES, source, ping_id2);
|
||||||
packet + (ANNOUNCE_REQUEST_SIZE - ONION_RETURN_3), ping_id2);
|
|
||||||
|
|
||||||
int stored = 0;
|
int stored = 0;
|
||||||
|
|
||||||
if (memcmp(ping_id1, plain, PING_ID_SIZE) == 0 || memcmp(ping_id2, plain, PING_ID_SIZE) == 0) {
|
if (memcmp(ping_id1, plain, PING_ID_SIZE) == 0 || memcmp(ping_id2, plain, PING_ID_SIZE) == 0) {
|
||||||
stored = add_to_entries(onion_a, source, packet + 1 + crypto_box_NONCEBYTES,
|
stored = add_to_entries(onion_a, source, packet + 1 + crypto_box_NONCEBYTES,
|
||||||
packet + (ANNOUNCE_REQUEST_SIZE - ONION_RETURN_3));
|
packet + (ANNOUNCE_REQUEST_SIZE_RECV - ONION_RETURN_3));
|
||||||
} else {
|
} else {
|
||||||
stored = (in_entries(onion_a, plain + PING_ID_SIZE) != -1);
|
stored = (in_entries(onion_a, plain + PING_ID_SIZE) != -1);
|
||||||
}
|
}
|
||||||
|
@ -193,7 +226,7 @@ static int handle_announce_request(void *object, IP_Port source, uint8_t *packet
|
||||||
memcpy(data + 1, nonce, crypto_box_NONCEBYTES);
|
memcpy(data + 1, nonce, crypto_box_NONCEBYTES);
|
||||||
|
|
||||||
if (send_onion_response(onion_a->net, source, data, 1 + crypto_box_NONCEBYTES + len,
|
if (send_onion_response(onion_a->net, source, data, 1 + crypto_box_NONCEBYTES + len,
|
||||||
packet + (ANNOUNCE_REQUEST_SIZE - ONION_RETURN_3)) == -1)
|
packet + (ANNOUNCE_REQUEST_SIZE_RECV - ONION_RETURN_3)) == -1)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -29,10 +29,10 @@
|
||||||
#define ONION_ANNOUNCE_TIMEOUT 300
|
#define ONION_ANNOUNCE_TIMEOUT 300
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
|
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
|
||||||
IP_Port ret_ip_port;
|
IP_Port ret_ip_port;
|
||||||
uint8_t ret[ONION_RETURN_3];
|
uint8_t ret[ONION_RETURN_3];
|
||||||
uint64_t time;
|
uint64_t time;
|
||||||
} Onion_Announce_Entry;
|
} Onion_Announce_Entry;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -43,7 +43,20 @@ typedef struct {
|
||||||
uint8_t secret_bytes[crypto_secretbox_KEYBYTES];
|
uint8_t secret_bytes[crypto_secretbox_KEYBYTES];
|
||||||
} Onion_Announce;
|
} Onion_Announce;
|
||||||
|
|
||||||
|
/* Create and send an onion announce request packet.
|
||||||
|
*
|
||||||
|
* nodes is a list of 4 nodes, the packet will route through nodes 0, 1, 2 and the data
|
||||||
|
* with length length will arrive at 3.
|
||||||
|
*
|
||||||
|
* public_key and secret_key is the kepair which will be used to encrypt the request.
|
||||||
|
* ping_id is the ping id that will be sent in the request.
|
||||||
|
* client_id is the client id of the node we are searching for.
|
||||||
|
*
|
||||||
|
* return -1 on failure.
|
||||||
|
* return 0 on success.
|
||||||
|
*/
|
||||||
|
int send_announce_request(DHT *dht, Node_format *nodes, uint8_t *public_key, uint8_t *secret_key, uint8_t *ping_id,
|
||||||
|
uint8_t *client_id);
|
||||||
|
|
||||||
Onion_Announce *new_onion_announce(DHT *dht);
|
Onion_Announce *new_onion_announce(DHT *dht);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user