Major protocol changes, this commit breaks all network compatibility.

Removed a bunch of useless space from various structs.

pack_nodes() and unpack_nodes() are now used to transmit lists of
nodes for the DHT and onion parts. They provide a way to transmit
both ipv6 and ipv4 nodes in the same packet in a portable way that
takes as little space as possible without compression.

Using pack_nodes, merged the send nodes and send nodes ipv6 packets
into one packet greatly reducing the risk of amplification attacks.
This commit is contained in:
irungentoo 2014-04-15 18:09:07 -04:00
parent 229b2b2dcd
commit f9bf7b074a
No known key found for this signature in database
GPG Key ID: 10349DC9BED89E98
7 changed files with 220 additions and 300 deletions

View File

@ -136,9 +136,8 @@ START_TEST(test_struct_sizes)
{
ck_assert_msg(sizeof(IP4) == 4, "sizeof(IP4): expected result 4, got %u.", sizeof(IP4));
ck_assert_msg(sizeof(IP6) == 16, "sizeof(IP6): expected result 16, got %u.", sizeof(IP6));
ck_assert_msg(sizeof(IP) == 20, "sizeof(IP): expected result 20, got %u.", sizeof(IP));
ck_assert_msg(sizeof(IP_Port) == 24, "sizeof(IP_Port): expected result 24, got %u.", sizeof(IP_Port));
ck_assert_msg(sizeof(IP4_Port) == 8, "sizeof(IP4_Port): expected result 8, got %u.", sizeof(IP4_Port));
ck_assert_msg(sizeof(IP) == 17, "sizeof(IP): expected result 17, got %u.", sizeof(IP));
ck_assert_msg(sizeof(IP_Port) == 19, "sizeof(IP_Port): expected result 19, got %u.", sizeof(IP_Port));
}
END_TEST

View File

