/* Add nodes to the toping list. * All nodes in this list are pinged every TIME_TOPING seconds * and are then removed from the list. * If the list is full the nodes farthest from our client_id are replaced. * The purpose of this list is to enable quick integration of new nodes into the * network while preventing amplification attacks. * * return 0 if node was added. * return -1 if node was not added. */ int add_toping(PING *ping, size_t *client_id, IP_Port ip_port) { if (!ip_isset(&ip_port.ip)) return -1; size_t i; for (i = 0; i < MAX_TOPING; ++i) { if (!ip_isset(&ping->toping[i].ip_port.ip)) { memcpy(ping->toping[i].client_id, client_id, CLIENT_ID_SIZE); ipport_copy(&ping->toping[i].ip_port, &ip_port); return 0; } } for (i = 0; i < MAX_TOPING; ++i) { if (id_closest(ping->dht->self_public_key, ping->toping[i].client_id, client_id) == 2) { memcpy(ping->toping[i].client_id, client_id, CLIENT_ID_SIZE); ipport_copy(&ping->toping[i].ip_port, &ip_port); return 0; } } return -1; }
int send_LANdiscovery(uint16_t port, DHT *dht) { uint8_t data[crypto_box_PUBLICKEYBYTES + 1]; data[0] = NET_PACKET_LAN_DISCOVERY; id_copy(data + 1, dht->self_public_key); #ifdef __linux send_broadcasts(dht->net, port, data, 1 + crypto_box_PUBLICKEYBYTES); #endif int res = -1; IP_Port ip_port; ip_port.port = port; /* IPv6 multicast */ if (dht->net->family == AF_INET6) { ip_port.ip = broadcast_ip(AF_INET6, AF_INET6); if (ip_isset(&ip_port.ip)) if (sendpacket(dht->net, ip_port, data, 1 + crypto_box_PUBLICKEYBYTES) > 0) res = 1; } /* IPv4 broadcast (has to be IPv4-in-IPv6 mapping if socket is AF_INET6 */ ip_port.ip = broadcast_ip(dht->net->family, AF_INET); if (ip_isset(&ip_port.ip)) if (sendpacket(dht->net, ip_port, data, 1 + crypto_box_PUBLICKEYBYTES)) res = 1; return res; }
/* Add nodes to the to_ping list. * All nodes in this list are pinged every TIME_TO_PING seconds * and are then removed from the list. * If the list is full the nodes farthest from our client_id are replaced. * The purpose of this list is to enable quick integration of new nodes into the * network while preventing amplification attacks. * * return 0 if node was added. * return -1 if node was not added. */ int add_to_ping(PING *ping, uint8_t *client_id, IP_Port ip_port) { if (!ip_isset(&ip_port.ip)) return -1; if (in_list(ping->dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) return -1; uint32_t i; for (i = 0; i < MAX_TO_PING; ++i) { if (!ip_isset(&ping->to_ping[i].ip_port.ip)) { memcpy(ping->to_ping[i].client_id, client_id, CLIENT_ID_SIZE); ipport_copy(&ping->to_ping[i].ip_port, &ip_port); return 0; } if (memcmp(ping->to_ping[i].client_id, client_id, CLIENT_ID_SIZE) == 0) { return -1; } } uint32_t r = rand(); for (i = 0; i < MAX_TO_PING; ++i) { if (id_closest(ping->dht->self_public_key, ping->to_ping[(i + r) % MAX_TO_PING].client_id, client_id) == 2) { memcpy(ping->to_ping[(i + r) % MAX_TO_PING].client_id, client_id, CLIENT_ID_SIZE); ipport_copy(&ping->to_ping[(i + r) % MAX_TO_PING].ip_port, &ip_port); return 0; } } return -1; }
/* Add nodes to the to_ping list. * All nodes in this list are pinged every TIME_TO_PING seconds * and are then removed from the list. * If the list is full the nodes farthest from our public_key are replaced. * The purpose of this list is to enable quick integration of new nodes into the * network while preventing amplification attacks. * * return 0 if node was added. * return -1 if node was not added. */ int add_to_ping(PING *ping, const uint8_t *public_key, IP_Port ip_port) { if (!ip_isset(&ip_port.ip)) return -1; if (in_list(ping->dht->close_clientlist, LCLIENT_LIST, public_key, ip_port)) return -1; uint32_t i; for (i = 0; i < MAX_TO_PING; ++i) { if (!ip_isset(&ping->to_ping[i].ip_port.ip)) { memcpy(ping->to_ping[i].public_key, public_key, crypto_box_PUBLICKEYBYTES); ipport_copy(&ping->to_ping[i].ip_port, &ip_port); return 0; } if (memcmp(ping->to_ping[i].public_key, public_key, crypto_box_PUBLICKEYBYTES) == 0) { return -1; } } uint32_t r = rand(); for (i = 0; i < MAX_TO_PING; ++i) { if (id_closest(ping->dht->self_public_key, ping->to_ping[(i + r) % MAX_TO_PING].public_key, public_key) == 2) { memcpy(ping->to_ping[(i + r) % MAX_TO_PING].public_key, public_key, crypto_box_PUBLICKEYBYTES); ipport_copy(&ping->to_ping[(i + r) % MAX_TO_PING].ip_port, &ip_port); return 0; } } return -1; }
int lan_discovery_send(uint16_t port, DHT *dht) { uint8_t data[CRYPTO_PUBLIC_KEY_SIZE + 1]; data[0] = NET_PACKET_LAN_DISCOVERY; id_copy(data + 1, dht_get_self_public_key(dht)); send_broadcasts(dht_get_net(dht), port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE); int res = -1; IP_Port ip_port; ip_port.port = port; /* IPv6 multicast */ if (net_family(dht_get_net(dht)) == TOX_AF_INET6) { ip_port.ip = broadcast_ip(TOX_AF_INET6, TOX_AF_INET6); if (ip_isset(&ip_port.ip)) { if (sendpacket(dht_get_net(dht), ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) { res = 1; } } } /* IPv4 broadcast (has to be IPv4-in-IPv6 mapping if socket is TOX_AF_INET6 */ ip_port.ip = broadcast_ip(net_family(dht_get_net(dht)), TOX_AF_INET); if (ip_isset(&ip_port.ip)) { if (sendpacket(dht_get_net(dht), ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE)) { res = 1; } } return res; }
/* Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds. * This function must be run at least once every TIME_TO_PING seconds. */ void do_to_ping(PING *ping) { if (!is_timeout(ping->last_to_ping, TIME_TO_PING)) { return; } if (!ip_isset(&ping->to_ping[0].ip_port.ip)) { return; } unsigned int i; for (i = 0; i < MAX_TO_PING; ++i) { if (!ip_isset(&ping->to_ping[i].ip_port.ip)) { break; } if (!node_addable_to_close_list(ping->dht, ping->to_ping[i].public_key, ping->to_ping[i].ip_port)) { continue; } send_ping_request(ping, ping->to_ping[i].ip_port, ping->to_ping[i].public_key); ip_reset(&ping->to_ping[i].ip_port.ip); } if (i != 0) { ping->last_to_ping = unix_time(); } }
int send_LANdiscovery(uint16_t port, DHT *dht) { uint8_t data[CRYPTO_PUBLIC_KEY_SIZE + 1]; data[0] = NET_PACKET_LAN_DISCOVERY; id_copy(data + 1, dht->self_public_key); send_broadcasts(dht->net, port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE); int res = -1; IP_Port ip_port; ip_port.port = port; /* IPv6 multicast */ if (dht->net->family == AF_INET6) { ip_port.ip = broadcast_ip(AF_INET6, AF_INET6); if (ip_isset(&ip_port.ip)) { if (sendpacket(dht->net, ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) { res = 1; } } } /* IPv4 broadcast (has to be IPv4-in-IPv6 mapping if socket is AF_INET6 */ ip_port.ip = broadcast_ip(dht->net->family, AF_INET); if (ip_isset(&ip_port.ip)) { if (sendpacket(dht->net, ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE)) { res = 1; } } return res; }
/* checks if ip is valid */ int ipport_isset(IP_Port *ipport) { if (!ipport) return 0; if (!ipport->port) return 0; return ip_isset(&ipport->ip); };
/* Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds. * This function must be run at least once every TIME_TO_PING seconds. */ void do_to_ping(PING *ping) { if (!is_timeout(ping->last_to_ping, TIME_TO_PING)) return; if (!ip_isset(&ping->to_ping[0].ip_port.ip)) return; ping->last_to_ping = unix_time(); uint32_t i; for (i = 0; i < MAX_TO_PING; ++i) { if (!ip_isset(&ping->to_ping[i].ip_port.ip)) return; send_ping_request(ping, ping->to_ping[i].ip_port, ping->to_ping[i].client_id); ip_reset(&ping->to_ping[i].ip_port.ip); } }
/* Add nodes to the to_ping list. * All nodes in this list are pinged every TIME_TO_PING seconds * and are then removed from the list. * If the list is full the nodes farthest from our public_key are replaced. * The purpose of this list is to enable quick integration of new nodes into the * network while preventing amplification attacks. * * return 0 if node was added. * return -1 if node was not added. */ int add_to_ping(PING *ping, const uint8_t *public_key, IP_Port ip_port) { if (!ip_isset(&ip_port.ip)) { return -1; } if (!node_addable_to_close_list(ping->dht, public_key, ip_port)) { return -1; } if (in_list(ping->dht->close_clientlist, LCLIENT_LIST, public_key, ip_port)) { return -1; } IP_Port temp; if (DHT_getfriendip(ping->dht, public_key, &temp) == 0) { send_ping_request(ping, ip_port, public_key); return -1; } unsigned int i; for (i = 0; i < MAX_TO_PING; ++i) { if (!ip_isset(&ping->to_ping[i].ip_port.ip)) { memcpy(ping->to_ping[i].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); ipport_copy(&ping->to_ping[i].ip_port, &ip_port); return 0; } if (public_key_cmp(ping->to_ping[i].public_key, public_key) == 0) { return -1; } } if (add_to_list(ping->to_ping, MAX_TO_PING, public_key, ip_port, ping->dht->self_public_key)) { return 0; } return -1; }
/* Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds. * This function must be run at least once every TIME_TO_PING seconds. */ void do_to_ping(PING *ping) { if (!is_timeout(ping->last_to_ping, TIME_TO_PING)) return; if (!ip_isset(&ping->to_ping[0].ip_port.ip)) return; uint32_t i; for (i = 0; i < MAX_TO_PING; ++i) { if (!ip_isset(&ping->to_ping[i].ip_port.ip)) break; send_ping_request(ping, ping->to_ping[i].ip_port, ping->to_ping[i].public_key); ip_reset(&ping->to_ping[i].ip_port.ip); } if (i != 0) ping->last_to_ping = unix_time(); }
static bool is_pinging(PING *ping, IP_Port ipp, uint64_t ping_id) // O(n) TODO: Replace this with something else. { /* shouldn't that be an OR ? */ if (!ip_isset(&ipp.ip) && ping_id == 0) return false; size_t i, id; remove_timeouts(ping); for (i = 0; i < ping->num_pings; i++) { id = (ping->pos_pings + i) % PING_NUM_MAX; /* ping_id = 0 means match any id. */ if ((!ip_isset(&ipp.ip) || ipport_equal(&ping->pings[id].ip_port, &ipp)) && (ping->pings[id].id == ping_id || ping_id == 0)) { return true; } } return false; }
/* * Send data to all peers in close peer list. * * return the number of peers the packet was sent to. */ static uint8_t sendto_allpeers(Group_Chat *chat, uint8_t *data, uint16_t length, uint8_t request_id) { uint16_t sent = 0; uint32_t i; for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { if (ip_isset(&chat->close[i].ip_port.ip) && !is_timeout(chat->close[i].last_recv, BAD_GROUPNODE_TIMEOUT)) { if (send_groupchatpacket(chat, chat->close[i].ip_port, chat->close[i].client_id, data, length, request_id) == 0) ++sent; } } return sent; }
/* checks if ip/port or ping_id are already in the list to ping * if both are set, both must match, otherwise the set must match * * returns 0 if neither is set or no match was found * returns the (index + 1) of the match if one was found */ static int is_pinging(PING *ping, IP_Port ipp, size_t ping_id) { // O(n) TODO: Replace this with something else. /* at least one MUST be set */ size_t ip_valid = ip_isset(&ipp.ip); if (!ip_valid && !ping_id) return 0; size_t i; remove_timeouts(ping); for (i = 0; i < ping->num_pings; i++) { size_t id = (ping->pos_pings + i) % PING_NUM_MAX; if (!ping_id || (ping->pings[id].id == ping_id)) if (!ip_valid || ipport_equal(&ping->pings[id].ip_port, &ipp)) return id + 1; } return 0; }