summaryrefslogtreecommitdiff
path: root/toxcore/DHT.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxcore/DHT.c')
-rw-r--r--toxcore/DHT.c157
1 files changed, 118 insertions, 39 deletions
diff --git a/toxcore/DHT.c b/toxcore/DHT.c
index 1761f50b..ad4c8a1d 100644
--- a/toxcore/DHT.c
+++ b/toxcore/DHT.c
@@ -131,14 +131,32 @@ static int is_timeout(uint64_t time_now, uint64_t timestamp, uint64_t timeout)
131 * 131 *
132 * return True(1) or False(0) 132 * return True(1) or False(0)
133 */ 133 */
134static int client_in_list(Client_data *list, uint32_t length, uint8_t *client_id, IP_Port ip_port) 134static int client_or_ip_port_in_list(Client_data *list, uint32_t length, uint8_t *client_id, IP_Port ip_port)
135{ 135{
136 uint32_t i; 136 uint32_t i;
137 uint64_t temp_time = unix_time(); 137 uint64_t temp_time = unix_time();
138 138
139 uint8_t candropipv4 = 1;
140 if (ip_port.ip.family == AF_INET6) {
141 uint8_t ipv6cnt = 0;
142
143 /* ipv6: count how many spots are used */
144 for(i = 0; i < length; i++)
145 if (list[i].ip_port.ip.family == AF_INET6)
146 ipv6cnt++;
147
148 /* more than half the list filled with ipv6: block ipv4->ipv6 overwrite */
149 if (ipv6cnt > length / 2)
150 candropipv4 = 0;
151 }
152
139 /* if client_id is in list, find it and maybe overwrite ip_port */ 153 /* if client_id is in list, find it and maybe overwrite ip_port */
140 for (i = 0; i < length; ++i) 154 for (i = 0; i < length; ++i)
141 if (id_equal(list[i].client_id, client_id)) { 155 if (id_equal(list[i].client_id, client_id)) {
156 /* if we got "too many" ipv6 addresses already, keep the ipv4 address */
157 if (!candropipv4 && (list[i].ip_port.ip.family == AF_INET))
158 return 1;
159
142 /* Refresh the client timestamp. */ 160 /* Refresh the client timestamp. */
143 list[i].timestamp = temp_time; 161 list[i].timestamp = temp_time;
144 list[i].ip_port = ip_port; 162 list[i].ip_port = ip_port;
@@ -299,15 +317,31 @@ static int replace_bad( Client_data *list,
299 uint32_t i; 317 uint32_t i;
300 uint64_t temp_time = unix_time(); 318 uint64_t temp_time = unix_time();
301 319
320 uint8_t candropipv4 = 1;
321 if (ip_port.ip.family == AF_INET6) {
322 uint32_t ipv6cnt = 0;
323
324 /* ipv6: count how many spots are used */
325 for(i = 0; i < length; i++)
326 if (list[i].ip_port.ip.family == AF_INET6)
327 ipv6cnt++;
328
329 /* more than half the list filled with ipv6: block ipv4->ipv6 overwrite */
330 if (ipv6cnt > length / 2)
331 candropipv4 = 0;
332 }
333
302 for (i = 0; i < length; ++i) { 334 for (i = 0; i < length; ++i) {
303 /* If node is bad */ 335 /* If node is bad */
304 if (is_timeout(temp_time, list[i].timestamp, BAD_NODE_TIMEOUT)) { 336 Client_data *client = &list[i];
305 memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE); 337 if ((candropipv4 || (client->ip_port.ip.family == AF_INET6)) &&
306 list[i].ip_port = ip_port; 338 is_timeout(temp_time, client->timestamp, BAD_NODE_TIMEOUT)) {
307 list[i].timestamp = temp_time; 339 memcpy(client->client_id, client_id, CLIENT_ID_SIZE);
308 ip_reset(&list[i].ret_ip_port.ip); 340 client->ip_port = ip_port;
309 list[i].ret_ip_port.port = 0; 341 client->timestamp = temp_time;
310 list[i].ret_timestamp = 0; 342 ip_reset(&client->ret_ip_port.ip);
343 client->ret_ip_port.port = 0;
344 client->ret_timestamp = 0;
311 return 0; 345 return 0;
312 } 346 }
313 } 347 }
@@ -344,20 +378,69 @@ static int replace_good( Client_data *list,
344 IP_Port ip_port, 378 IP_Port ip_port,
345 uint8_t *comp_client_id ) 379 uint8_t *comp_client_id )
346{ 380{
347 uint32_t i;
348 uint64_t temp_time = unix_time();
349 sort_list(list, length, comp_client_id); 381 sort_list(list, length, comp_client_id);
350 382
351 for (i = 0; i < length; ++i) 383 uint8_t candropipv4 = 1;
352 if (id_closest(comp_client_id, list[i].client_id, client_id) == 2) { 384 if (ip_port.ip.family == AF_INET6) {
353 memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE); 385 uint32_t i, ipv6cnt = 0;
354 list[i].ip_port = ip_port; 386
355 list[i].timestamp = temp_time; 387 /* ipv6: count how many spots are used */
356 ip_reset(&list[i].ret_ip_port.ip); 388 for(i = 0; i < length; i++)
357 list[i].ret_ip_port.port = 0; 389 if (list[i].ip_port.ip.family == AF_INET6)
358 list[i].ret_timestamp = 0; 390 ipv6cnt++;
359 return 0; 391
392 /* more than half the list filled with ipv6: block ipv4->ipv6 overwrite */
393 if (ipv6cnt > length / 2)
394 candropipv4 = 0;
395 }
396
397 int8_t replace = -1;
398 uint32_t i;
399
400 if (candropipv4) {
401 /* either we got an ipv4 address, or we're "allowed" to push out an ipv4
402 * address in favor of an ipv6 one
403 *
404 * because the list is sorted, we can simply check the client_id at the
405 * border, either it is closer, then every other one is as well, or it is
406 * further, then it gets pushed out in favor of the new address, which
407 * will with the next sort() move to its "rightful" position
408 *
409 * CAVEAT: weirdly enough, the list is sorted DESCENDING in distance
410 * so the furthest element is the first, NOT the last (at least that's
411 * what the comment above sort_list() claims)
412 */
413 if (id_closest(comp_client_id, list[0].client_id, client_id) == 2)
414 replace = 0;
415 } else {
416 /* ipv6 case without a right to push out an ipv4: only look for ipv6
417 * addresses, the first one we find is either closer (then we can skip
418 * out like above) or further (then we can replace it, like above)
419 */
420 for (i = 0; i < length; i++) {
421 Client_data *client = &list[i];
422 if (client->ip_port.ip.family == AF_INET6) {
423 if (id_closest(comp_client_id, list[i].client_id, client_id) == 2)
424 replace = i;
425
426 break;
427 }
360 } 428 }
429 }
430
431 if (replace != -1) {
432#ifdef DEBUG
433 assert(replace >= 0 && replace < length);
434#endif
435 Client_data *client = &list[replace];
436 memcpy(client->client_id, client_id, CLIENT_ID_SIZE);
437 client->ip_port = ip_port;
438 client->timestamp = unix_time();
439 ip_reset(&client->ret_ip_port.ip);
440 client->ret_ip_port.port = 0;
441 client->ret_timestamp = 0;
442 return 0;
443 }
361 444
362 return 1; 445 return 1;
363} 446}
@@ -369,36 +452,32 @@ void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id)
369{ 452{
370 uint32_t i; 453 uint32_t i;
371 454
455 /* convert IPv4-in-IPv6 to IPv4 */
456 if ((ip_port.ip.family == AF_INET6) && IN6_IS_ADDR_V4MAPPED(&ip_port.ip.ip6.in6_addr)) {
457 ip_port.ip.family = AF_INET;
458 ip_port.ip.ip4.uint32 = ip_port.ip.ip6.uint32[3];
459 }
460
372 /* NOTE: Current behavior if there are two clients with the same id is 461 /* NOTE: Current behavior if there are two clients with the same id is
373 * to replace the first ip by the second. 462 * to replace the first ip by the second.
374 */ 463 */
375 if (!client_in_list(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) { 464 if (!client_or_ip_port_in_list(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) {
376 if (replace_bad(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) { 465 if (replace_bad(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) {
377 /* If we can't replace bad nodes we try replacing good ones. */ 466 /* If we can't replace bad nodes we try replacing good ones. */
378 replace_good( dht->close_clientlist, 467 replace_good(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port,
379 LCLIENT_LIST, 468 dht->c->self_public_key);
380 client_id,
381 ip_port,
382 dht->c->self_public_key );
383 } 469 }
384 } 470 }
385 471
386 for (i = 0; i < dht->num_friends; ++i) { 472 for (i = 0; i < dht->num_friends; ++i) {
387 if (!client_in_list( dht->friends_list[i].client_list, 473 if (!client_or_ip_port_in_list(dht->friends_list[i].client_list,
388 MAX_FRIEND_CLIENTS, 474 MAX_FRIEND_CLIENTS, client_id, ip_port)) {
389 client_id, 475
390 ip_port )) { 476 if (replace_bad(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS,
391 477 client_id, ip_port)) {
392 if (replace_bad( dht->friends_list[i].client_list,
393 MAX_FRIEND_CLIENTS,
394 client_id,
395 ip_port )) {
396 /* If we can't replace bad nodes we try replacing good ones. */ 478 /* If we can't replace bad nodes we try replacing good ones. */
397 replace_good( dht->friends_list[i].client_list, 479 replace_good(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS,
398 MAX_FRIEND_CLIENTS, 480 client_id, ip_port, dht->friends_list[i].client_id);
399 client_id,
400 ip_port,
401 dht->friends_list[i].client_id );
402 } 481 }
403 } 482 }
404 } 483 }