@ -79,12 +79,9 @@ Protocol
Node format:
```
[char array (node_id), length=32 bytes][ip (in network byte order), length=4 bytes][port (in network byte order), length=2 bytes][Padding , length=2 bytes]
[uint8_t family (2 == IPv4, 10 == IPv6, 130 == TCP IPv4, 138 == TCP IPv6)][ip (in network byte order), length=4 bytes if ipv4, 16 bytes if ipv6][port (in network byte order), length=2 bytes][char array (node_id), length=32 bytes]
```
see also: DHT.h (Node4_format struct)
IPv6 Node format:
see: DHT.h (Node_format struct)
see also: DHT.h (pack_nodes() and unpack_nodes())
Valid queries and Responses:
@ -102,12 +99,7 @@ Packet contents:
```
Valid replies: a send_nodes packet
Send_nodes (response (for ipv4 addresses)):
Send_nodes (response (for all addresses)):
```
[byte with value: 03][char array (client node_id), length=32 bytes][random 24 byte nonce][Encrypted with the nonce and private key of the sender:[Nodes in node format, length=40 * (number of nodes (maximum of 8 nodes)) bytes][Encrypted data, length=NODES_ENCRYPTED_MESSAGE_LENGTH bytes]]
```
Send_nodes_IPv6 (response (for ipv6 addresses)):
```
[byte with value: 04][char array (client node_id), length=32 bytes][random 24 byte nonce][Encrypted with the nonce and private key of the sender:[Nodes in ipv6_node format, length=56 * (number of nodes (maximum of 8 nodes)) bytes][Encrypted data, length=NODES_ENCRYPTED_MESSAGE_LENGTH bytes]]
[byte with value: 04][char array (client node_id), length=32 bytes][random 24 byte nonce][Encrypted with the nonce and private key of the sender:[Nodes in node format, length=?? * (number of nodes (maximum of 8 nodes)) bytes][Encrypted data, length=NODES_ENCRYPTED_MESSAGE_LENGTH bytes]]
```

View File

@ -179,6 +179,105 @@ void DHT_get_shared_key_sent(DHT *dht, uint8_t *shared_key, uint8_t *client_id)
return get_shared_key(&dht->shared_keys_sent, shared_key, dht->self_secret_key, client_id);
}
void to_net_family(IP *ip)
{
if (ip->family == AF_INET)
ip->family = TOX_AF_INET;
else if (ip->family == AF_INET6)
ip->family = TOX_AF_INET6;
}
void to_host_family(IP *ip)
{
if (ip->family == TOX_AF_INET)
ip->family = AF_INET;
else if (ip->family == TOX_AF_INET6)
ip->family = AF_INET6;
}
/* Pack number of nodes into data of maxlength length.
*
* return length of packed nodes on success.
* return -1 on failure.
*/
int pack_nodes(uint8_t *data, uint16_t length, Node_format *nodes, uint16_t number)
{
uint32_t i, packed_length = 0;
for (i = 0; i < number; ++i) {
if (nodes[i].ip_port.ip.family == AF_INET) {
uint32_t size = 1 + sizeof(IP4) + sizeof(uint16_t) + CLIENT_ID_SIZE;
if (packed_length + size > length)
return -1;
data[packed_length] = TOX_AF_INET;
memcpy(data + packed_length + 1, &nodes[i].ip_port.ip.ip4, sizeof(IP4));
memcpy(data + packed_length + 1 + sizeof(IP4), &nodes[i].ip_port.port, sizeof(uint16_t));
memcpy(data + packed_length + 1 + sizeof(IP4) + sizeof(uint16_t), nodes[i].client_id, CLIENT_ID_SIZE);
packed_length += size;
} else if (nodes[i].ip_port.ip.family == AF_INET6) {
uint32_t size = 1 + sizeof(IP6) + sizeof(uint16_t) + CLIENT_ID_SIZE;
if (packed_length + size > length)
return -1;
data[packed_length] = TOX_AF_INET6;
memcpy(data + packed_length + 1, &nodes[i].ip_port.ip.ip6, sizeof(IP6));
memcpy(data + packed_length + 1 + sizeof(IP6), &nodes[i].ip_port.port, sizeof(uint16_t));
memcpy(data + packed_length + 1 + sizeof(IP6) + sizeof(uint16_t), nodes[i].client_id, CLIENT_ID_SIZE);
packed_length += size;
} else {
return -1;
}
}
return packed_length;
}
/* Unpack data of length into nodes of size (in number of nodes).
*
* return number of unpacked nodes on success.
* return -1 on failure.
*/
int unpack_nodes(Node_format *nodes, uint16_t size, uint8_t *data, uint16_t length)
{
uint32_t num = 0, len_processed = 0;
while (num < size && len_processed < length) {
if (data[len_processed] == TOX_AF_INET) {
uint32_t size = 1 + sizeof(IP4) + sizeof(uint16_t) + CLIENT_ID_SIZE;
if (len_processed + size > length)
return -1;
nodes[num].ip_port.ip.family = AF_INET;
memcpy(&nodes[num].ip_port.ip.ip4, data + len_processed + 1, sizeof(IP4));
memcpy(&nodes[num].ip_port.port, data + len_processed + 1 + sizeof(IP4), sizeof(uint16_t));
memcpy(nodes[num].client_id, data + len_processed + 1 + sizeof(IP4) + sizeof(uint16_t), CLIENT_ID_SIZE);
len_processed += size;
++num;
} else if (data[len_processed] == TOX_AF_INET6) {
uint32_t size = 1 + sizeof(IP6) + sizeof(uint16_t) + CLIENT_ID_SIZE;
if (len_processed + size > length)
return -1;
nodes[num].ip_port.ip.family = AF_INET6;
memcpy(&nodes[num].ip_port.ip.ip6, data + len_processed + 1, sizeof(IP6));
memcpy(&nodes[num].ip_port.port, data + len_processed + 1 + sizeof(IP6), sizeof(uint16_t));
memcpy(nodes[num].client_id, data + len_processed + 1 + sizeof(IP6) + sizeof(uint16_t), CLIENT_ID_SIZE);
len_processed += size;
++num;
} else {
return -1;
}
}
return num;
}
/* Check if client with client_id is already in list of length length.
* If it is then set its corresponding timestamp to current time.
@ -322,15 +421,15 @@ static uint8_t hardening_correct(Hardening *h)
/*
* helper for get_close_nodes(). argument list is a monster :D
*/
static void get_close_nodes_inner(DHT *dht, uint8_t *client_id, Node_format *nodes_list,
static void get_close_nodes_inner(uint8_t *client_id, Node_format *nodes_list,
sa_family_t sa_family, Client_data *client_list, uint32_t client_list_length,
uint32_t *num_nodes_ptr, uint8_t is_LAN, uint8_t want_good)
{
if ((sa_family != AF_INET) && (sa_family != AF_INET6))
if ((sa_family != AF_INET) && (sa_family != AF_INET6) && (sa_family != 0))
return;
uint32_t num_nodes = *num_nodes_ptr;
int ipv46x, j, closest;
int j, closest;
uint32_t i;
for (i = 0; i < client_list_length; i++) {
@ -342,39 +441,22 @@ static void get_close_nodes_inner(DHT *dht, uint8_t *client_id, Node_format *nod
IPPTsPng *ipptp = NULL;
if (sa_family == AF_INET)
if (sa_family == AF_INET) {
ipptp = &client->assoc4;
else
} else if (sa_family == AF_INET6) {
ipptp = &client->assoc6;
} else {
if (client->assoc4.timestamp >= client->assoc6.timestamp) {
ipptp = &client->assoc4;
} else {
ipptp = &client->assoc6;
}
}
/* node not in a good condition? */
if (is_timeout(ipptp->timestamp, BAD_NODE_TIMEOUT))
continue;
IP *client_ip = &ipptp->ip_port.ip;
/*
* Careful: AF_INET isn't seen as AF_INET on dual-stack sockets for
* our connections, instead we have to look if it is an embedded
* IPv4-in-IPv6 here and convert it down in sendnodes().
*/
sa_family_t ip_treat_as_family = client_ip->family;
if ((dht->net->family == AF_INET6) &&
(client_ip->family == AF_INET6)) {
/* socket is AF_INET6, address claims AF_INET6:
* check for embedded IPv4-in-IPv6 (shouldn't happen anymore,
* all storing functions should already convert down to IPv4) */
if (IN6_IS_ADDR_V4MAPPED(&client_ip->ip6.in6_addr))
ip_treat_as_family = AF_INET;
}
ipv46x = !(sa_family == ip_treat_as_family);
/* node address of the wrong family? */
if (ipv46x)
continue;
/* don't send LAN ips to non LAN peers */
if (LAN_ip(ipptp->ip_port.ip) == 0 && !is_LAN)
continue;
@ -428,7 +510,7 @@ static int get_somewhat_close_nodes(DHT *dht, uint8_t *client_id, Node_format *n
uint8_t is_LAN, uint8_t want_good)
{
uint32_t num_nodes = 0, i;
get_close_nodes_inner(dht, client_id, nodes_list, sa_family,
get_close_nodes_inner(client_id, nodes_list, sa_family,
dht->close_clientlist, LCLIENT_LIST, &num_nodes, is_LAN, want_good);
/*TODO uncomment this when hardening is added to close friend clients
@ -438,7 +520,7 @@ static int get_somewhat_close_nodes(DHT *dht, uint8_t *client_id, Node_format *n
&num_nodes, is_LAN, want_good);
*/
for (i = 0; i < dht->num_friends; ++i)
get_close_nodes_inner(dht, client_id, nodes_list, sa_family,
get_close_nodes_inner(client_id, nodes_list, sa_family,
dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS,
&num_nodes, is_LAN, 0);
@ -456,6 +538,7 @@ int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_fa
return get_somewhat_close_nodes(dht, client_id, nodes_list, sa_family, is_LAN, want_good);
#ifdef ENABLE_ASSOC_DHT
//TODO: assoc, sa_family 0 (don't care if ipv4 or ipv6) support.
Client_data *result[MAX_SENT_NODES];
Assoc_close_entries request;
@ -907,95 +990,6 @@ static int getnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *cli
return sendpacket(dht->net, ip_port, data, sizeof(data));
}
/* Send a send nodes response. */
/* because of BINARY compatibility, the Node_format MUST BE Node4_format,
* IPv6 nodes are sent in a different message
* encrypted_data must be of size NODES_ENCRYPTED_MESSAGE_LENGTH */
static int sendnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *client_id, uint8_t *encrypted_data,
uint8_t *shared_encryption_key)
{
/* Check if packet is going to be sent to ourself. */
if (id_equal(public_key, dht->self_public_key))
return -1;
size_t Node4_format_size = sizeof(Node4_format);
uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES
+ Node4_format_size * MAX_SENT_NODES + NODES_ENCRYPTED_MESSAGE_LENGTH + crypto_box_MACBYTES];
Node_format nodes_list[MAX_SENT_NODES];
uint32_t num_nodes = get_close_nodes(dht, client_id, nodes_list, AF_INET, LAN_ip(ip_port.ip) == 0, 1);
if (num_nodes == 0)
return 0;
uint8_t plain[Node4_format_size * MAX_SENT_NODES + NODES_ENCRYPTED_MESSAGE_LENGTH];
uint8_t encrypt[Node4_format_size * MAX_SENT_NODES + NODES_ENCRYPTED_MESSAGE_LENGTH + crypto_box_MACBYTES];
uint8_t nonce[crypto_box_NONCEBYTES];
new_nonce(nonce);
Node4_format *nodes4_list = (Node4_format *)(plain);
uint32_t i, num_nodes_ok = 0;
for (i = 0; i < num_nodes; i++) {
memcpy(nodes4_list[num_nodes_ok].client_id, nodes_list[i].client_id, CLIENT_ID_SIZE);
nodes4_list[num_nodes_ok].ip_port.port = nodes_list[i].ip_port.port;
IP *node_ip = &nodes_list[i].ip_port.ip;
if ((node_ip->family == AF_INET6) && IN6_IS_ADDR_V4MAPPED(&node_ip->ip6.in6_addr))
/* embedded IPv4-in-IPv6 address: return it in regular sendnodes packet */
nodes4_list[num_nodes_ok].ip_port.ip.uint32 = node_ip->ip6.uint32[3];
else if (node_ip->family == AF_INET)
nodes4_list[num_nodes_ok].ip_port.ip.uint32 = node_ip->ip4.uint32;
else /* shouldn't happen */
continue;
num_nodes_ok++;
}
if (num_nodes_ok < num_nodes) {
/* shouldn't happen */
num_nodes = num_nodes_ok;
}
memcpy(plain + num_nodes * Node4_format_size, encrypted_data, NODES_ENCRYPTED_MESSAGE_LENGTH);
int len = encrypt_data_fast( shared_encryption_key,
nonce,
plain,
num_nodes * Node4_format_size + NODES_ENCRYPTED_MESSAGE_LENGTH,
encrypt );
if ((unsigned int)len != num_nodes * Node4_format_size + NODES_ENCRYPTED_MESSAGE_LENGTH +
crypto_box_MACBYTES)
return -1;
data[0] = NET_PACKET_SEND_NODES;
memcpy(data + 1, dht->self_public_key, CLIENT_ID_SIZE);
memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES);
memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len);
return sendpacket(dht->net, ip_port, data, 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + len);
}
void to_net_family(IP *ip)
{
ip->padding[0] = 0;
ip->padding[1] = 0;
ip->padding[2] = 0;
if (ip->family == AF_INET)
ip->family = TOX_AF_INET;
else if (ip->family == AF_INET6)
ip->family = TOX_AF_INET6;
}
void to_host_family(IP *ip)
{
if (ip->family == TOX_AF_INET)
ip->family = AF_INET;
else if (ip->family == TOX_AF_INET6)
ip->family = AF_INET6;
}
/* Send a send nodes response: message for IPv6 nodes */
static int sendnodes_ipv6(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *client_id, uint8_t *encrypted_data,
uint8_t *shared_encryption_key)
@ -1009,7 +1003,7 @@ static int sendnodes_ipv6(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_
+ Node_format_size * MAX_SENT_NODES + NODES_ENCRYPTED_MESSAGE_LENGTH + crypto_box_MACBYTES];
Node_format nodes_list[MAX_SENT_NODES];
uint32_t num_nodes = get_close_nodes(dht, client_id, nodes_list, AF_INET6, LAN_ip(ip_port.ip) == 0, 1);
uint32_t num_nodes = get_close_nodes(dht, client_id, nodes_list, 0, LAN_ip(ip_port.ip) == 0, 1);
if (num_nodes == 0)
return 0;
@ -1019,20 +1013,19 @@ static int sendnodes_ipv6(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_
uint8_t nonce[crypto_box_NONCEBYTES];
new_nonce(nonce);
uint32_t i;
int nodes_length = pack_nodes(plain, Node_format_size * MAX_SENT_NODES, nodes_list, num_nodes);
for (i = 0; i < num_nodes; ++i)
to_net_family(&nodes_list[i].ip_port.ip);
if (nodes_length <= 0)
return -1;
memcpy(plain, nodes_list, num_nodes * Node_format_size);
memcpy(plain + num_nodes * Node_format_size, encrypted_data, NODES_ENCRYPTED_MESSAGE_LENGTH);
memcpy(plain + nodes_length, encrypted_data, NODES_ENCRYPTED_MESSAGE_LENGTH);
int len = encrypt_data_fast( shared_encryption_key,
nonce,
plain,
num_nodes * Node_format_size + NODES_ENCRYPTED_MESSAGE_LENGTH,
nodes_length + NODES_ENCRYPTED_MESSAGE_LENGTH,
encrypt );
if ((unsigned int)len != num_nodes * Node_format_size + NODES_ENCRYPTED_MESSAGE_LENGTH + crypto_box_MACBYTES)
if ((unsigned int)len != nodes_length + NODES_ENCRYPTED_MESSAGE_LENGTH + crypto_box_MACBYTES)
return -1;
data[0] = NET_PACKET_SEND_NODES_IPV6;
@ -1068,12 +1061,9 @@ static int handle_getnodes(void *object, IP_Port source, uint8_t *packet, uint32
if (len != CLIENT_ID_SIZE + NODES_ENCRYPTED_MESSAGE_LENGTH)
return 1;
sendnodes(dht, source, packet + 1, plain, plain + CLIENT_ID_SIZE, shared_key);
sendnodes_ipv6(dht, source, packet + 1, plain,
plain + CLIENT_ID_SIZE, shared_key); /* TODO: prevent possible amplification attacks */
sendnodes_ipv6(dht, source, packet + 1, plain, plain + CLIENT_ID_SIZE, shared_key);
add_to_ping(dht->ping, packet + 1, source);
//send_ping_request(dht, source, packet + 1); /* TODO: make this smarter? */
return 0;
}
@ -1108,15 +1098,12 @@ static uint8_t sent_getnode_to_node(DHT *dht, uint8_t *client_id, IP_Port node_i
}
/* Function is needed in following functions. */
static int send_hardening_getnode_res(DHT *dht, Node_format *sendto, uint8_t *queried_client_id, Node_format *list,
uint16_t num_nodes);
static int send_hardening_getnode_res(DHT *dht, Node_format *sendto, uint8_t *queried_client_id, uint8_t *nodes_data,
uint16_t nodes_data_length);
static int handle_sendnodes_core(void *object, IP_Port source, uint8_t *packet, uint32_t length,
size_t node_format_size, uint8_t *plain, uint16_t plain_length, uint32_t *num_nodes_out, Node_format *sendback_node)
Node_format *plain_nodes, uint16_t size_plain_nodes, uint32_t *num_nodes_out)
{
if (plain_length != MAX_SENT_NODES * node_format_size + NODES_ENCRYPTED_MESSAGE_LENGTH)
return 1;
DHT *dht = object;
uint32_t cid_size = 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + NODES_ENCRYPTED_MESSAGE_LENGTH + crypto_box_MACBYTES;
@ -1125,27 +1112,33 @@ static int handle_sendnodes_core(void *object, IP_Port source, uint8_t *packet,
uint32_t data_size = length - cid_size;
if ((data_size % node_format_size) != 0) /* invalid length */
if (data_size == 0)
return 1;
uint32_t num_nodes = data_size / node_format_size;
if (num_nodes > MAX_SENT_NODES) /* too long */
if (data_size > sizeof(Node_format) * MAX_SENT_NODES) /* invalid length */
return 1;
uint8_t plain[data_size + NODES_ENCRYPTED_MESSAGE_LENGTH];
uint8_t shared_key[crypto_box_BEFORENMBYTES];
DHT_get_shared_key_sent(dht, shared_key, packet + 1);
int len = decrypt_data_fast(
shared_key,
packet + 1 + CLIENT_ID_SIZE,
packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES,
num_nodes * node_format_size + NODES_ENCRYPTED_MESSAGE_LENGTH + crypto_box_MACBYTES,
data_size + NODES_ENCRYPTED_MESSAGE_LENGTH + crypto_box_MACBYTES,
plain);
if ((unsigned int)len != num_nodes * node_format_size + NODES_ENCRYPTED_MESSAGE_LENGTH)
if ((unsigned int)len != sizeof(plain))
return 1;
if (!sent_getnode_to_node(dht, packet + 1, source, plain + num_nodes * node_format_size, sendback_node))
Node_format sendback_node;
if (!sent_getnode_to_node(dht, packet + 1, source, plain + data_size, &sendback_node))
return 1;
int num_nodes = unpack_nodes(plain_nodes, size_plain_nodes, plain, data_size);
if (num_nodes <= 0)
return 1;
/* store the address the *request* was sent to */
@ -1153,79 +1146,28 @@ static int handle_sendnodes_core(void *object, IP_Port source, uint8_t *packet,
*num_nodes_out = num_nodes;
return 0;
}
static int handle_sendnodes(void *object, IP_Port source, uint8_t *packet, uint32_t length)
{
DHT *dht = object;
size_t node4_format_size = sizeof(Node4_format);
uint8_t plain[node4_format_size * MAX_SENT_NODES + NODES_ENCRYPTED_MESSAGE_LENGTH];
uint32_t num_nodes;
Node_format sendback_node;
if (handle_sendnodes_core(object, source, packet, length, node4_format_size, plain, sizeof(plain), &num_nodes,
&sendback_node))
return 1;
if (num_nodes == 0)
return 0;
Node4_format *nodes4_list = (Node4_format *)(plain);
uint64_t time_now = unix_time();
IPPTs ippts;
ippts.ip_port.ip.family = AF_INET;
ippts.timestamp = time_now;
uint32_t i;
Node_format nodes_list[MAX_SENT_NODES];
for (i = 0; i < num_nodes; i++)
if ((nodes4_list[i].ip_port.ip.uint32 != 0) && (nodes4_list[i].ip_port.ip.uint32 != (uint32_t)~0)) {
ippts.ip_port.ip.ip4.uint32 = nodes4_list[i].ip_port.ip.uint32;
ippts.ip_port.port = nodes4_list[i].ip_port.port;
send_ping_request(dht->ping, ippts.ip_port, nodes4_list[i].client_id);
returnedip_ports(dht, ippts.ip_port, nodes4_list[i].client_id, packet + 1);
memcpy(nodes_list[i].client_id, nodes4_list[i].client_id, CLIENT_ID_SIZE);
ipport_copy(&nodes_list[i].ip_port, &ippts.ip_port);
}
send_hardening_getnode_res(dht, &sendback_node, packet + 1, nodes_list, num_nodes);
send_hardening_getnode_res(dht, &sendback_node, packet + 1, plain, data_size);
return 0;
}
static int handle_sendnodes_ipv6(void *object, IP_Port source, uint8_t *packet, uint32_t length)
{
DHT *dht = object;
size_t node_format_size = sizeof(Node_format);
uint8_t plain[node_format_size * MAX_SENT_NODES + NODES_ENCRYPTED_MESSAGE_LENGTH];
Node_format plain_nodes[MAX_SENT_NODES];
uint32_t num_nodes;
Node_format sendback_node;
if (handle_sendnodes_core(object, source, packet, length, node_format_size, plain, sizeof(plain), &num_nodes,
&sendback_node))
if (handle_sendnodes_core(object, source, packet, length, plain_nodes, MAX_SENT_NODES, &num_nodes))
return 1;
if (num_nodes == 0)
return 0;
Node_format *nodes_list = (Node_format *)(plain);
uint32_t i;
send_hardening_getnode_res(dht, &sendback_node, packet + 1, nodes_list, num_nodes);
for (i = 0; i < num_nodes; i++) {
to_host_family(&nodes_list[i].ip_port.ip);
if (ipport_isset(&nodes_list[i].ip_port)) {
send_ping_request(dht->ping, nodes_list[i].ip_port, nodes_list[i].client_id);
returnedip_ports(dht, nodes_list[i].ip_port, nodes_list[i].client_id, packet + 1);
if (ipport_isset(&plain_nodes[i].ip_port)) {
send_ping_request(dht->ping, plain_nodes[i].ip_port, plain_nodes[i].client_id);
returnedip_ports(dht, plain_nodes[i].ip_port, plain_nodes[i].client_id, packet + 1);
}
}
@ -1977,17 +1919,17 @@ static int send_hardening_getnode_req(DHT *dht, Node_format *dest, Node_format *
}
/* Send a get node hardening response */
static int send_hardening_getnode_res(DHT *dht, Node_format *sendto, uint8_t *queried_client_id, Node_format *list,
uint16_t num_nodes)
static int send_hardening_getnode_res(DHT *dht, Node_format *sendto, uint8_t *queried_client_id, uint8_t *nodes_data,
uint16_t nodes_data_length)
{
if (!ip_isset(&sendto->ip_port.ip))
return -1;
uint8_t packet[MAX_DATA_SIZE];
uint8_t data[1 + CLIENT_ID_SIZE + num_nodes * sizeof(Node_format)];
uint8_t data[1 + CLIENT_ID_SIZE + nodes_data_length];
data[0] = CHECK_TYPE_GETNODE_RES;
memcpy(data + 1, queried_client_id, CLIENT_ID_SIZE);
memcpy(data + 1 + CLIENT_ID_SIZE, list, num_nodes * sizeof(Node_format));
memcpy(data + 1 + CLIENT_ID_SIZE, nodes_data, nodes_data_length);
int len = create_request(dht->self_public_key, dht->self_secret_key, packet, sendto->client_id, data,
sizeof(data), CRYPTO_PACKET_HARDENING);
@ -2075,28 +2017,22 @@ static int handle_hardening(void *object, IP_Port source, uint8_t *source_pubkey
if (length <= CLIENT_ID_SIZE + 1)
return 1;
if ((length - 1 - CLIENT_ID_SIZE) % sizeof(Node_format) != 0)
if (length > 1 + CLIENT_ID_SIZE + sizeof(Node_format) * MAX_SENT_NODES)
return 1;
uint16_t num = (length - 1 - CLIENT_ID_SIZE) / sizeof(Node_format);
uint16_t length_nodes = length - 1 - CLIENT_ID_SIZE;
Node_format nodes[MAX_SENT_NODES];
int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, packet + 1 + CLIENT_ID_SIZE, length_nodes);
/* TODO: MAX_SENT_NODES nodes should be returned at all times
(right now we have a small network size so it could cause problems for testing and etc..) */
if (num > MAX_SENT_NODES || num == 0)
if (num_nodes <= 0)
return 1;
Node_format nodes[num];
memcpy(nodes, packet + 1 + CLIENT_ID_SIZE, sizeof(Node_format)*num);
uint32_t i;
for (i = 0; i < num; ++i)
to_host_family(&nodes[i].ip_port.ip);
/* NOTE: This should work for now but should be changed to something better. */
if (have_nodes_closelist(dht, nodes, num) < (uint32_t)((num + 2) / 2))
if (have_nodes_closelist(dht, nodes, num_nodes) < (uint32_t)((num_nodes + 2) / 2))
return 1;
IPPTsPng *temp = get_closelist_IPPTsPng(dht, packet + 1, nodes[0].ip_port.ip.family);
if (temp == NULL)
@ -2329,7 +2265,6 @@ DHT *new_DHT(Net_Crypto *c)
}
networking_registerhandler(dht->net, NET_PACKET_GET_NODES, &handle_getnodes, dht);
networking_registerhandler(dht->net, NET_PACKET_SEND_NODES, &handle_sendnodes, dht);
networking_registerhandler(dht->net, NET_PACKET_SEND_NODES_IPV6, &handle_sendnodes_ipv6, dht);
init_cryptopackets(dht);
cryptopacket_registerhandler(c, CRYPTO_PACKET_NAT_PING, &handle_NATping, dht);

View File

@ -52,6 +52,8 @@
/* Redefinitions of variables for safe transfer over wire. */
#define TOX_AF_INET 2
#define TOX_AF_INET6 10
#define TOX_TCP_INET 130
#define TOX_TCP_INET6 138
/* The number of "fake" friends to add (for optimization purposes and so our paths for the onion part are more random) */
#define DHT_FAKE_FRIEND_NUMBER 4
@ -128,16 +130,27 @@ typedef struct {
NAT nat;
} DHT_Friend;
/* this must be kept even if IP_Port is expanded: wire compatibility */
typedef struct {
uint8_t client_id[CLIENT_ID_SIZE];
IP4_Port ip_port;
} Node4_format;
typedef struct {
typedef struct __attribute__ ((__packed__))
{
uint8_t client_id[CLIENT_ID_SIZE];
IP_Port ip_port;
} Node_format;
}
Node_format;
/* Pack number of nodes into data of maxlength length.
*
* return length of packed nodes on success.
* return -1 on failure.
*/
int pack_nodes(uint8_t *data, uint16_t length, Node_format *nodes, uint16_t number);
/* Unpack data of length into nodes of size (in number of nodes).
*
* return number of unpacked nodes on success.
* return -1 on failure.
*/
int unpack_nodes(Node_format *nodes, uint16_t size, uint8_t *data, uint16_t length);
/*----------------------------------------------------------------------------------*/
/* struct to store some shared keys so we don't have to regenerate them for each request. */
@ -251,7 +264,7 @@ int id_closest(uint8_t *id, uint8_t *id1, uint8_t *id2);
/* Get the (maximum MAX_SENT_NODES) closest nodes to client_id we know
* and put them in nodes_list (must be MAX_SENT_NODES big).
*
* sa_family = family (IPv4 or IPv6)?
* sa_family = family (IPv4 or IPv6) (0 if we don't care)?
* is_LAN = return some LAN ips (true or false)
* want_good = do we want tested nodes or not? (TODO)
*

View File

@ -161,9 +161,6 @@ typedef int sock_t;
#define TOX_PORTRANGE_TO 33545
#define TOX_PORT_DEFAULT TOX_PORTRANGE_FROM
/* TODO: remove padding bytes next time we need to break compatibility with old versions of core. */
typedef union __attribute__ ((__packed__))
{
uint8_t uint8[4];
@ -186,8 +183,6 @@ IP6;
typedef struct __attribute__ ((__packed__))
{
uint8_t family;
/* Not used for anything right now. */
uint8_t padding[3];
union {
IP4 ip4;
IP6 ip6;
@ -195,22 +190,9 @@ typedef struct __attribute__ ((__packed__))
}
IP;
typedef union __attribute__ ((__packed__))
{
struct {
IP4 ip;
uint16_t port;
/* Not used for anything right now. */
uint16_t padding;
};
uint8_t uint8[8];
}
IP4_Port;
typedef struct __attribute__ ((__packed__)) IP_Port {
IP ip;
uint16_t port;
uint16_t padding;
} IP_Port;
#define TOX_ENABLE_IPV6_DEFAULT 1

View File

@ -247,14 +247,8 @@ static int handle_announce_request(void *object, IP_Port source, uint8_t *packet
/*Respond with a announce response packet*/
Node_format nodes_list[MAX_SENT_NODES];
uint32_t num_nodes = get_close_nodes(onion_a->dht, plain + ONION_PING_ID_SIZE, nodes_list, source.ip.family,
LAN_ip(source.ip) == 0, 1);
uint32_t i;
for (i = 0; i < num_nodes; ++i)
to_net_family(&nodes_list[i].ip_port.ip);
uint32_t num_nodes = get_close_nodes(onion_a->dht, plain + ONION_PING_ID_SIZE, nodes_list, 0, LAN_ip(source.ip) == 0,
1);
uint8_t nonce[crypto_box_NONCEBYTES];
random_nonce(nonce);
@ -274,13 +268,16 @@ static int handle_announce_request(void *object, IP_Port source, uint8_t *packet
}
}
memcpy(pl + 1 + ONION_PING_ID_SIZE, nodes_list, num_nodes * sizeof(Node_format));
int nodes_length = pack_nodes(pl + 1 + ONION_PING_ID_SIZE, sizeof(nodes_list), nodes_list, num_nodes);
if (nodes_length <= 0)
return 1;
uint8_t data[ONION_ANNOUNCE_RESPONSE_MAX_SIZE];
len = encrypt_data_fast(shared_key, nonce, pl, 1 + ONION_PING_ID_SIZE + num_nodes * sizeof(Node_format),
len = encrypt_data_fast(shared_key, nonce, pl, 1 + ONION_PING_ID_SIZE + nodes_length,
data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + crypto_box_NONCEBYTES);
if ((uint32_t)len != 1 + ONION_PING_ID_SIZE + num_nodes * sizeof(Node_format) + crypto_box_MACBYTES)
if (len != 1 + ONION_PING_ID_SIZE + nodes_length + crypto_box_MACBYTES)
return 1;
data[0] = NET_PACKET_ANNOUNCE_RESPONSE;

View File

@ -345,8 +345,6 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, Node_format *n
if (*ping_nodes_sent_second > MAX_PING_NODES_SECOND_PEER)
return 0;
to_host_family(&nodes[i].ip_port.ip);
if (!lan_ips_accepted)
if (LAN_ip(nodes[i].ip_port.ip) == 0)
continue;
@ -377,10 +375,7 @@ static int handle_announce_response(void *object, IP_Port source, uint8_t *packe
if (length < ONION_ANNOUNCE_RESPONSE_MIN_SIZE || length > ONION_ANNOUNCE_RESPONSE_MAX_SIZE)
return 1;
if ((length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE) % sizeof(Node_format) != 0)
return 1;
uint16_t num_nodes = (length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE) / sizeof(Node_format);
uint16_t len_nodes = length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE;
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
IP_Port ip_port;
@ -389,7 +384,7 @@ static int handle_announce_response(void *object, IP_Port source, uint8_t *packe
if (num > onion_c->num_friends)
return 1;
uint8_t plain[1 + ONION_PING_ID_SIZE + num_nodes * sizeof(Node_format)];
uint8_t plain[1 + ONION_PING_ID_SIZE + len_nodes];
int len = -1;
if (num == 0) {
@ -409,15 +404,19 @@ static int handle_announce_response(void *object, IP_Port source, uint8_t *packe
if ((uint32_t)len != sizeof(plain))
return 1;
if (client_add_to_list(onion_c, num, public_key, ip_port, plain[0], plain + 1, source) == -1)
return 1;
Node_format nodes[MAX_SENT_NODES];
memcpy(nodes, plain + 1 + ONION_PING_ID_SIZE, num_nodes * sizeof(Node_format));
if (len_nodes != 0) {
Node_format nodes[MAX_SENT_NODES];
int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, plain + 1 + ONION_PING_ID_SIZE, len_nodes);
if (client_ping_nodes(onion_c, num, nodes, num_nodes, source) == -1)
return 1;
if (num_nodes <= 0)
return 1;
if (client_ping_nodes(onion_c, num, nodes, num_nodes, source) == -1)
return 1;
}
return 0;
}
@ -469,9 +468,6 @@ static int handle_fakeid_announce(void *object, uint8_t *source_pubkey, uint8_t
if (length > FAKEID_DATA_MAX_LENGTH)
return 1;
if ((length - FAKEID_DATA_MIN_LENGTH) % sizeof(Node_format) != 0)
return 1;
int friend_num = onion_friend_num(onion_c, source_pubkey);
if (friend_num == -1)
@ -500,14 +496,20 @@ static int handle_fakeid_announce(void *object, uint8_t *source_pubkey, uint8_t
memcpy(onion_c->friends_list[friend_num].fake_client_id, data + 1 + sizeof(uint64_t), crypto_box_PUBLICKEYBYTES);
}
uint16_t num_nodes = (length - FAKEID_DATA_MIN_LENGTH) / sizeof(Node_format);
Node_format nodes[num_nodes];
memcpy(nodes, data + 1 + sizeof(uint64_t) + crypto_box_PUBLICKEYBYTES, sizeof(nodes));
uint32_t i;
uint16_t len_nodes = length - FAKEID_DATA_MIN_LENGTH;
for (i = 0; i < num_nodes; ++i) {
to_host_family(&nodes[i].ip_port.ip);
DHT_getnodes(onion_c->dht, &nodes[i].ip_port, nodes[i].client_id, onion_c->friends_list[friend_num].fake_client_id);
if (len_nodes != 0) {
Node_format nodes[MAX_SENT_NODES];
int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, data + 1 + sizeof(uint64_t) + crypto_box_PUBLICKEYBYTES, len_nodes);
if (num_nodes <= 0)
return 1;
int i;
for (i = 0; i < num_nodes; ++i) {
DHT_getnodes(onion_c->dht, &nodes[i].ip_port, nodes[i].client_id, onion_c->friends_list[friend_num].fake_client_id);
}
}
return 0;
@ -658,19 +660,19 @@ static int send_fakeid_announce(Onion_Client *onion_c, uint16_t friend_num, uint
memcpy(data + 1 + sizeof(uint64_t), onion_c->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
Node_format nodes[MAX_SENT_NODES];
uint16_t num_nodes = closelist_nodes(onion_c->dht, nodes, MAX_SENT_NODES);
uint32_t i;
int nodes_len = pack_nodes(data + FAKEID_DATA_MIN_LENGTH, FAKEID_DATA_MAX_LENGTH - FAKEID_DATA_MIN_LENGTH, nodes,
num_nodes);
for (i = 0; i < num_nodes; ++i)
to_net_family(&nodes[i].ip_port.ip);
if (nodes_len <= 0)
return -1;
memcpy(data + FAKEID_DATA_MIN_LENGTH, nodes, sizeof(Node_format) * num_nodes);
int num1 = -1, num2 = -1;
if (onion_dht_both != 1)
num1 = send_onion_data(onion_c, friend_num, data, FAKEID_DATA_MIN_LENGTH + sizeof(Node_format) * num_nodes);
num1 = send_onion_data(onion_c, friend_num, data, FAKEID_DATA_MIN_LENGTH + nodes_len);
if (onion_dht_both != 0)
num2 = send_dht_fakeid(onion_c, friend_num, data, FAKEID_DATA_MIN_LENGTH + sizeof(Node_format) * num_nodes);
num2 = send_dht_fakeid(onion_c, friend_num, data, FAKEID_DATA_MIN_LENGTH + nodes_len);
if (num1 == -1)
return num2;