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