diff --git a/auto_tests/messenger_test.c b/auto_tests/messenger_test.c index 604e282c..1b72a92f 100644 --- a/auto_tests/messenger_test.c +++ b/auto_tests/messenger_test.c @@ -278,7 +278,8 @@ int main(int argc, char *argv[]) good_id_b = hex_string_to_bin(good_id_b_str); bad_id = hex_string_to_bin(bad_id_str); - m = initMessenger(); + /* IPv6 status from global define */ + m = initMessenger(TOX_ENABLE_IPV6_DEFAULT); /* setup a default friend and friendnum */ if (m_addfriend_norequest(m, (uint8_t *)friend_id) < 0) diff --git a/docs/TODO b/docs/TODO index 49b06690..c905f96b 100644 --- a/docs/TODO +++ b/docs/TODO @@ -22,5 +22,9 @@ Lossless UDP: [NOT STARTED] Offline messaging [NOT STARTED] Friends list syncing [IN PROGRESS] IPV6 support + [DONE] Networking + [DONE] DHT + Messenger + [NOT STARTED] Group chats + [IN PROGRESS] GUI (https://github.com/nurupo/ProjectTox-Qt-GUI) [NOT STARTED] Security audit from professionals diff --git a/other/DHT_bootstrap.c b/other/DHT_bootstrap.c index aed17020..484a511c 100644 --- a/other/DHT_bootstrap.c +++ b/other/DHT_bootstrap.c @@ -85,11 +85,27 @@ void manage_keys(DHT *dht) int main(int argc, char *argv[]) { + if (argc == 2 && !strncasecmp(argv[1], "-h", 3)) { + printf("Usage (connected) : %s [--ipv4|--ipv6] IP PORT KEY\n", argv[0]); + printf("Usage (unconnected): %s [--ipv4|--ipv6]\n", argv[0]); + exit(0); + } + + /* let user override default by cmdline */ + uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */ + int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled); + + if (argvoffset < 0) + exit(1); + /* Initialize networking - - Bind to ip 0.0.0.0:PORT */ + Bind to ip 0.0.0.0 / [::] : PORT */ IP ip; - ip.uint32 = 0; + ip_init(&ip, ipv6enabled); + DHT *dht = new_DHT(new_net_crypto(new_networking(ip, PORT))); + perror("Initialization"); + manage_keys(dht); printf("Public key: "); uint32_t i; @@ -108,18 +124,20 @@ int main(int argc, char *argv[]) fclose(file); printf("\n"); - printf("Port: %u\n", PORT); + printf("Port: %u\n", ntohs(dht->c->lossless_udp->net->port)); - perror("Initialization."); - - if (argc > 3) { + if (argc > argvoffset + 3) { printf("Trying to bootstrap into the network...\n"); - IP_Port bootstrap_info; - bootstrap_info.ip.uint32 = inet_addr(argv[1]); - bootstrap_info.port = htons(atoi(argv[2])); - uint8_t *bootstrap_key = hex_string_to_bin(argv[3]); - DHT_bootstrap(dht, bootstrap_info, bootstrap_key); + uint16_t port = htons(atoi(argv[argvoffset + 2])); + uint8_t *bootstrap_key = hex_string_to_bin(argv[argvoffset + 3]); + int res = DHT_bootstrap_from_address(dht, argv[argvoffset + 1], + ipv6enabled, port, bootstrap_key); free(bootstrap_key); + + if (!res) { + printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]); + exit(1); + } } int is_waiting_for_dht_connection = 1; @@ -134,6 +152,7 @@ int main(int argc, char *argv[]) } do_DHT(dht); + if (last_LANdiscovery + (is_waiting_for_dht_connection ? 5 : LAN_DISCOVERY_INTERVAL) < unix_time()) { send_LANdiscovery(htons(PORT), dht->c); last_LANdiscovery = unix_time(); diff --git a/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c b/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c index 0ef1f314..890085cc 100644 --- a/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c +++ b/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c @@ -305,7 +305,12 @@ struct server_conf_s configure_server(char *cfg_file) printf("bootstrap_server %d: Invalid port.\n", i); } +#ifdef TOX_ENABLE_IPV6 + server_conf.info[i].conn.ip.family = AF_INET; + server_conf.info[i].conn.ip.ip4.uint32 = resolve_addr(strcpy(tmp_ip, bs_ip)); +#else server_conf.info[i].conn.ip.uint32 = resolve_addr(strcpy(tmp_ip, bs_ip)); +#endif server_conf.info[i].conn.port = htons(bs_port); b16_to_key(strcpy(tmp_pk, bs_pk), bs_pk_p); } @@ -344,7 +349,7 @@ int main(int argc, char *argv[]) /* Initialize networking bind to ip 0.0.0.0:PORT */ IP ip; - ip.uint32 = 0; + ip_init(&ip, 0); DHT *dht = new_DHT(new_net_crypto(new_networking(ip, server_conf.port))); /* Read the config file */ printf("PID file: %s\n", server_conf.pid_file); diff --git a/testing/DHT_test.c b/testing/DHT_test.c index fce9c257..d76057c1 100644 --- a/testing/DHT_test.c +++ b/testing/DHT_test.c @@ -66,13 +66,11 @@ void print_clientlist(DHT *dht) } p_ip = dht->close_clientlist[i].ip_port; - printf("\nIP: %u.%u.%u.%u Port: %u", p_ip.ip.uint8[0], p_ip.ip.uint8[1], p_ip.ip.uint8[2], p_ip.ip.uint8[3], - ntohs(p_ip.port)); + printf("\nIP: %s Port: %u", ip_ntoa(&p_ip.ip), ntohs(p_ip.port)); printf("\nTimestamp: %llu", (long long unsigned int) dht->close_clientlist[i].timestamp); printf("\nLast pinged: %llu\n", (long long unsigned int) dht->close_clientlist[i].last_pinged); p_ip = dht->close_clientlist[i].ret_ip_port; - printf("OUR IP: %u.%u.%u.%u Port: %u\n", p_ip.ip.uint8[0], p_ip.ip.uint8[1], p_ip.ip.uint8[2], p_ip.ip.uint8[3], - ntohs(p_ip.port)); + printf("OUR IP: %s Port: %u\n", ip_ntoa(&p_ip.ip), ntohs(p_ip.port)); printf("Timestamp: %llu\n", (long long unsigned int) dht->close_clientlist[i].ret_timestamp); } } @@ -91,9 +89,8 @@ void print_friendlist(DHT *dht) printf("%c", dht->friends_list[k].client_id[j]); } - p_ip = DHT_getfriendip(dht, dht->friends_list[k].client_id); - printf("\nIP: %u.%u.%u.%u:%u", p_ip.ip.uint8[0], p_ip.ip.uint8[1], p_ip.ip.uint8[2], p_ip.ip.uint8[3], - ntohs(p_ip.port)); + int friendok = DHT_getfriendip(dht, dht->friends_list[k].client_id, &p_ip); + printf("\nIP: %s:%u", ip_ntoa(&p_ip.ip), ntohs(p_ip.port)); printf("\nCLIENTS IN LIST:\n\n"); @@ -108,13 +105,11 @@ void print_friendlist(DHT *dht) } p_ip = dht->friends_list[k].client_list[i].ip_port; - printf("\nIP: %u.%u.%u.%u:%u", p_ip.ip.uint8[0], p_ip.ip.uint8[1], p_ip.ip.uint8[2], p_ip.ip.uint8[3], - ntohs(p_ip.port)); + printf("\nIP: %s:%u", ip_ntoa(&p_ip.ip), ntohs(p_ip.port)); printf("\nTimestamp: %llu", (long long unsigned int) dht->friends_list[k].client_list[i].timestamp); printf("\nLast pinged: %llu\n", (long long unsigned int) dht->friends_list[k].client_list[i].last_pinged); p_ip = dht->friends_list[k].client_list[i].ret_ip_port; - printf("ret IP: %u.%u.%u.%u:%u\n", p_ip.ip.uint8[0], p_ip.ip.uint8[1], p_ip.ip.uint8[2], p_ip.ip.uint8[3], - ntohs(p_ip.port)); + printf("ret IP: %s:%u\n", ip_ntoa(&p_ip.ip), ntohs(p_ip.port)); printf("Timestamp: %llu\n", (long long unsigned int)dht->friends_list[k].client_list[i].ret_timestamp); } } @@ -138,19 +133,26 @@ void printpacket(uint8_t *data, uint32_t length, IP_Port ip_port) int main(int argc, char *argv[]) { + if (argc < 4) { + printf("Usage: %s [--ipv4|--ipv6] ip port public_key\n", argv[0]); + exit(0); + } + + /* let user override default by cmdline */ + uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */ + int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled); + + if (argvoffset < 0) + exit(1); + //memcpy(self_client_id, "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", 32); /* initialize networking */ /* bind to ip 0.0.0.0:PORT */ IP ip; - ip.uint32 = 0; + ip_init(&ip, ipv6enabled); DHT *dht = new_DHT(new_net_crypto(new_networking(ip, PORT))); - if (argc < 4) { - printf("usage %s ip port public_key\n", argv[0]); - exit(0); - } - new_keys(dht->c); printf("OUR ID: "); uint32_t i; @@ -170,16 +172,17 @@ int main(int argc, char *argv[]) DHT_addfriend(dht, hex_string_to_bin(temp_id)); - perror("Initialization"); - IP_Port bootstrap_ip_port; - bootstrap_ip_port.port = htons(atoi(argv[2])); - /* bootstrap_ip_port.ip.c[0] = 127; - * bootstrap_ip_port.ip.c[1] = 0; - * bootstrap_ip_port.ip.c[2] = 0; - * bootstrap_ip_port.ip.c[3] = 1; */ - bootstrap_ip_port.ip.uint32 = inet_addr(argv[1]); - DHT_bootstrap(dht, bootstrap_ip_port, hex_string_to_bin(argv[3])); + + uint16_t port = htons(atoi(argv[argvoffset + 2])); + unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]); + int res = DHT_bootstrap_from_address(dht, argv[argvoffset + 1], ipv6enabled, port, binary_string); + free(binary_string); + + if (!res) { + printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]); + return 1; + } /* IP_Port ip_port; diff --git a/testing/Lossless_UDP_testclient.c b/testing/Lossless_UDP_testclient.c index d5fb1544..3c52c6d6 100644 --- a/testing/Lossless_UDP_testclient.c +++ b/testing/Lossless_UDP_testclient.c @@ -34,6 +34,7 @@ #include "../toxcore/network.h" #include "../toxcore/Lossless_UDP.h" +#include "misc_tools.c" #ifdef WIN32 @@ -66,8 +67,7 @@ void printpacket(uint8_t *data, uint32_t length, IP_Port ip_port) void printip(IP_Port ip_port) { - printf("\nIP: %u.%u.%u.%u Port: %u", ip_port.ip.uint8[0], ip_port.ip.uint8[1], ip_port.ip.uint8[2], ip_port.ip.uint8[3], - ntohs(ip_port.port)); + printf("\nIP: %s Port: %u", ip_ntoa(&ip_port.ip), ntohs(ip_port.port)); } /* void printpackets(Data test) @@ -152,30 +152,48 @@ void printconnection(int connection_id) int main(int argc, char *argv[]) { - if (argc < 4) { - printf("usage: %s ip port filename\n", argv[0]); + /* let user override default by cmdline */ + uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */ + int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled); + + if (argvoffset < 0) + exit(1); + + if (argc < argvoffset + 4) { + printf("Usage: %s [--ipv4|--ipv6] ip port filename\n", argv[0]); exit(0); } uint8_t buffer[512]; int read; - FILE *file = fopen(argv[3], "rb"); + FILE *file = fopen(argv[argvoffset + 3], "rb"); - if (file == NULL) + if (file == NULL) { + printf("Failed to open file \"%s\".\n", argv[argvoffset + 3]); return 1; + } /* initialize networking */ /* bind to ip 0.0.0.0:PORT */ IP ip; - ip.uint32 = 0; + ip_init(&ip, ipv6enabled); + Lossless_UDP *ludp = new_lossless_udp(new_networking(ip, PORT)); perror("Initialization"); + IP_Port serverip; - serverip.ip.uint32 = inet_addr(argv[1]); - serverip.port = htons(atoi(argv[2])); + ip_init(&serverip.ip, ipv6enabled); + + if (!addr_resolve(argv[argvoffset + 1], &serverip.ip, NULL)) { + printf("Failed to convert \"%s\" into an IP address.\n", argv[argvoffset + 1]); + return 1; + } + + serverip.port = htons(atoi(argv[argvoffset + 2])); printip(serverip); + int connection = new_connection(ludp, serverip); uint64_t timer = current_time(); diff --git a/testing/Lossless_UDP_testserver.c b/testing/Lossless_UDP_testserver.c index eb506b3d..9d061c0c 100644 --- a/testing/Lossless_UDP_testserver.c +++ b/testing/Lossless_UDP_testserver.c @@ -34,6 +34,7 @@ #include "../toxcore/network.h" #include "../toxcore/Lossless_UDP.h" +#include "misc_tools.c" //Sleep function (x = milliseconds) #ifdef WIN32 @@ -147,24 +148,34 @@ void printconnection(int connection_id) int main(int argc, char *argv[]) { - if (argc < 2) { - printf("usage: %s filename\n", argv[0]); + /* let user override default by cmdline */ + uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */ + int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled); + + if (argvoffset < 0) + exit(1); + + if (argc < argvoffset + 2) { + printf("Usage: %s [--ipv4|--ipv6] filename\n", argv[0]); exit(0); } uint8_t buffer[512]; int read; - FILE *file = fopen(argv[1], "wb"); + FILE *file = fopen(argv[argvoffset + 1], "wb"); - if (file == NULL) + if (file == NULL) { + printf("Failed to open file \"%s\".\n", argv[argvoffset + 1]); return 1; + } //initialize networking //bind to ip 0.0.0.0:PORT IP ip; - ip.uint32 = 0; + ip_init(&ip, ipv6enabled); + Lossless_UDP *ludp = new_lossless_udp(new_networking(ip, PORT)); perror("Initialization"); diff --git a/testing/Messenger_test.c b/testing/Messenger_test.c index e85a85a2..7b94364a 100644 --- a/testing/Messenger_test.c +++ b/testing/Messenger_test.c @@ -95,27 +95,44 @@ void print_message(Messenger *m, int friendnumber, uint8_t *string, uint16_t len int main(int argc, char *argv[]) { - if (argc < 4 && argc != 2) { - printf("usage %s ip port public_key (of the DHT bootstrap node)\n or\n %s Save.bak\n", argv[0], argv[0]); + /* let user override default by cmdline */ + uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */ + int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled); + + if (argvoffset < 0) + exit(1); + + /* with optional --ipvx, now it can be 1-4 arguments... */ + if ((argc != argvoffset + 2) && (argc != argvoffset + 4)) { + printf("Usage: %s [--ipv4|--ipv6] ip port public_key (of the DHT bootstrap node)\n", argv[0]); + printf("or\n"); + printf(" %s [--ipv4|--ipv6] Save.bak (to read Save.bak as state file)\n", argv[0]); exit(0); } - m = initMessenger(); + m = initMessenger(ipv6enabled); if ( !m ) { fputs("Failed to allocate messenger datastructure\n", stderr); exit(0); } - if (argc > 3) { - IP_Port bootstrap_ip_port; - bootstrap_ip_port.port = htons(atoi(argv[2])); - bootstrap_ip_port.ip.uint32 = inet_addr(argv[1]); - DHT_bootstrap(m->dht, bootstrap_ip_port, hex_string_to_bin(argv[3])); + if (argc == argvoffset + 4) { + uint16_t port = htons(atoi(argv[argvoffset + 2])); + uint8_t *bootstrap_key = hex_string_to_bin(argv[argvoffset + 3]); + int res = DHT_bootstrap_from_address(m->dht, argv[argvoffset + 1], + ipv6enabled, port, bootstrap_key); + free(bootstrap_key); + + if (!res) { + printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]); + exit(1); + } } else { - FILE *file = fopen(argv[1], "rb"); + FILE *file = fopen(argv[argvoffset + 1], "rb"); if ( file == NULL ) { + printf("Failed to open \"%s\" - does it exist?\n", argv[argvoffset + 1]); return 1; } diff --git a/testing/misc_tools.c b/testing/misc_tools.c index c4dce1bb..81f5ed8a 100644 --- a/testing/misc_tools.c +++ b/testing/misc_tools.c @@ -46,3 +46,37 @@ unsigned char *hex_string_to_bin(char hex_string[]) return val; } + + +int cmdline_parsefor_ipv46(int argc, char **argv, uint8_t *ipv6enabled) +{ + int argvoffset = 0, argi; + + for (argi = 1; argi < argc; argi++) + if (!strncasecmp(argv[argi], "--ipv", 5)) { + if (argv[argi][5] && !argv[argi][6]) { + char c = argv[argi][5]; + + if (c == '4') + *ipv6enabled = 0; + else if (c == '6') + *ipv6enabled = 1; + else { + printf("Invalid argument: %s. Try --ipv4 or --ipv6!\n", argv[argi]); + return -1; + } + } else { + printf("Invalid argument: %s. Try --ipv4 or --ipv6!\n", argv[argi]); + return -1; + } + + if (argvoffset != argi - 1) { + printf("Argument must come first: %s.\n", argv[argi]); + return -1; + } + + argvoffset++; + } + + return argvoffset; +}; diff --git a/testing/nTox.c b/testing/nTox.c index 8c87c20d..029d62b4 100644 --- a/testing/nTox.c +++ b/testing/nTox.c @@ -292,7 +292,7 @@ void line_eval(Tox *m, char *line) int num = atoi(numstring); - if (tox_sendmessage(m, num, (uint8_t *) message, strlen(message) + 1) != 1) { + if (tox_sendmessage(m, num, (uint8_t *) message, strlen(message) + 1) < 1) { new_lines("[i] could not send message"); } else { new_lines(format_message(m, message, -1)); @@ -598,36 +598,36 @@ void print_groupmessage(Tox *m, int groupnumber, uint8_t *message, uint16_t leng int main(int argc, char *argv[]) { + if (argc < 4) { + printf("Usage: %s [--ipv4|--ipv6] IP PORT KEY [-f keyfile]\n", argv[0]); + exit(0); + } + + /* let user override default by cmdline */ + uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */ + int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled); + + if (argvoffset < 0) + exit(1); + int on = 0; int c = 0; - int i = 0; char *filename = "data"; char idstring[200] = {0}; Tox *m; - if (argc < 4) { - printf("[!] Usage: %s [IP] [port] [public_key] \n", argv[0]); + if ((argc == 2) && !strcmp(argv[1], "-h")) { + print_help(); exit(0); } - for (i = 0; i < argc; i++) { - if (argv[i] == NULL) { - break; - } else if (argv[i][0] == '-') { - if (argv[i][1] == 'h') { - print_help(); - exit(0); - } else if (argv[i][1] == 'f') { - if (argv[i + 1] != NULL) - filename = argv[i + 1]; - else { - fputs("[!] you passed '-f' without giving an argument!\n", stderr); - } - } - } - } + /* [-f keyfile] MUST be last two arguments, no point in walking over the list + * especially not a good idea to accept it anywhere in the middle */ + if (argc > argvoffset + 3) + if (!strcmp(argv[argc - 2], "-f")) + filename = argv[argc - 1]; - m = tox_new(); + m = tox_new(ipv6enabled); if ( !m ) { fputs("Failed to allocate Messenger datastructure", stderr); @@ -653,18 +653,17 @@ int main(int argc, char *argv[]) new_lines(idstring); strcpy(line, ""); - tox_IP_Port bootstrap_ip_port; - bootstrap_ip_port.port = htons(atoi(argv[2])); - int resolved_address = resolve_addr(argv[1]); - - if (resolved_address != 0) - bootstrap_ip_port.ip.i = resolved_address; - else - exit(1); - - unsigned char *binary_string = hex_string_to_bin(argv[3]); - tox_bootstrap(m, bootstrap_ip_port, binary_string); + uint16_t port = htons(atoi(argv[argvoffset + 2])); + unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]); + int res = tox_bootstrap_from_address(m, argv[argvoffset + 1], ipv6enabled, port, binary_string); free(binary_string); + + if (!res) { + printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]); + endwin(); + exit(1); + } + nodelay(stdscr, TRUE); new_lines("[i] change username with /n"); diff --git a/testing/nTox.h b/testing/nTox.h index 2cd5db09..a72ce0c2 100644 --- a/testing/nTox.h +++ b/testing/nTox.h @@ -31,6 +31,7 @@ #include #include "../toxcore/tox.h" + #define STRING_LENGTH 256 #define HISTORY 50 #define PUB_KEY_BYTES 32 diff --git a/toxcore/DHT.c b/toxcore/DHT.c index a11f1aad..2f7b2263 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -28,8 +28,10 @@ #endif #include "DHT.h" +#include "network.h" #include "ping.h" #include "misc_tools.h" +#include "util.h" /* The number of seconds for a non responsive node to become bad. */ #define BAD_NODE_TIMEOUT 70 @@ -115,11 +117,6 @@ static int client_id_cmp(ClientPair p1, ClientPair p2) return c; } -static int ipport_equal(IP_Port a, IP_Port b) -{ - return (a.ip.uint32 == b.ip.uint32) && (a.port == b.port); -} - static int id_equal(uint8_t *a, uint8_t *b) { return memcmp(a, b, CLIENT_ID_SIZE) == 0; @@ -142,20 +139,23 @@ static int client_in_list(Client_data *list, uint32_t length, uint8_t *client_id uint32_t i; uint64_t temp_time = unix_time(); - for (i = 0; i < length; ++i) { - /* If ip_port is assigned to a different client_id replace it */ - if (ipport_equal(list[i].ip_port, ip_port)) { - memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE); - } - + /* if client_id is in list, find it and maybe overwrite ip_port */ + for (i = 0; i < length; ++i) if (id_equal(list[i].client_id, client_id)) { /* Refresh the client timestamp. */ list[i].timestamp = temp_time; - list[i].ip_port.ip.uint32 = ip_port.ip.uint32; - list[i].ip_port.port = ip_port.port; + list[i].ip_port = ip_port; + return 1; + } + + /* client_id not in list yet: find ip_port to overwrite */ + for (i = 0; i < length; ++i) + if (ipport_equal(&list[i].ip_port, &ip_port)) { + /* Refresh the client timestamp. */ + list[i].timestamp = temp_time; + memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE); return 1; } - } return 0; } @@ -192,92 +192,98 @@ static int friend_number(DHT *dht, uint8_t *client_id) return -1; } -/* Find MAX_SENT_NODES nodes closest to the client_id for the send nodes request: - * put them in the nodes_list and return how many were found. - * - * TODO: For the love of based Allah make this function cleaner and much more efficient. +/* + * helper for get_close_nodes(). argument list is a monster :D */ -static int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list) +static void get_close_nodes_inner(DHT *dht, uint8_t *client_id, Node_format *nodes_list, + sa_family_t sa_family, Client_data *client_list, uint32_t client_list_length, + time_t timestamp, int *num_nodes_ptr) { - uint32_t i, j, k; - uint64_t temp_time = unix_time(); - int num_nodes = 0, closest, tout, inlist; + int num_nodes = *num_nodes_ptr; + int i, tout, inlist, ipv46x, j, closest; - for (i = 0; i < LCLIENT_LIST; ++i) { - tout = is_timeout(temp_time, dht->close_clientlist[i].timestamp, BAD_NODE_TIMEOUT); - inlist = client_in_nodelist(nodes_list, MAX_SENT_NODES, dht->close_clientlist[i].client_id); + for (i = 0; i < client_list_length; i++) { + Client_data *client = &client_list[i]; + tout = is_timeout(timestamp, client->timestamp, BAD_NODE_TIMEOUT); + inlist = client_in_nodelist(nodes_list, MAX_SENT_NODES, client->client_id); + +#ifdef TOX_ENABLE_IPV6 + IP *client_ip = &client->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->c->lossless_udp->net->family == AF_INET6) && + (client_ip->family == AF_INET6)) { + /* socket is AF_INET6, address claims AF_INET6: + * check for embedded IPv4-in-IPv6 */ + if (IN6_IS_ADDR_V4MAPPED(&client_ip->ip6.in6_addr)) + ip_treat_as_family = AF_INET; + } + + ipv46x = !(sa_family == ip_treat_as_family); +#else + ipv46x = !(sa_family == AF_INET); +#endif /* If node isn't good or is already in list. */ - if (tout || inlist) + if (tout || inlist || ipv46x) continue; if (num_nodes < MAX_SENT_NODES) { + memcpy(nodes_list[num_nodes].client_id, + client->client_id, + CLIENT_ID_SIZE ); - memcpy( nodes_list[num_nodes].client_id, - dht->close_clientlist[i].client_id, - CLIENT_ID_SIZE ); - - nodes_list[num_nodes].ip_port = dht->close_clientlist[i].ip_port; + nodes_list[num_nodes].ip_port = client->ip_port; num_nodes++; - } else { - + /* see if node_list contains a client_id that's "further away" + * compared to the one we're looking at at the moment, if there + * is, replace it + */ for (j = 0; j < MAX_SENT_NODES; ++j) { closest = id_closest( client_id, nodes_list[j].client_id, - dht->close_clientlist[i].client_id ); + client->client_id ); + /* second client_id is closer than current: change to it */ if (closest == 2) { memcpy( nodes_list[j].client_id, - dht->close_clientlist[i].client_id, + client->client_id, CLIENT_ID_SIZE); - nodes_list[j].ip_port = dht->close_clientlist[i].ip_port; + nodes_list[j].ip_port = client->ip_port; break; } } } } - for (i = 0; i < dht->num_friends; ++i) { - for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { + *num_nodes_ptr = num_nodes; +} - tout = is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, BAD_NODE_TIMEOUT); - inlist = client_in_nodelist( nodes_list, - MAX_SENT_NODES, - dht->friends_list[i].client_list[j].client_id); +/* Find MAX_SENT_NODES nodes closest to the client_id for the send nodes request: + * put them in the nodes_list and return how many were found. + * + * TODO: For the love of based make + * this function cleaner and much more efficient. + */ +static int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family) +{ + time_t timestamp = unix_time(); + int num_nodes = 0, i; + get_close_nodes_inner(dht, client_id, nodes_list, sa_family, + dht->close_clientlist, LCLIENT_LIST, timestamp, &num_nodes); - /* If node isn't good or is already in list. */ - if (tout || inlist) - continue; - - if (num_nodes < MAX_SENT_NODES) { - - memcpy( nodes_list[num_nodes].client_id, - dht->friends_list[i].client_list[j].client_id, - CLIENT_ID_SIZE); - - nodes_list[num_nodes].ip_port = dht->friends_list[i].client_list[j].ip_port; - num_nodes++; - } else { - for (k = 0; k < MAX_SENT_NODES; ++k) { - - closest = id_closest( client_id, - nodes_list[k].client_id, - dht->friends_list[i].client_list[j].client_id ); - - if (closest == 2) { - memcpy( nodes_list[k].client_id, - dht->friends_list[i].client_list[j].client_id, - CLIENT_ID_SIZE ); - - nodes_list[k].ip_port = dht->friends_list[i].client_list[j].ip_port; - break; - } - } - } - } - } + for (i = 0; i < dht->num_friends; ++i) + get_close_nodes_inner(dht, client_id, nodes_list, sa_family, + dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, + timestamp, &num_nodes); return num_nodes; } @@ -301,7 +307,7 @@ static int replace_bad( Client_data *list, memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE); list[i].ip_port = ip_port; list[i].timestamp = temp_time; - list[i].ret_ip_port.ip.uint32 = 0; + ip_reset(&list[i].ret_ip_port.ip); list[i].ret_ip_port.port = 0; list[i].ret_timestamp = 0; return 0; @@ -349,7 +355,7 @@ static int replace_good( Client_data *list, memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE); list[i].ip_port = ip_port; list[i].timestamp = temp_time; - list[i].ret_ip_port.ip.uint32 = 0; + ip_reset(&list[i].ret_ip_port.ip); list[i].ret_ip_port.port = 0; list[i].ret_timestamp = 0; return 0; @@ -447,13 +453,13 @@ static int is_gettingnodes(DHT *dht, IP_Port ip_port, uint64_t ping_id) if (!is_timeout(temp_time, dht->send_nodes[i].timestamp, PING_TIMEOUT)) { pinging = 0; - if (ip_port.ip.uint32 != 0 && ipport_equal(dht->send_nodes[i].ip_port, ip_port)) - ++pinging; - if (ping_id != 0 && dht->send_nodes[i].ping_id == ping_id) ++pinging; - if (pinging == (ping_id != 0) + (ip_port.ip.uint32 != 0)) + if (ip_isset(&ip_port.ip) && ipport_equal(&dht->send_nodes[i].ip_port, &ip_port)) + ++pinging; + + if (pinging == (ping_id != 0) + ip_isset(&ip_port.ip)) return 1; } } @@ -518,44 +524,75 @@ static int getnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *cli memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES); memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len); - return sendpacket(dht->c->lossless_udp->net->sock, ip_port, data, sizeof(data)); + return sendpacket(dht->c->lossless_udp->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 */ static int sendnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *client_id, uint64_t ping_id) { /* Check if packet is going to be sent to ourself. */ if (id_equal(public_key, dht->c->self_public_key)) return -1; + size_t Node4_format_size = sizeof(Node4_format); uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) - + sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING]; + + Node4_format_size * MAX_SENT_NODES + ENCRYPTION_PADDING]; Node_format nodes_list[MAX_SENT_NODES]; - int num_nodes = get_close_nodes(dht, client_id, nodes_list); + int num_nodes = get_close_nodes(dht, client_id, nodes_list, AF_INET); if (num_nodes == 0) return 0; - uint8_t plain[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES]; - uint8_t encrypt[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING]; + uint8_t plain[sizeof(ping_id) + Node4_format_size * MAX_SENT_NODES]; + uint8_t encrypt[sizeof(ping_id) + Node4_format_size * MAX_SENT_NODES + ENCRYPTION_PADDING]; uint8_t nonce[crypto_box_NONCEBYTES]; new_nonce(nonce); memcpy(plain, &ping_id, sizeof(ping_id)); - memcpy(plain + sizeof(ping_id), nodes_list, num_nodes * sizeof(Node_format)); +#ifdef TOX_ENABLE_IPV6 + Node4_format *nodes4_list = (Node4_format *)(plain + sizeof(ping_id)); + int 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; + } + +#else + memcpy(plain + sizeof(ping_id), nodes_list, num_nodes * Node4_format_size); +#endif int len = encrypt_data( public_key, dht->c->self_secret_key, nonce, plain, - sizeof(ping_id) + num_nodes * sizeof(Node_format), + sizeof(ping_id) + num_nodes * Node4_format_size, encrypt ); if (len == -1) return -1; - if ((unsigned int)len != sizeof(ping_id) + num_nodes * sizeof(Node_format) + ENCRYPTION_PADDING) + if ((unsigned int)len != sizeof(ping_id) + num_nodes * Node4_format_size + ENCRYPTION_PADDING) return -1; data[0] = NET_PACKET_SEND_NODES; @@ -563,9 +600,57 @@ static int sendnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *cl memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES); memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len); - return sendpacket(dht->c->lossless_udp->net->sock, ip_port, data, 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + len); + return sendpacket(dht->c->lossless_udp->net, ip_port, data, 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + len); } +#ifdef TOX_ENABLE_IPV6 +/* 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, uint64_t ping_id) +{ + /* Check if packet is going to be sent to ourself. */ + if (id_equal(public_key, dht->c->self_public_key)) + return -1; + + size_t Node_format_size = sizeof(Node_format); + uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + + Node_format_size * MAX_SENT_NODES + ENCRYPTION_PADDING]; + + Node_format nodes_list[MAX_SENT_NODES]; + int num_nodes = get_close_nodes(dht, client_id, nodes_list, AF_INET6); + + if (num_nodes == 0) + return 0; + + uint8_t plain[sizeof(ping_id) + Node_format_size * MAX_SENT_NODES]; + uint8_t encrypt[sizeof(ping_id) + Node_format_size * MAX_SENT_NODES + ENCRYPTION_PADDING]; + uint8_t nonce[crypto_box_NONCEBYTES]; + new_nonce(nonce); + + memcpy(plain, &ping_id, sizeof(ping_id)); + memcpy(plain + sizeof(ping_id), nodes_list, num_nodes * Node_format_size); + + int len = encrypt_data( public_key, + dht->c->self_secret_key, + nonce, + plain, + sizeof(ping_id) + num_nodes * Node_format_size, + encrypt ); + + if (len == -1) + return -1; + + if ((unsigned int)len != sizeof(ping_id) + num_nodes * Node_format_size + ENCRYPTION_PADDING) + return -1; + + data[0] = NET_PACKET_SEND_NODES_IPV6; + memcpy(data + 1, dht->c->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->c->lossless_udp->net, ip_port, data, 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + len); +} +#endif + static int handle_getnodes(void *object, IP_Port source, uint8_t *packet, uint32_t length) { DHT *dht = object; @@ -593,6 +678,10 @@ static int handle_getnodes(void *object, IP_Port source, uint8_t *packet, uint32 memcpy(&ping_id, plain, sizeof(ping_id)); sendnodes(dht, source, packet + 1, plain + sizeof(ping_id), ping_id); +#ifdef TOX_ENABLE_IPV6 + sendnodes_ipv6(dht, source, packet + 1, plain + sizeof(ping_id), + ping_id); /* TODO: prevent possible amplification attacks */ +#endif //send_ping_request(dht, source, packet + 1); /* TODO: make this smarter? */ @@ -606,22 +695,24 @@ static int handle_sendnodes(void *object, IP_Port source, uint8_t *packet, uint3 uint32_t cid_size = 1 + CLIENT_ID_SIZE; cid_size += crypto_box_NONCEBYTES + sizeof(ping_id) + ENCRYPTION_PADDING; - if (length > (cid_size + sizeof(Node_format) * MAX_SENT_NODES) || - ((length - cid_size) % sizeof(Node_format)) != 0 || - (length < cid_size + sizeof(Node_format))) + size_t Node4_format_size = sizeof(Node4_format); + + if (length > (cid_size + Node4_format_size * MAX_SENT_NODES) || + ((length - cid_size) % Node4_format_size) != 0 || + (length < cid_size + Node4_format_size)) return 1; - uint32_t num_nodes = (length - cid_size) / sizeof(Node_format); - uint8_t plain[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES]; + uint32_t num_nodes = (length - cid_size) / Node4_format_size; + uint8_t plain[sizeof(ping_id) + Node4_format_size * MAX_SENT_NODES]; int len = decrypt_data( packet + 1, dht->c->self_secret_key, packet + 1 + CLIENT_ID_SIZE, packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, - sizeof(ping_id) + num_nodes * sizeof(Node_format) + ENCRYPTION_PADDING, plain ); + sizeof(ping_id) + num_nodes * Node4_format_size + ENCRYPTION_PADDING, plain ); - if ((unsigned int)len != sizeof(ping_id) + num_nodes * sizeof(Node_format)) + if ((unsigned int)len != sizeof(ping_id) + num_nodes * Node4_format_size) return 1; memcpy(&ping_id, plain, sizeof(ping_id)); @@ -629,13 +720,35 @@ static int handle_sendnodes(void *object, IP_Port source, uint8_t *packet, uint3 if (!is_gettingnodes(dht, source, ping_id)) return 1; + uint32_t i; Node_format nodes_list[MAX_SENT_NODES]; + +#ifdef TOX_ENABLE_IPV6 + Node4_format *nodes4_list = (Node4_format *)(plain + sizeof(ping_id)); + + int num_nodes_ok = 0; + + for (i = 0; i < num_nodes; i++) + if ((nodes4_list[i].ip_port.ip.uint32 != 0) && (nodes4_list[i].ip_port.ip.uint32 != ~0)) { + memcpy(nodes_list[num_nodes_ok].client_id, nodes4_list[i].client_id, CLIENT_ID_SIZE); + nodes_list[num_nodes_ok].ip_port.ip.family = AF_INET; + nodes_list[num_nodes_ok].ip_port.ip.ip4.uint32 = nodes4_list[i].ip_port.ip.uint32; + nodes_list[num_nodes_ok].ip_port.port = nodes4_list[i].ip_port.port; + + num_nodes_ok++; + } + + if (num_nodes_ok < num_nodes) { + /* shouldn't happen */ + num_nodes = num_nodes_ok; + } + +#else memcpy(nodes_list, plain + sizeof(ping_id), num_nodes * sizeof(Node_format)); +#endif addto_lists(dht, source, packet + 1); - uint32_t i; - for (i = 0; i < num_nodes; ++i) { send_ping_request(dht->ping, dht->c, 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); @@ -644,9 +757,75 @@ static int handle_sendnodes(void *object, IP_Port source, uint8_t *packet, uint3 return 0; } +#ifdef TOX_ENABLE_IPV6 +static int handle_sendnodes_ipv6(void *object, IP_Port source, uint8_t *packet, uint32_t length) +{ + DHT *dht = object; + uint64_t ping_id; + uint32_t cid_size = 1 + CLIENT_ID_SIZE; + cid_size += crypto_box_NONCEBYTES + sizeof(ping_id) + ENCRYPTION_PADDING; + + size_t Node_format_size = sizeof(Node_format); + + if (length > (cid_size + Node_format_size * MAX_SENT_NODES) || + ((length - cid_size) % Node_format_size) != 0 || + (length < cid_size + Node_format_size)) + return 1; + + uint32_t num_nodes = (length - cid_size) / Node_format_size; + uint8_t plain[sizeof(ping_id) + Node_format_size * MAX_SENT_NODES]; + + int len = decrypt_data( + packet + 1, + dht->c->self_secret_key, + packet + 1 + CLIENT_ID_SIZE, + packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, + sizeof(ping_id) + num_nodes * Node_format_size + ENCRYPTION_PADDING, plain ); + + if ((unsigned int)len != sizeof(ping_id) + num_nodes * Node_format_size) + return 1; + + memcpy(&ping_id, plain, sizeof(ping_id)); + + if (!is_gettingnodes(dht, source, ping_id)) + return 1; + + uint32_t i; + Node_format nodes_list[MAX_SENT_NODES]; + memcpy(nodes_list, plain + sizeof(ping_id), num_nodes * sizeof(Node_format)); + + addto_lists(dht, source, packet + 1); + + for (i = 0; i < num_nodes; ++i) { + send_ping_request(dht->ping, dht->c, 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); + } + + return 0; +} +#endif + /*----------------------------------------------------------------------------------*/ /*------------------------END of packet handling functions--------------------------*/ +/* + * Send get nodes requests with client_id to max_num peers in list of length length + */ +static void get_bunchnodes(DHT *dht, Client_data *list, uint16_t length, uint16_t max_num, uint8_t *client_id) +{ + uint64_t temp_time = unix_time(); + uint32_t i, num = 0; + + for (i = 0; i < length; ++i) + if (ipport_isset(&(list[i].ip_port)) && !is_timeout(temp_time, list[i].ret_timestamp, BAD_NODE_TIMEOUT)) { + getnodes(dht, list[i].ip_port, list[i].client_id, client_id); + ++num; + + if (num >= max_num) + return; + } +} + int DHT_addfriend(DHT *dht, uint8_t *client_id) { if (friend_number(dht, client_id) != -1) /* Is friend already in DHT? */ @@ -664,6 +843,7 @@ int DHT_addfriend(DHT *dht, uint8_t *client_id) dht->friends_list[dht->num_friends].NATping_id = ((uint64_t)random_int() << 32) + random_int(); ++dht->num_friends; + get_bunchnodes(dht, dht->close_clientlist, LCLIENT_LIST, MAX_FRIEND_CLIENTS, client_id);/*TODO: make this better?*/ return 0; } @@ -703,27 +883,30 @@ int DHT_delfriend(DHT *dht, uint8_t *client_id) } /* TODO: Optimize this. */ -IP_Port DHT_getfriendip(DHT *dht, uint8_t *client_id) +int DHT_getfriendip(DHT *dht, uint8_t *client_id, IP_Port *ip_port) { uint32_t i, j; uint64_t temp_time = unix_time(); - IP_Port empty = {{{{0}}, 0, 0}}; + + ip_reset(&ip_port->ip); + ip_port->port = 0; for (i = 0; i < dht->num_friends; ++i) { /* Equal */ if (id_equal(dht->friends_list[i].client_id, client_id)) { for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { if (id_equal(dht->friends_list[i].client_list[j].client_id, client_id) - && !is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, BAD_NODE_TIMEOUT)) - return dht->friends_list[i].client_list[j].ip_port; + && !is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, BAD_NODE_TIMEOUT)) { + *ip_port = dht->friends_list[i].client_list[j].ip_port; + return 1; + } } - return empty; + return 0; } } - empty.ip.uint32 = 1; - return empty; + return -1; } /* Ping each client in the "friends" list every PING_INTERVAL seconds. Send a get nodes request @@ -808,6 +991,40 @@ void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key) getnodes(dht, ip_port, public_key, dht->c->self_public_key); send_ping_request(dht->ping, dht->c, ip_port, public_key); } +int DHT_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled, + uint16_t port, uint8_t *public_key) +{ + IP_Port ip_port_v64; + IP *ip_extra = NULL; +#ifdef TOX_ENABLE_IPV6 + IP_Port ip_port_v4; + ip_init(&ip_port_v64.ip, ipv6enabled); + + if (ipv6enabled) { + ip_port_v64.ip.family = AF_UNSPEC; + ip_reset(&ip_port_v4.ip); + ip_extra = &ip_port_v4.ip; + } + +#else + ip_init(&ip_port_v64.ip, 0); +#endif + + if (addr_resolve_or_parse_ip(address, &ip_port_v64.ip, ip_extra)) { + ip_port_v64.port = port; + DHT_bootstrap(dht, ip_port_v64, public_key); +#ifdef TOX_ENABLE_IPV6 + + if ((ip_extra != NULL) && ip_isset(ip_extra)) { + ip_port_v4.port = port; + DHT_bootstrap(dht, ip_port_v4, public_key); + } + +#endif + return 1; + } else + return 0; +} /* Send the given packet to node with client_id * @@ -819,7 +1036,7 @@ int route_packet(DHT *dht, uint8_t *client_id, uint8_t *packet, uint32_t length) for (i = 0; i < LCLIENT_LIST; ++i) { if (id_equal(client_id, dht->close_clientlist[i].client_id)) - return sendpacket(dht->c->lossless_udp->net->sock, dht->close_clientlist[i].ip_port, packet, length); + return sendpacket(dht->c->lossless_udp->net, dht->close_clientlist[i].ip_port, packet, length); } return -1; @@ -848,7 +1065,7 @@ static int friend_iplist(DHT *dht, IP_Port *ip_portlist, uint16_t friend_num) client = &friend->client_list[i]; /* If ip is not zero and node is good. */ - if (client->ret_ip_port.ip.uint32 != 0 && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) { + if (ip_isset(&client->ret_ip_port.ip) && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) { if (id_equal(client->client_id, friend->client_id)) return 0; @@ -890,8 +1107,8 @@ int route_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint32_t lengt client = &friend->client_list[i]; /* If ip is not zero and node is good. */ - if (client->ret_ip_port.ip.uint32 != 0 && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) { - int retval = sendpacket(dht->c->lossless_udp->net->sock, client->ip_port, packet, length); + if (ip_isset(&client->ret_ip_port.ip) && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) { + int retval = sendpacket(dht->c->lossless_udp->net, client->ip_port, packet, length); if ((unsigned int)retval == length) ++sent; @@ -924,7 +1141,7 @@ static int routeone_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint client = &friend->client_list[i]; /* If ip is not zero and node is good. */ - if (client->ret_ip_port.ip.uint32 != 0 && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) { + if (ip_isset(&client->ret_ip_port.ip) && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) { ip_list[n] = client->ip_port; ++n; } @@ -933,7 +1150,7 @@ static int routeone_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint if (n < 1) return 0; - int retval = sendpacket(dht->c->lossless_udp->net->sock, ip_list[rand() % n], packet, length); + int retval = sendpacket(dht->c->lossless_udp->net, ip_list[rand() % n], packet, length); if ((unsigned int)retval == length) return 1; @@ -1032,7 +1249,8 @@ static int handle_NATping(void *object, IP_Port source, uint8_t *source_pubkey, */ static IP NAT_commonip(IP_Port *ip_portlist, uint16_t len, uint16_t min_num) { - IP zero = {{0}}; + IP zero; + ip_reset(&zero); if (len > MAX_FRIEND_CLIENTS) return zero; @@ -1042,7 +1260,7 @@ static IP NAT_commonip(IP_Port *ip_portlist, uint16_t len, uint16_t min_num) for (i = 0; i < len; ++i) { for (j = 0; j < len; ++j) { - if (ip_portlist[i].ip.uint32 == ip_portlist[j].ip.uint32) + if (ip_equal(&ip_portlist[i].ip, &ip_portlist[j].ip)) ++numbers[i]; } @@ -1065,7 +1283,7 @@ static uint16_t NAT_getports(uint16_t *portlist, IP_Port *ip_portlist, uint16_t uint16_t num = 0; for (i = 0; i < len; ++i) { - if (ip_portlist[i].ip.uint32 == ip.uint32) { + if (ip_equal(&ip_portlist[i].ip, &ip)) { portlist[num] = ntohs(ip_portlist[i].port); ++num; } @@ -1085,7 +1303,9 @@ static void punch_holes(DHT *dht, IP ip, uint16_t *port_list, uint16_t numports, for (i = dht->friends_list[friend_num].punching_index; i != top; i++) { /* TODO: Improve port guessing algorithm. */ uint16_t port = port_list[(i / 2) % numports] + (i / (2 * numports)) * ((i % 2) ? -1 : 1); - IP_Port pinging = {{ip, htons(port), 0}}; + IP_Port pinging; + ip_copy(&pinging.ip, &ip); + pinging.port = htons(port); send_ping_request(dht->ping, dht->c, pinging, dht->friends_list[friend_num].client_id); } @@ -1116,7 +1336,7 @@ static void do_NAT(DHT *dht) IP ip = NAT_commonip(ip_list, num, MAX_FRIEND_CLIENTS / 2); - if (ip.uint32 == 0) + if (!ip_isset(&ip)) continue; uint16_t port_list[MAX_FRIEND_CLIENTS]; @@ -1145,16 +1365,15 @@ static void do_NAT(DHT *dht) */ int add_toping(DHT *dht, uint8_t *client_id, IP_Port ip_port) { - if (ip_port.ip.uint32 == 0) + if (!ip_isset(&ip_port.ip)) return -1; uint32_t i; for (i = 0; i < MAX_TOPING; ++i) { - if (dht->toping[i].ip_port.ip.uint32 == 0) { + if (!ip_isset(&dht->toping[i].ip_port.ip)) { memcpy(dht->toping[i].client_id, client_id, CLIENT_ID_SIZE); - dht->toping[i].ip_port.ip.uint32 = ip_port.ip.uint32; - dht->toping[i].ip_port.port = ip_port.port; + ipport_copy(&dht->toping[i].ip_port, &ip_port); return 0; } } @@ -1162,8 +1381,7 @@ int add_toping(DHT *dht, uint8_t *client_id, IP_Port ip_port) for (i = 0; i < MAX_TOPING; ++i) { if (id_closest(dht->c->self_public_key, dht->toping[i].client_id, client_id) == 2) { memcpy(dht->toping[i].client_id, client_id, CLIENT_ID_SIZE); - dht->toping[i].ip_port.ip.uint32 = ip_port.ip.uint32; - dht->toping[i].ip_port.port = ip_port.port; + ipport_copy(&dht->toping[i].ip_port, &ip_port); return 0; } } @@ -1185,11 +1403,11 @@ static void do_toping(DHT *dht) uint32_t i; for (i = 0; i < MAX_TOPING; ++i) { - if (dht->toping[i].ip_port.ip.uint32 == 0) + if (!ip_isset(&dht->toping[i].ip_port.ip)) return; send_ping_request(dht->ping, dht->c, dht->toping[i].ip_port, dht->toping[i].client_id); - dht->toping[i].ip_port.ip.uint32 = 0; + ip_reset(&dht->toping[i].ip_port.ip); } } @@ -1216,6 +1434,9 @@ DHT *new_DHT(Net_Crypto *c) networking_registerhandler(c->lossless_udp->net, NET_PACKET_PING_RESPONSE, &handle_ping_response, temp); networking_registerhandler(c->lossless_udp->net, NET_PACKET_GET_NODES, &handle_getnodes, temp); networking_registerhandler(c->lossless_udp->net, NET_PACKET_SEND_NODES, &handle_sendnodes, temp); +#ifdef TOX_ENABLE_IPV6 + networking_registerhandler(c->lossless_udp->net, NET_PACKET_SEND_NODES_IPV6, &handle_sendnodes_ipv6, temp); +#endif init_cryptopackets(temp); cryptopacket_registerhandler(c, CRYPTO_PACKET_NAT_PING, &handle_NATping, temp); return temp; @@ -1255,24 +1476,26 @@ void DHT_save(DHT *dht, uint8_t *data) */ int DHT_load(DHT *dht, uint8_t *data, uint32_t size) { - if (size < sizeof(dht->close_clientlist)) + if (size < sizeof(dht->close_clientlist)) { + fprintf(stderr, "DHT_load: Expected at least %lu bytes, got %u.\n", sizeof(dht->close_clientlist), size); return -1; + } - if ((size - sizeof(dht->close_clientlist)) % sizeof(DHT_Friend) != 0) + uint32_t friendlistsize = size - sizeof(dht->close_clientlist); + + if (friendlistsize % sizeof(DHT_Friend) != 0) { + fprintf(stderr, "DHT_load: Expected a multiple of %lu, got %u.\n", sizeof(DHT_Friend), friendlistsize); return -1; + } uint32_t i, j; - uint16_t temp; - /* uint64_t temp_time = unix_time(); */ - Client_data *client; + uint16_t friends_num = friendlistsize / sizeof(DHT_Friend); - temp = (size - sizeof(dht->close_clientlist)) / sizeof(DHT_Friend); - - if (temp != 0) { + if (friends_num != 0) { DHT_Friend *tempfriends_list = (DHT_Friend *)(data + sizeof(dht->close_clientlist)); - for (i = 0; i < temp; ++i) { + for (i = 0; i < friends_num; ++i) { DHT_addfriend(dht, tempfriends_list[i].client_id); for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { diff --git a/toxcore/DHT.h b/toxcore/DHT.h index e5122b5e..ff20c892 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -75,10 +75,22 @@ typedef struct { uint64_t NATping_timestamp; } 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 { uint8_t client_id[CLIENT_ID_SIZE]; IP_Port ip_port; -} Node_format; +} Node46_format; + +#ifdef TOX_ENABLE_IPV6 +typedef Node46_format Node_format; +#else +typedef Node4_format Node_format; +#endif typedef struct { IP_Port ip_port; @@ -88,15 +100,15 @@ typedef struct { /*----------------------------------------------------------------------------------*/ typedef struct { - Net_Crypto *c; + Net_Crypto *c; Client_data close_clientlist[LCLIENT_LIST]; - DHT_Friend *friends_list; + DHT_Friend *friends_list; uint16_t num_friends; Pinged send_nodes[LSEND_NODES_ARRAY]; Node_format toping[MAX_TOPING]; uint64_t last_toping; - uint64_t close_lastgetnodes; - void *ping; + uint64_t close_lastgetnodes; + void *ping; } DHT; /*----------------------------------------------------------------------------------*/ @@ -124,19 +136,45 @@ int DHT_delfriend(DHT *dht, uint8_t *client_id); * ip must be 4 bytes long. * port must be 2 bytes long. * + * !!! Signature changed !!! + * + * OLD: IP_Port DHT_getfriendip(DHT *dht, uint8_t *client_id); + * * return ip if success. * return ip of 0 if failure (This means the friend is either offline or we have not found him yet). * return ip of 1 if friend is not in list. + * + * NEW: int DHT_getfriendip(DHT *dht, uint8_t *client_id, IP_Port *ip_port); + * + * return -1, -- if client_id does NOT refer to a friend + * return 0, -- if client_id refers to a friend and we failed to find the friend (yet) + * return 1, ip if client_id refers to a friend and we found him */ -IP_Port DHT_getfriendip(DHT *dht, uint8_t *client_id); +int DHT_getfriendip(DHT *dht, uint8_t *client_id, IP_Port *ip_port); /* Run this function at least a couple times per second (It's the main loop). */ void do_DHT(DHT *dht); -/* Use this function to bootstrap the client. - * Sends a get nodes request to the given node with ip port and public_key. +/* + * Use these two functions to bootstrap the client. + */ +/* Sends a "get nodes" request to the given node with ip, port and public_key + * to setup connections */ void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key); +/* Resolves address into an IP address. If successful, sends a "get nodes" + * request to the given node with ip, port and public_key to setup connections + * + * address can be a hostname or an IP address (IPv4 or IPv6). + * if ipv6enabled is 0 (zero), the resolving sticks STRICTLY to IPv4 addresses + * if ipv6enabled is not 0 (zero), the resolving looks for IPv6 addresses first, + * then IPv4 addresses. + * + * returns 1 if the address could be converted into an IP address + * returns 0 otherwise + */ +int DHT_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled, + uint16_t port, uint8_t *public_key); /* Add nodes to the toping list. * All nodes in this list are pinged every TIME_TOPING seconds diff --git a/toxcore/LAN_discovery.c b/toxcore/LAN_discovery.c index 1980cd93..cf4196d2 100644 --- a/toxcore/LAN_discovery.c +++ b/toxcore/LAN_discovery.c @@ -30,12 +30,15 @@ #define MAX_INTERFACES 16 #ifdef __linux +#ifndef TOX_ENABLE_IPV6 /* Send packet to all broadcast addresses * * return higher than 0 on success. * return 0 on error. + * + * TODO: Make this work with IPv6 and remove the #ifndef TOX_ENABLE_IPV6. */ -static uint32_t send_broadcasts(Networking_Core *net, uint16_t port, uint8_t * data, uint16_t length) +static uint32_t send_broadcasts(Networking_Core *net, uint16_t port, uint8_t *data, uint16_t length) { /* Not sure how many platforms this will run on, * so it's wrapped in __linux for now. @@ -63,28 +66,64 @@ static uint32_t send_broadcasts(Networking_Core *net, uint16_t port, uint8_t * d } for (i = 0; i < count; i++) { - if (ioctl(sock, SIOCGIFBRDADDR, &i_faces[i]) < 0) { - return 1; - } + if (ioctl(sock, SIOCGIFBRDADDR, &i_faces[i]) < 0) { + return 1; + } - /* Just to clarify where we're getting the values from. */ - sock_holder = (struct sockaddr_in *)&i_faces[i].ifr_broadaddr; - if (sock_holder != NULL) { - IP_Port ip_port = {{{{sock_holder->sin_addr.s_addr}}, port, 0}}; - sendpacket(net->sock, ip_port, data, 1 + crypto_box_PUBLICKEYBYTES); - } + /* Just to clarify where we're getting the values from. */ + sock_holder = (struct sockaddr_in *)&i_faces[i].ifr_broadaddr; + + if (sock_holder != NULL) { + IP_Port ip_port = {{{{sock_holder->sin_addr.s_addr}}, port, 0}}; + sendpacket(net, ip_port, data, 1 + crypto_box_PUBLICKEYBYTES); + } } close(sock); return 0; } #endif +#endif /* Return the broadcast ip. */ -static IP broadcast_ip(void) +static IP broadcast_ip(sa_family_t family_socket, sa_family_t family_broadcast) { IP ip; - ip.uint32 = ~0; + ip_reset(&ip); + +#ifdef TOX_ENABLE_IPV6 + + if (family_socket == AF_INET6) { + if (family_broadcast == AF_INET6) { + ip.family = AF_INET6; + /* FF02::1 is - according to RFC 4291 - multicast all-nodes link-local */ + /* FE80::*: MUST be exact, for that we would need to look over all + * interfaces and check in which status they are */ + ip.ip6.uint8[ 0] = 0xFF; + ip.ip6.uint8[ 1] = 0x02; + ip.ip6.uint8[15] = 0x01; + } else if (family_broadcast == AF_INET) { + ip.family = AF_INET6; + ip.ip6.uint32[0] = 0; + ip.ip6.uint32[1] = 0; + ip.ip6.uint32[2] = htonl(0xFFFF); + ip.ip6.uint32[3] = INADDR_BROADCAST; + } + } else if (family_socket == AF_INET) { + if (family_broadcast == AF_INET) { + ip.family = AF_INET; + ip.ip4.uint32 = INADDR_BROADCAST; + } + } + +#else + + if (family_socket == AF_INET) + if (family_broadcast == AF_INET) + ip.uint32 = INADDR_BROADCAST; + +#endif + return ip; } @@ -93,21 +132,54 @@ static IP broadcast_ip(void) */ static int LAN_ip(IP ip) { - if (ip.uint8[0] == 127) /* Loopback. */ - return 0; +#ifdef TOX_ENABLE_IPV6 - if (ip.uint8[0] == 10) /* 10.0.0.0 to 10.255.255.255 range. */ - return 0; + if (ip.family == AF_INET) { + IP4 ip4 = ip.ip4; +#else + IP4 ip4 = ip; +#endif - if (ip.uint8[0] == 172 && ip.uint8[1] >= 16 && ip.uint8[1] <= 31) /* 172.16.0.0 to 172.31.255.255 range. */ - return 0; + /* Loopback. */ + if (ip4.uint8[0] == 127) + return 0; - if (ip.uint8[0] == 192 && ip.uint8[1] == 168) /* 192.168.0.0 to 192.168.255.255 range. */ - return 0; + /* 10.0.0.0 to 10.255.255.255 range. */ + if (ip4.uint8[0] == 10) + return 0; - if (ip.uint8[0] == 169 && ip.uint8[1] == 254 && ip.uint8[2] != 0 - && ip.uint8[2] != 255)/* 169.254.1.0 to 169.254.254.255 range. */ - return 0; + /* 172.16.0.0 to 172.31.255.255 range. */ + if (ip4.uint8[0] == 172 && ip4.uint8[1] >= 16 && ip4.uint8[1] <= 31) + return 0; + + /* 192.168.0.0 to 192.168.255.255 range. */ + if (ip4.uint8[0] == 192 && ip4.uint8[1] == 168) + return 0; + + /* 169.254.1.0 to 169.254.254.255 range. */ + if (ip4.uint8[0] == 169 && ip4.uint8[1] == 254 && ip4.uint8[2] != 0 + && ip4.uint8[2] != 255) + return 0; + +#ifdef TOX_ENABLE_IPV6 + } else if (ip.family == AF_INET6) + { + /* autogenerated for each interface: FE80::* (up to FEBF::*) + FF02::1 is - according to RFC 4291 - multicast all-nodes link-local */ + if (((ip.ip6.uint8[0] == 0xFF) && (ip.ip6.uint8[1] < 3) && (ip.ip6.uint8[15] == 1)) || + ((ip.ip6.uint8[0] == 0xFE) && ((ip.ip6.uint8[1] & 0xC0) == 0x80))) + return 0; + + /* embedded IPv4-in-IPv6 */ + if (IN6_IS_ADDR_V4MAPPED(&ip.ip6.in6_addr)) { + IP ip4; + ip4.family = AF_INET; + ip4.ip4.uint32 = ip.ip6.uint32[3]; + return LAN_ip(ip4); + } + } + +#endif return -1; } @@ -132,11 +204,38 @@ int send_LANdiscovery(uint16_t port, Net_Crypto *c) uint8_t data[crypto_box_PUBLICKEYBYTES + 1]; data[0] = NET_PACKET_LAN_DISCOVERY; memcpy(data + 1, c->self_public_key, crypto_box_PUBLICKEYBYTES); + #ifdef __linux +#ifndef TOX_ENABLE_IPV6 send_broadcasts(c->lossless_udp->net, port, data, 1 + crypto_box_PUBLICKEYBYTES); #endif - IP_Port ip_port = {{broadcast_ip(), port, 0}}; - return sendpacket(c->lossless_udp->net->sock, ip_port, data, 1 + crypto_box_PUBLICKEYBYTES); +#endif + int res = -1; + IP_Port ip_port; + ip_port.port = port; + +#ifdef TOX_ENABLE_IPV6 + + /* IPv6 multicast */ + if (c->lossless_udp->net->family == AF_INET6) { + ip_port.ip = broadcast_ip(AF_INET6, AF_INET6); + + if (ip_isset(&ip_port.ip)) + if (sendpacket(c->lossless_udp->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(c->lossless_udp->net->family, AF_INET); +#else + ip_port.ip = broadcast_ip(AF_INET, AF_INET); +#endif + + if (ip_isset(&ip_port.ip)) + if (sendpacket(c->lossless_udp->net, ip_port, data, 1 + crypto_box_PUBLICKEYBYTES)) + res = 1; + + return res; } diff --git a/toxcore/Lossless_UDP.c b/toxcore/Lossless_UDP.c index fbb473e7..46b0bed1 100644 --- a/toxcore/Lossless_UDP.c +++ b/toxcore/Lossless_UDP.c @@ -44,9 +44,7 @@ int getconnection_id(Lossless_UDP *ludp, IP_Port ip_port) { tox_array_for_each(&ludp->connections, Connection, tmp) { - if (tmp->ip_port.ip.uint32 == ip_port.ip.uint32 && - tmp->ip_port.port == ip_port.port && - tmp->status > 0) { + if (tmp-> status > 0 && ipport_equal(&tmp->ip_port, &ip_port)) { return tmp_i; } } @@ -61,17 +59,53 @@ int getconnection_id(Lossless_UDP *ludp, IP_Port ip_port) * * TODO: make this better */ + +static uint8_t randtable_initget(Lossless_UDP *ludp, uint32_t index, uint8_t value) +{ + if (ludp->randtable[index][value] == 0) + ludp->randtable[index][value] = random_int(); + + return ludp->randtable[index][value]; +} + static uint32_t handshake_id(Lossless_UDP *ludp, IP_Port source) { - uint32_t id = 0, i; + uint32_t id = 0, i = 0; - for (i = 0; i < 6; ++i) { - if (ludp->randtable[i][source.uint8[i]] == 0) - ludp->randtable[i][source.uint8[i]] = random_int(); + uint8_t *uint8; + uint8 = (uint8_t *)&source.port; + id ^= randtable_initget(ludp, i, *uint8); + i++, uint8++; + id ^= randtable_initget(ludp, i, *uint8); + i++; - id ^= ludp->randtable[i][source.uint8[i]]; +#ifdef TOX_ENABLE_IPV6 + + if (source.ip.family == AF_INET) { + IP4 ip4 = source.ip.ip4; +#else + IP4 ip4 = source.ip; +#endif + int k; + + for (k = 0; k < 4; k++) { + id ^= randtable_initget(ludp, i++, ip4.uint8[k]); + } + +#ifdef TOX_ENABLE_IPV6 } + if (source.ip.family == AF_INET6) + { + int k; + + for (k = 0; k < 16; k++) { + id ^= randtable_initget(ludp, i++, source.ip.ip6.uint8[k]); + } + } + +#endif + /* id can't be zero. */ if (id == 0) id = 1; @@ -86,8 +120,21 @@ static uint32_t handshake_id(Lossless_UDP *ludp, IP_Port source) */ static void change_handshake(Lossless_UDP *ludp, IP_Port source) { - uint8_t rand = random_int() % 4; - ludp->randtable[rand][((uint8_t *)&source)[rand]] = random_int(); +#ifdef TOX_ENABLE_IPV6 + uint8_t rand; + + if (source.ip.family == AF_INET) { + rand = 2 + random_int() % 4; + } else if (source.ip.family == AF_INET6) { + rand = 2 + random_int() % 16; + } else { + return; + } + +#else + uint8_t rand = 2 + random_int() % 4; +#endif + ludp->randtable[rand][((uint8_t *)&source.ip)[rand]] = random_int(); } /* @@ -290,7 +337,9 @@ IP_Port connection_ip(Lossless_UDP *ludp, int connection_id) if ((unsigned int)connection_id < ludp->connections.len) return tox_array_get(&ludp->connections, connection_id, Connection).ip_port; - IP_Port zero = {{{{0}}, 0, 0}}; + IP_Port zero; + ip_reset(&zero.ip); + zero.port = 0; return zero; } @@ -429,7 +478,7 @@ static int send_handshake(Lossless_UDP *ludp, IP_Port ip_port, uint32_t handshak temp = htonl(handshake_id2); memcpy(packet + 5, &temp, 4); - return sendpacket(ludp->net->sock, ip_port, packet, sizeof(packet)); + return sendpacket(ludp->net, ip_port, packet, sizeof(packet)); } static int send_SYNC(Lossless_UDP *ludp, int connection_id) @@ -456,7 +505,7 @@ static int send_SYNC(Lossless_UDP *ludp, int connection_id) index += 4; memcpy(packet + index, requested, 4 * number); - return sendpacket(ludp->net->sock, ip_port, packet, (number * 4 + 4 + 4 + 2)); + return sendpacket(ludp->net, ip_port, packet, (number * 4 + 4 + 4 + 2)); } @@ -471,7 +520,7 @@ static int send_data_packet(Lossless_UDP *ludp, int connection_id, uint32_t pack temp = htonl(packet_num); memcpy(packet + 1, &temp, 4); memcpy(packet + 5, connection->sendbuffer[index].data, connection->sendbuffer[index].size); - return sendpacket(ludp->net->sock, connection->ip_port, packet, 1 + 4 + connection->sendbuffer[index].size); + return sendpacket(ludp->net, connection->ip_port, packet, 1 + 4 + connection->sendbuffer[index].size); } /* Sends 1 data packet. */ diff --git a/toxcore/Lossless_UDP.h b/toxcore/Lossless_UDP.h index 20471b0a..f0ce0e87 100644 --- a/toxcore/Lossless_UDP.h +++ b/toxcore/Lossless_UDP.h @@ -123,7 +123,13 @@ typedef struct { tox_array connections; /* Table of random numbers used in handshake_id. */ +#ifdef TOX_ENABLE_IPV6 + /* IPv6 (16) + port (2)*/ + uint32_t randtable[18][256]; +#else + /* IPv4 (4) + port (2) */ uint32_t randtable[6][256]; +#endif } Lossless_UDP; diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 951057c5..eb18e3a3 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -26,6 +26,7 @@ #endif #include "Messenger.h" +#include "util.h" #define MIN(a,b) (((a)<(b))?(a):(b)) @@ -901,19 +902,17 @@ static void do_allgroupchats(Messenger *m) /*********************************/ -#define PORT 33445 - /* Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds. */ static void LANdiscovery(Messenger *m) { if (m->last_LANdiscovery + LAN_DISCOVERY_INTERVAL < unix_time()) { - send_LANdiscovery(htons(PORT), m->net_crypto); + send_LANdiscovery(htons(TOX_PORT_DEFAULT), m->net_crypto); m->last_LANdiscovery = unix_time(); } } /* Run this at startup. */ -Messenger *initMessenger(void) +Messenger *initMessenger(uint8_t ipv6enabled) { Messenger *m = calloc(1, sizeof(Messenger)); @@ -921,8 +920,8 @@ Messenger *initMessenger(void) return NULL; IP ip; - ip.uint32 = 0; - m->net = new_networking(ip, PORT); + ip_init(&ip, ipv6enabled); + m->net = new_networking(ip, TOX_PORT_DEFAULT); if (m->net == NULL) { free(m); @@ -1006,11 +1005,12 @@ void doFriends(Messenger *m) } } - IP_Port friendip = DHT_getfriendip(m->dht, m->friendlist[i].client_id); + IP_Port friendip; + int friendok = DHT_getfriendip(m->dht, m->friendlist[i].client_id, &friendip); switch (is_cryptoconnected(m->net_crypto, m->friendlist[i].crypt_connection_id)) { case 0: - if (friendip.ip.uint32 > 1) + if (friendok == 1) m->friendlist[i].crypt_connection_id = crypto_connect(m->net_crypto, m->friendlist[i].client_id, friendip); break; @@ -1219,6 +1219,22 @@ void doInbound(Messenger *m) } } +#ifdef LOGGING +#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60 +static time_t lastdump = 0; +static char IDString[CLIENT_ID_SIZE * 2 + 1]; +static char *ID2String(uint8_t *client_id) +{ + uint32_t i; + + for (i = 0; i < CLIENT_ID_SIZE; i++) + sprintf(&IDString[i], "%02X", client_id[i]); + + IDString[CLIENT_ID_SIZE * 2] = 0; + return IDString; +} +#endif + /* The main loop that needs to be run at least 20 times per second. */ void doMessenger(Messenger *m) { @@ -1230,6 +1246,88 @@ void doMessenger(Messenger *m) doFriends(m); do_allgroupchats(m); LANdiscovery(m); + +#ifdef LOGGING + + if (now() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) { + loglog(" = = = = = = = = \n"); + + lastdump = now(); + uint32_t client, last_pinged; + + for (client = 0; client < LCLIENT_LIST; client++) { + Client_data *cptr = &m->dht->close_clientlist[client]; + + if (ip_isset(&cptr->ip_port.ip)) { + last_pinged = lastdump - cptr->last_pinged; + + if (last_pinged > 999) + last_pinged = 999; + + snprintf(logbuffer, sizeof(logbuffer), "C[%2u] %s:%u [%3u] %s\n", + client, ip_ntoa(&cptr->ip_port.ip), ntohs(cptr->ip_port.port), + last_pinged, ID2String(cptr->client_id)); + loglog(logbuffer); + } + } + + loglog(" = = = = = = = = \n"); + + uint32_t num_friends = MIN(m->numfriends, m->dht->num_friends); + + if (m->numfriends != m->dht->num_friends) { + sprintf(logbuffer, "Friend num in DHT %u != friend num in msger %u\n", + m->dht->num_friends, m->numfriends); + loglog(logbuffer); + } + + uint32_t friend, ping_lastrecv; + + for (friend = 0; friend < num_friends; friend++) { + Friend *msgfptr = &m->friendlist[friend]; + DHT_Friend *dhtfptr = &m->dht->friends_list[friend]; + + if (memcmp(msgfptr->client_id, dhtfptr->client_id, CLIENT_ID_SIZE)) { + if (sizeof(logbuffer) > 2 * CLIENT_ID_SIZE + 64) { + sprintf(logbuffer, "F[%2u] ID(m) %s != ID(d) ", friend, + ID2String(msgfptr->client_id)); + strcat(logbuffer + strlen(logbuffer), ID2String(dhtfptr->client_id)); + strcat(logbuffer + strlen(logbuffer), "\n"); + } else + sprintf(logbuffer, "F[%2u] ID(m) != ID(d) ", friend); + + loglog(logbuffer); + } + + ping_lastrecv = lastdump - msgfptr->ping_lastrecv; + + if (ping_lastrecv > 999) + ping_lastrecv = 999; + + snprintf(logbuffer, sizeof(logbuffer), "F[%2u] <%s> %02u [%03u] %s\n", + friend, msgfptr->name, msgfptr->crypt_connection_id, + ping_lastrecv, ID2String(msgfptr->client_id)); + loglog(logbuffer); + + for (client = 0; client < MAX_FRIEND_CLIENTS; client++) { + Client_data *cptr = &dhtfptr->client_list[client]; + last_pinged = lastdump - cptr->last_pinged; + + if (last_pinged > 999) + last_pinged = 999; + + snprintf(logbuffer, sizeof(logbuffer), "F[%2u] => C[%2u] %s:%u [%3u] %s\n", + friend, client, ip_ntoa(&cptr->ip_port.ip), + ntohs(cptr->ip_port.port), last_pinged, + ID2String(cptr->client_id)); + loglog(logbuffer); + } + } + + loglog(" = = = = = = = = \n"); + } + +#endif } /* return size of the messenger data (for saving) */ @@ -1251,19 +1349,23 @@ void Messenger_save(Messenger *m, uint8_t *data) { save_keys(m->net_crypto, data); data += crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; + uint32_t nospam = get_nospam(&(m->fr)); memcpy(data, &nospam, sizeof(nospam)); data += sizeof(nospam); + uint32_t size = DHT_size(m->dht); memcpy(data, &size, sizeof(size)); data += sizeof(size); DHT_save(m->dht, data); data += size; + size = sizeof(Friend) * m->numfriends; memcpy(data, &size, sizeof(size)); data += sizeof(size); memcpy(data, m->friendlist, sizeof(Friend) * m->numfriends); data += size; + uint16_t small_size = m->name_length; memcpy(data, &small_size, sizeof(small_size)); data += sizeof(small_size); @@ -1276,59 +1378,70 @@ int Messenger_load(Messenger *m, uint8_t *data, uint32_t length) if (length == ~((uint32_t)0)) return -1; - if (length < crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t) * 3) + /* BLOCK1: PUBKEY, SECKEY, NOSPAM, SIZE */ + if (length < crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t) * 2) return -1; - length -= crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t) * 3; load_keys(m->net_crypto, data); data += crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; + length -= crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; + uint32_t nospam; memcpy(&nospam, data, sizeof(nospam)); set_nospam(&(m->fr), nospam); data += sizeof(nospam); + length -= sizeof(nospam); + uint32_t size; memcpy(&size, data, sizeof(size)); data += sizeof(size); + length -= sizeof(size); if (length < size) return -1; - length -= size; - if (DHT_load(m->dht, data, size) == -1) - return -1; + fprintf(stderr, "Data file: Something wicked happened to the stored connections...\n"); + + /* go on, friends still might be intact */ data += size; - memcpy(&size, data, sizeof(size)); - data += sizeof(size); + length -= size; - if (length < size || size % sizeof(Friend) != 0) + if (length < sizeof(size)) return -1; - Friend *temp = malloc(size); - memcpy(temp, data, size); + memcpy(&size, data, sizeof(size)); + data += sizeof(size); + length -= sizeof(size); - uint16_t num = size / sizeof(Friend); + if (length < size) + return -1; - uint32_t i; + if (!(size % sizeof(Friend))) { + uint16_t num = size / sizeof(Friend); + Friend temp[num]; + memcpy(temp, data, size); - for (i = 0; i < num; ++i) { - if (temp[i].status >= 3) { - int fnum = m_addfriend_norequest(m, temp[i].client_id); - setfriendname(m, fnum, temp[i].name, temp[i].name_length); - /* set_friend_statusmessage(fnum, temp[i].statusmessage, temp[i].statusmessage_length); */ - } else if (temp[i].status != 0) { - /* TODO: This is not a good way to do this. */ - uint8_t address[FRIEND_ADDRESS_SIZE]; - memcpy(address, temp[i].client_id, crypto_box_PUBLICKEYBYTES); - memcpy(address + crypto_box_PUBLICKEYBYTES, &(temp[i].friendrequest_nospam), sizeof(uint32_t)); - uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); - memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), &checksum, sizeof(checksum)); - m_addfriend(m, address, temp[i].info, temp[i].info_size); + uint32_t i; + + for (i = 0; i < num; ++i) { + if (temp[i].status >= 3) { + int fnum = m_addfriend_norequest(m, temp[i].client_id); + setfriendname(m, fnum, temp[i].name, temp[i].name_length); + /* set_friend_statusmessage(fnum, temp[i].statusmessage, temp[i].statusmessage_length); */ + } else if (temp[i].status != 0) { + /* TODO: This is not a good way to do this. */ + uint8_t address[FRIEND_ADDRESS_SIZE]; + memcpy(address, temp[i].client_id, crypto_box_PUBLICKEYBYTES); + memcpy(address + crypto_box_PUBLICKEYBYTES, &(temp[i].friendrequest_nospam), sizeof(uint32_t)); + uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); + memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), &checksum, sizeof(checksum)); + m_addfriend(m, address, temp[i].info, temp[i].info_size); + } } } - free(temp); data += size; length -= size; diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index 0656c736..bfcc69df 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -427,7 +427,7 @@ int group_message_send(Messenger *m, int groupnumber, uint8_t *message, uint32_t * return allocated instance of Messenger on success. * return 0 if there are problems. */ -Messenger *initMessenger(void); +Messenger *initMessenger(uint8_t ipv6enabled); /* Run this before closing shop * Free all datastructures. diff --git a/toxcore/friend_requests.c b/toxcore/friend_requests.c index b01015c4..c821d998 100644 --- a/toxcore/friend_requests.c +++ b/toxcore/friend_requests.c @@ -50,18 +50,22 @@ int send_friendrequest(DHT *dht, uint8_t *public_key, uint32_t nospam_num, uint8 if (len == -1) return -1; - IP_Port ip_port = DHT_getfriendip(dht, public_key); + IP_Port ip_port; + int friendok = DHT_getfriendip(dht, public_key, &ip_port); - if (ip_port.ip.uint32 == 1) + // not a friend + if (friendok == -1) return -1; - if (ip_port.ip.uint32 != 0) { - if (sendpacket(dht->c->lossless_udp->net->sock, ip_port, packet, len) != -1) + // is a friend and we know how to reach him + if (friendok == 1) { + if (sendpacket(dht->c->lossless_udp->net, ip_port, packet, len) != -1) return 0; return -1; } + // is a friend, we DON'T know how to reach him int num = route_tofriend(dht, public_key, packet, len); if (num == 0) diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c index 1e5309ad..f37c6a9c 100644 --- a/toxcore/group_chats.c +++ b/toxcore/group_chats.c @@ -189,7 +189,7 @@ static int send_groupchatpacket(Group_Chat *chat, IP_Port ip_port, uint8_t *publ if (len == -1) return -1; - if (sendpacket(chat->net->sock, ip_port, packet, len) == len) + if (sendpacket(chat->net, ip_port, packet, len) == len) return 0; return -1; @@ -208,7 +208,7 @@ static uint8_t sendto_allpeers(Group_Chat *chat, uint8_t *data, uint16_t length, uint64_t temp_time = unix_time(); for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { - if (chat->close[i].ip_port.ip.uint32 != 0 && chat->close[i].last_recv + BAD_NODE_TIMEOUT > temp_time) { + if (ip_isset(&chat->close[i].ip_port.ip) && chat->close[i].last_recv + BAD_NODE_TIMEOUT > temp_time) { if (send_groupchatpacket(chat, chat->close[i].ip_port, chat->close[i].client_id, data, length, request_id) == 0) ++sent; } diff --git a/toxcore/net_crypto.c b/toxcore/net_crypto.c index 8163701e..3f866f74 100644 --- a/toxcore/net_crypto.c +++ b/toxcore/net_crypto.c @@ -451,7 +451,7 @@ int crypto_connect(Net_Crypto *c, uint8_t *public_key, IP_Port ip_port) if (id != -1) { IP_Port c_ip = connection_ip(c->lossless_udp, c->crypto_connections[id].number); - if (c_ip.ip.uint32 == ip_port.ip.uint32 && c_ip.port == ip_port.port) + if (ipport_equal(&c_ip, &ip_port)) return -1; } diff --git a/toxcore/network.c b/toxcore/network.c index c6c4965e..fe2ef238 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -26,6 +26,7 @@ #endif #include "network.h" +#include "util.h" /* return current UNIX time in microseconds (us). */ uint64_t current_time(void) @@ -62,17 +63,83 @@ uint32_t random_int(void) #endif } +#ifdef LOGGING +static void loglogdata(char *message, uint8_t *buffer, size_t buflen, IP_Port *ip_port, ssize_t res); +#endif + /* Basic network functions: * Function to send packet(data) of length length to ip_port. */ -#ifdef WIN32 -int sendpacket(unsigned int sock, IP_Port ip_port, uint8_t *data, uint32_t length) -#else -int sendpacket(int sock, IP_Port ip_port, uint8_t *data, uint32_t length) -#endif +int sendpacket(Networking_Core *net, IP_Port ip_port, uint8_t *data, uint32_t length) { - ADDR addr = {AF_INET, ip_port.port, ip_port.ip, {0}}; - return sendto(sock, (char *) data, length, 0, (struct sockaddr *)&addr, sizeof(addr)); +#ifdef TOX_ENABLE_IPV6 + + /* socket AF_INET, but target IP NOT: can't send */ + if ((net->family == AF_INET) && (ip_port.ip.family != AF_INET)) + return -1; + +#endif + + struct sockaddr_storage addr; + size_t addrsize = 0; + +#ifdef TOX_ENABLE_IPV6 + + if (ip_port.ip.family == AF_INET) { + if (net->family == AF_INET6) { + /* must convert to IPV4-in-IPV6 address */ + addrsize = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; + addr6->sin6_family = AF_INET6; + addr6->sin6_port = ip_port.port; + + /* there should be a macro for this in a standards compliant + * environment, not found */ + IP6 ip6; + + ip6.uint32[0] = 0; + ip6.uint32[1] = 0; + ip6.uint32[2] = htonl(0xFFFF); + ip6.uint32[3] = ip_port.ip.ip4.uint32; + addr6->sin6_addr = ip6.in6_addr; + + addr6->sin6_flowinfo = 0; + addr6->sin6_scope_id = 0; + } else { + IP4 ip4 = ip_port.ip.ip4; +#else + IP4 ip4 = ip_port.ip; +#endif + addrsize = sizeof(struct sockaddr_in); + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; + addr4->sin_family = AF_INET; + addr4->sin_addr = ip4.in_addr; + addr4->sin_port = ip_port.port; +#ifdef TOX_ENABLE_IPV6 + } + } else if (ip_port.ip.family == AF_INET6) + { + addrsize = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; + addr6->sin6_family = AF_INET6; + addr6->sin6_port = ip_port.port; + addr6->sin6_addr = ip_port.ip.ip6.in6_addr; + + addr6->sin6_flowinfo = 0; + addr6->sin6_scope_id = 0; + } else + { + /* unknown address type*/ + return -1; + } + +#endif + + int res = sendto(net->sock, (char *) data, length, 0, (struct sockaddr *)&addr, addrsize); +#ifdef LOGGING + loglogdata("O=>", data, length, &ip_port, res); +#endif + return res; } /* Function to receive data @@ -81,13 +148,9 @@ int sendpacket(int sock, IP_Port ip_port, uint8_t *data, uint32_t length) * Packet length is put into length. * Dump all empty packets. */ -#ifdef WIN32 -static int receivepacket(unsigned int sock, IP_Port *ip_port, uint8_t *data, uint32_t *length) -#else -static int receivepacket(int sock, IP_Port *ip_port, uint8_t *data, uint32_t *length) -#endif +static int receivepacket(sock_t sock, IP_Port *ip_port, uint8_t *data, uint32_t *length) { - ADDR addr; + struct sockaddr_storage addr; #ifdef WIN32 int addrlen = sizeof(addr); #else @@ -95,11 +158,48 @@ static int receivepacket(int sock, IP_Port *ip_port, uint8_t *data, uint32_t *le #endif (*(int32_t *)length) = recvfrom(sock, (char *) data, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrlen); - if (*(int32_t *)length <= 0) - return -1; /* Nothing received or empty packet. */ + if (*(int32_t *)length <= 0) { +#ifdef LOGGING + + if ((length < 0) && (errno != EWOULDBLOCK)) { + sprintf(logbuffer, "Unexpected error reading from socket: %u, %s\n", errno, strerror(errno)); + loglog(logbuffer); + } + +#endif + return -1; /* Nothing received or empty packet. */ + } + +#ifdef TOX_ENABLE_IPV6 + + if (addr.ss_family == AF_INET) { + struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; + ip_port->ip.family = addr_in->sin_family; + ip_port->ip.ip4.in_addr = addr_in->sin_addr; + ip_port->port = addr_in->sin_port; + } else if (addr.ss_family == AF_INET6) { + struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)&addr; + ip_port->ip.family = addr_in6->sin6_family; + ip_port->ip.ip6.in6_addr = addr_in6->sin6_addr; + ip_port->port = addr_in6->sin6_port; + } else + return -1; + +#else + + if (addr.ss_family == AF_INET) { + struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; + ip_port->ip.in_addr = addr_in->sin_addr; + ip_port->port = addr_in->sin_port; + } else + return -1; + +#endif + +#ifdef LOGGING + loglogdata("=>O", data, MAX_UDP_PACKET_SIZE, ip_port, *length); +#endif - ip_port->ip = addr.ip; - ip_port->port = addr.port; return 0; } @@ -118,13 +218,20 @@ void networking_poll(Networking_Core *net) while (receivepacket(net->sock, &ip_port, data, &length) != -1) { if (length < 1) continue; - if (!(net->packethandlers[data[0]].function)) continue; + if (!(net->packethandlers[data[0]].function)) { +#ifdef LOGGING + sprintf(logbuffer, "[%02u] -- Packet has no handler.\n", data[0]); + loglog(logbuffer); +#endif + continue; + } net->packethandlers[data[0]].function(net->packethandlers[data[0]].object, ip_port, data, length); } } -uint8_t at_startup_ran; + +uint8_t at_startup_ran = 0; static int at_startup(void) { if (at_startup_ran != 0) @@ -163,16 +270,34 @@ static void at_shutdown(void) */ Networking_Core *new_networking(IP ip, uint16_t port) { +#ifdef TOX_ENABLE_IPV6 + + /* maybe check for invalid IPs like 224+.x.y.z? if there is any IP set ever */ + if (ip.family != AF_INET && ip.family != AF_INET6) { + fprintf(stderr, "Invalid address family: %u\n", ip.family); + return NULL; + } + +#endif + if (at_startup() != 0) return NULL; - /* Initialize our socket. */ Networking_Core *temp = calloc(1, sizeof(Networking_Core)); if (temp == NULL) return NULL; - temp->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +#ifdef TOX_ENABLE_IPV6 + temp->family = ip.family; +#else + temp->family = AF_INET; +#endif + temp->port = 0; + + /* Initialize our socket. */ + /* add log message what we're creating */ + temp->sock = socket(temp->family, SOCK_DGRAM, IPPROTO_UDP); /* Check for socket error. */ #ifdef WIN32 @@ -185,6 +310,7 @@ Networking_Core *new_networking(IP ip, uint16_t port) #else if (temp->sock < 0) { + fprintf(stderr, "Failed to get a socket?! %u, %s\n", errno, strerror(errno)); free(temp); return NULL; } @@ -205,7 +331,7 @@ Networking_Core *new_networking(IP ip, uint16_t port) return -1; */ - /* Enable broadcast on socket. */ + /* Enable broadcast on socket */ int broadcast = 1; setsockopt(temp->sock, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)); @@ -219,10 +345,140 @@ Networking_Core *new_networking(IP ip, uint16_t port) fcntl(temp->sock, F_SETFL, O_NONBLOCK, 1); #endif - /* Bind our socket to port PORT and address 0.0.0.0 */ - ADDR addr = {AF_INET, htons(port), ip, {0}}; - bind(temp->sock, (struct sockaddr *)&addr, sizeof(addr)); - return temp; +#ifdef LOGGING + loginit(ntohs(port)); +#endif + + /* Bind our socket to port PORT and the given IP address (usually 0.0.0.0 or ::) */ + uint16_t *portptr = NULL; + struct sockaddr_storage addr; + size_t addrsize; +#ifdef TOX_ENABLE_IPV6 + + if (temp->family == AF_INET) { + IP4 ip4 = ip.ip4; +#else + IP4 ip4 = ip; +#endif + addrsize = sizeof(struct sockaddr_in); + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; + addr4->sin_family = AF_INET; + addr4->sin_port = 0; + addr4->sin_addr = ip4.in_addr; + + portptr = &addr4->sin_port; +#ifdef TOX_ENABLE_IPV6 + } else if (temp->family == AF_INET6) + { + addrsize = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; + addr6->sin6_family = AF_INET6; + addr6->sin6_port = 0; + addr6->sin6_addr = ip.ip6.in6_addr; + + addr6->sin6_flowinfo = 0; + addr6->sin6_scope_id = 0; + + portptr = &addr6->sin6_port; + } else + return NULL; + + if (ip.family == AF_INET6) + { + char ipv6only = 0; +#ifdef LOGGING + int res = +#endif + setsockopt(temp->sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&ipv6only, sizeof(ipv6only)); +#ifdef LOGGING + + if (res < 0) { + sprintf(logbuffer, + "Failed to enable dual-stack on IPv6 socket, won't be able to receive from/send to IPv4 addresses. (%u, %s)\n", + errno, strerror(errno)); + loglog(logbuffer); + } else + loglog("Embedded IPv4 addresses enabled successfully.\n"); + +#endif + + /* multicast local nodes */ + struct ipv6_mreq mreq; + memset(&mreq, 0, sizeof(mreq)); + mreq.ipv6mr_multiaddr.s6_addr[ 0] = 0xFF; + mreq.ipv6mr_multiaddr.s6_addr[ 1] = 0x02; + mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01; + mreq.ipv6mr_interface = 0; +#ifdef LOGGING + res = +#endif + setsockopt(temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); +#ifdef LOGGING + + if (res < 0) { + sprintf(logbuffer, "Failed to activate local multicast membership. (%u, %s)\n", + errno, strerror(errno)); + loglog(logbuffer); + } else + loglog("Local multicast group FF02::1 joined successfully.\n"); + +#endif + } + +#endif + + /* a hanging program or a different user might block the standard port; + * as long as it isn't a parameter coming from the commandline, + * try a few ports after it, to see if we can find a "free" one + * + * if we go on without binding, the first sendto() automatically binds to + * a free port chosen by the system (i.e. anything from 1024 to 65535) + * + * returning NULL after bind fails has both advantages and disadvantages: + * advantage: + * we can rely on getting the port in the range 33445..33450, which + * enables us to tell joe user to open their firewall to a small range + * + * disadvantage: + * some clients might not test return of tox_new(), blindly assuming that + * it worked ok (which it did previously without a successful bind) + */ + uint16_t port_to_try = port; + *portptr = htons(port_to_try); + int tries, res; + + for (tries = TOX_PORTRANGE_FROM; tries <= TOX_PORTRANGE_TO; tries++) + { + res = bind(temp->sock, (struct sockaddr *)&addr, addrsize); + + if (!res) { + temp->port = *portptr; +#ifdef LOGGING + sprintf(logbuffer, "Bound successfully to %s:%u.\n", ip_ntoa(&ip), ntohs(temp->port)); + loglog(logbuffer); +#endif + + /* errno isn't reset on success, only set on failure, the failed + * binds with parallel clients yield a -EPERM to the outside if + * errno isn't cleared here */ + if (tries > 0) + errno = 0; + + return temp; + } + + port_to_try++; + + if (port_to_try > TOX_PORTRANGE_TO) + port_to_try = TOX_PORTRANGE_FROM; + + *portptr = htons(port_to_try); + } + + fprintf(stderr, "Failed to bind socket: %u, %s (IP/Port: %s:%u\n", errno, + strerror(errno), ip_ntoa(&ip), port); + kill_networking(temp); + return NULL; } /* Function to cleanup networking stuff. */ @@ -236,3 +492,391 @@ void kill_networking(Networking_Core *net) free(net); return; } + +/* ip_equal + * compares two IPAny structures + * unset means unequal + * + * returns 0 when not equal or when uninitialized + */ +int ip_equal(IP *a, IP *b) +{ + if (!a || !b) + return 0; + +#ifdef TOX_ENABLE_IPV6 + + /* same family */ + if (a->family == b->family) { + if (a->family == AF_INET) + return (a->ip4.in_addr.s_addr == b->ip4.in_addr.s_addr); + else if (a->family == AF_INET6) + return IN6_ARE_ADDR_EQUAL(&a->ip6.in6_addr, &b->ip6.in6_addr); + else + return 0; + } + + /* different family: check on the IPv6 one if it is the IPv4 one embedded */ + if ((a->family == AF_INET) && (b->family == AF_INET6)) { + if (IN6_IS_ADDR_V4COMPAT(&b->ip6.in6_addr)) + return (a->ip4.in_addr.s_addr == b->ip6.uint32[3]); + } else if ((a->family == AF_INET6) && (b->family == AF_INET)) { + if (IN6_IS_ADDR_V4COMPAT(&a->ip6.in6_addr)) + return (a->ip6.uint32[3] == b->ip4.in_addr.s_addr); + } + + return 0; +#else + return (a->uint32 == b->uint32); +#endif +}; + +/* ipport_equal + * compares two IPAny_Port structures + * unset means unequal + * + * returns 0 when not equal or when uninitialized + */ +int ipport_equal(IP_Port *a, IP_Port *b) +{ + if (!a || !b) + return 0; + + if (!a->port || (a->port != b->port)) + return 0; + + return ip_equal(&a->ip, &b->ip); +}; + +/* nulls out ip */ +void ip_reset(IP *ip) +{ + if (!ip) + return; + +#ifdef TOX_ENABLE_IPV6 + memset(ip, 0, sizeof(IP)); +#else + ip->uint32 = 0; +#endif +}; + +/* nulls out ip, sets family according to flag */ +void ip_init(IP *ip, uint8_t ipv6enabled) +{ + if (!ip) + return; + +#ifdef TOX_ENABLE_IPV6 + memset(ip, 0, sizeof(IP)); + ip->family = ipv6enabled ? AF_INET6 : AF_INET; +#else + ip->uint32 = 0; +#endif +}; + +/* checks if ip is valid */ +int ip_isset(IP *ip) +{ + if (!ip) + return 0; + +#ifdef TOX_ENABLE_IPV6 + return (ip->family != 0); +#else + return (ip->uint32 != 0); +#endif +}; + +/* 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); +}; + +/* copies an ip structure (careful about direction!) */ +void ip_copy(IP *target, IP *source) +{ + if (!source || !target) + return; + + memcpy(target, source, sizeof(IP)); +}; + +/* copies an ip_port structure (careful about direction!) */ +void ipport_copy(IP_Port *target, IP_Port *source) +{ + if (!source || !target) + return; + + memcpy(target, source, sizeof(IP_Port)); +}; + +/* ip_ntoa + * converts ip into a string + * uses a static buffer, so mustn't used multiple times in the same output + */ +/* there would be INET6_ADDRSTRLEN, but it might be too short for the error message */ +static char addresstext[96]; +const char *ip_ntoa(IP *ip) +{ + if (ip) { +#ifdef TOX_ENABLE_IPV6 + + if (ip->family == AF_INET) { + addresstext[0] = 0; + struct in_addr *addr = (struct in_addr *)&ip->ip4; + inet_ntop(ip->family, addr, addresstext, sizeof(addresstext)); + } else if (ip->family == AF_INET6) { + addresstext[0] = '['; + struct in6_addr *addr = (struct in6_addr *)&ip->ip6; + inet_ntop(ip->family, addr, &addresstext[1], sizeof(addresstext) - 3); + size_t len = strlen(addresstext); + addresstext[len] = ']'; + addresstext[len + 1] = 0; + } else + snprintf(addresstext, sizeof(addresstext), "(IP invalid, family %u)", ip->family); + +#else + addresstext[0] = 0; + struct in_addr *addr = (struct in_addr *)&ip; + inet_ntop(AF_INET, addr, addresstext, sizeof(addresstext)); +#endif + } else + snprintf(addresstext, sizeof(addresstext), "(IP invalid: NULL)"); + + /* brute force protection against lacking termination */ + addresstext[sizeof(addresstext) - 1] = 0; + return addresstext; +}; + +/* + * addr_parse_ip + * directly parses the input into an IP structure + * tries IPv4 first, then IPv6 + * + * input + * address: dotted notation (IPv4: quad, IPv6: 16) or colon notation (IPv6) + * + * output + * IP: family and the value is set on success + * + * returns 1 on success, 0 on failure + */ + +int addr_parse_ip(const char *address, IP *to) +{ + if (!address || !to) + return 0; + +#ifdef TOX_ENABLE_IPV6 + struct in_addr addr4; + + if (1 == inet_pton(AF_INET, address, &addr4)) { + to->family = AF_INET; + to->ip4.in_addr = addr4; + return 1; + }; + + struct in6_addr addr6; + + if (1 == inet_pton(AF_INET6, address, &addr6)) { + to->family = AF_INET6; + to->ip6.in6_addr = addr6; + return 1; + }; + +#else + struct in_addr addr4; + + if (1 == inet_pton(AF_INET, address, &addr4)) { + to->in_addr = addr4; + return 1; + }; + +#endif + + return 0; +}; + +/* + * addr_resolve(): + * uses getaddrinfo to resolve an address into an IP address + * uses the first IPv4/IPv6 addresses returned by getaddrinfo + * + * input + * address: a hostname (or something parseable to an IP address) + * to: to.family MUST be initialized, either set to a specific IP version + * (AF_INET/AF_INET6) or to the unspecified AF_UNSPEC (= 0), if both + * IP versions are acceptable + * extra can be NULL and is only set in special circumstances, see returns + * + * returns in *to a valid IPAny (v4/v6), + * prefers v6 if ip.family was AF_UNSPEC and both available + * returns in *extra an IPv4 address, if family was AF_UNSPEC and *to is AF_INET6 + * returns 0 on failure + */ + +int addr_resolve(const char *address, IP *to, IP *extra) +{ + if (!address || !to) + return 0; + + sa_family_t family; +#ifdef TOX_ENABLE_IPV6 + family = to->family; +#else + family = AF_INET; +#endif + + struct addrinfo *server = NULL; + struct addrinfo *walker = NULL; + struct addrinfo hints; + int rc; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses. + + if (at_startup() != 0) + return 0; + + rc = getaddrinfo(address, NULL, &hints, &server); + + // Lookup failed. + if (rc != 0) { + return 0; + } + +#ifdef TOX_ENABLE_IPV6 + IP4 ip4; + memset(&ip4, 0, sizeof(ip4)); + IP6 ip6; + memset(&ip6, 0, sizeof(ip6)); +#endif + + for (walker = server; (walker != NULL) && (rc != 3); walker = walker->ai_next) { + switch (walker->ai_family) { + case AF_INET: + if (walker->ai_family == family) { /* AF_INET requested, done */ + struct sockaddr_in *addr = (struct sockaddr_in *)walker->ai_addr; +#ifdef TOX_ENABLE_IPV6 + to->ip4.in_addr = addr->sin_addr; +#else + to->in_addr = addr->sin_addr; +#endif + rc = 3; + } + +#ifdef TOX_ENABLE_IPV6 + else if (!(rc & 1)) { /* AF_UNSPEC requested, store away */ + struct sockaddr_in *addr = (struct sockaddr_in *)walker->ai_addr; + ip4.in_addr = addr->sin_addr; + rc |= 1; + } + +#endif + break; /* switch */ +#ifdef TOX_ENABLE_IPV6 + + case AF_INET6: + if (walker->ai_family == family) { /* AF_INET6 requested, done */ + if (walker->ai_addrlen == sizeof(struct sockaddr_in6)) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)walker->ai_addr; + to->ip6.in6_addr = addr->sin6_addr; + rc = 3; + } + } else if (!(rc & 2)) { /* AF_UNSPEC requested, store away */ + if (walker->ai_addrlen == sizeof(struct sockaddr_in6)) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)walker->ai_addr; + ip6.in6_addr = addr->sin6_addr; + rc |= 2; + } + } + + break; /* switch */ +#endif + } + } + +#ifdef TOX_ENABLE_IPV6 + + if (to->family == AF_UNSPEC) { + if (rc & 2) { + to->family = AF_INET6; + to->ip6 = ip6; + + if ((rc & 1) && (extra != NULL)) { + extra->family = AF_INET; + extra->ip4 = ip4; + } + } else if (rc & 1) { + to->family = AF_INET; + to->ip4 = ip4; + } else + rc = 0; + } + +#endif + + + freeaddrinfo(server); + return rc; +} + +/* + * addr_resolve_or_parse_ip + * resolves string into an IP address + * + * address: a hostname (or something parseable to an IP address) + * to: to.family MUST be initialized, either set to a specific IP version + * (AF_INET/AF_INET6) or to the unspecified AF_UNSPEC (= 0), if both + * IP versions are acceptable + * extra can be NULL and is only set in special circumstances, see returns + * + * returns in *tro a matching address (IPv6 or IPv4) + * returns in *extra, if not NULL, an IPv4 address, if to->family was AF_UNSPEC + * returns 1 on success + * returns 0 on failure + */ +int addr_resolve_or_parse_ip(const char *address, IP *to, IP *extra) +{ + if (!addr_resolve(address, to, extra)) + if (!addr_parse_ip(address, to)) + return 0; + + return 1; +}; + +#ifdef LOGGING +static void loglogdata(char *message, uint8_t *buffer, size_t buflen, IP_Port *ip_port, ssize_t res) +{ + if (res < 0) + snprintf(logbuffer, sizeof(logbuffer), "[%2u] %s %3u%c %s:%u (%u: %s) | %04x%04x\n", + buffer[0], message, buflen < 999 ? buflen : 999, 'E', + ip_ntoa(&ip_port->ip), ntohs(ip_port->port), errno, + strerror(errno), buflen > 4 ? ntohl(*(uint32_t *)&buffer[1]) : 0, + buflen > 7 ? ntohl(*(uint32_t *)(&buffer[5])) : 0); + else if ((res > 0) && (res <= buflen)) + snprintf(logbuffer, sizeof(logbuffer), "[%2u] %s %3u%c %s:%u (%u: %s) | %04x%04x\n", + buffer[0], message, res < 999 ? res : 999, res < buflen ? '<' : '=', + ip_ntoa(&ip_port->ip), ntohs(ip_port->port), 0, + "OK", buflen > 4 ? ntohl(*(uint32_t *)&buffer[1]) : 0, + buflen > 7 ? ntohl(*(uint32_t *)(&buffer[5])) : 0); + else /* empty or overwrite */ + snprintf(logbuffer, sizeof(logbuffer), "[%2u] %s %u%c%u %s:%u (%u: %s) | %04x%04x\n", + buffer[0], message, res, !res ? '0' : '>', buflen, + ip_ntoa(&ip_port->ip), ntohs(ip_port->port), 0, + "OK", buflen > 4 ? ntohl(*(uint32_t *)&buffer[1]) : 0, + buflen > 7 ? ntohl(*(uint32_t *)(&buffer[5])) : 0); + + logbuffer[sizeof(logbuffer) - 1] = 0; + loglog(logbuffer); +} +#endif diff --git a/toxcore/network.h b/toxcore/network.h index e1f9b212..4432baed 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -39,17 +39,22 @@ #include #include +typedef unsigned int sock_t; + #else // Linux includes #include #include #include +#include #include #include #include #include #include +typedef int sock_t; + #endif #ifndef VANILLA_NACL @@ -61,13 +66,20 @@ #define crypto_box_MACBYTES (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES) #endif +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif +#endif #define MAX_UDP_PACKET_SIZE 65507 #define NET_PACKET_PING_REQUEST 0 /* Ping request packet ID. */ #define NET_PACKET_PING_RESPONSE 1 /* Ping response packet ID. */ #define NET_PACKET_GET_NODES 2 /* Get nodes request packet ID. */ -#define NET_PACKET_SEND_NODES 3 /* Send nodes response packet ID. */ +#define NET_PACKET_SEND_NODES 3 /* Send nodes response packet ID for IPv4 addresses. */ +#define NET_PACKET_SEND_NODES_IPV6 4 /* Send nodes response packet ID for other addresses. */ #define NET_PACKET_HANDSHAKE 16 /* Handshake packet ID. */ #define NET_PACKET_SYNC 17 /* SYNC packet ID. */ #define NET_PACKET_DATA 18 /* Data packet ID. */ @@ -75,6 +87,10 @@ #define NET_PACKET_LAN_DISCOVERY 33 /* LAN discovery packet ID. */ #define NET_PACKET_GROUP_CHATS 48 /* Group chats packet ID. */ +#define TOX_PORTRANGE_FROM 33445 +#define TOX_PORTRANGE_TO 33455 +#define TOX_PORT_DEFAULT TOX_PORTRANGE_FROM + /* Current time, unix format */ #define unix_time() ((uint64_t)time(NULL)) @@ -83,27 +99,123 @@ typedef union { uint8_t uint8[4]; uint16_t uint16[2]; uint32_t uint32; -} IP; + struct in_addr in_addr; +} IP4; + +typedef union { + uint8_t uint8[16]; + uint16_t uint16[8]; + uint32_t uint32[4]; + struct in6_addr in6_addr; +} IP6; + +typedef struct { + sa_family_t family; + union { + IP4 ip4; + IP6 ip6; + }; +} IPAny; typedef union { struct { - IP ip; + IP4 ip; uint16_t port; /* Not used for anything right now. */ uint16_t padding; }; uint8_t uint8[8]; -} IP_Port; +} IP4_Port; +/* will replace IP_Port as soon as the complete infrastructure is in place + * removed the unused union and padding also */ typedef struct { - int16_t family; + IPAny ip; uint16_t port; - IP ip; - uint8_t zeroes[8]; -#ifdef ENABLE_IPV6 - uint8_t zeroes2[12]; +} IPAny_Port; + +/* #undef TOX_ENABLE_IPV6 */ +#define TOX_ENABLE_IPV6 +#ifdef TOX_ENABLE_IPV6 +#define TOX_ENABLE_IPV6_DEFAULT 1 +typedef IPAny IP; +typedef IPAny_Port IP_Port; +#else +#define TOX_ENABLE_IPV6_DEFAULT 0 +typedef IP4 IP; +typedef IP4_Port IP_Port; #endif -} ADDR; + +/* ip_ntoa + * converts ip into a string + * uses a static buffer, so mustn't used multiple times in the same output + */ +const char *ip_ntoa(IP *ip); + +/* ip_equal + * compares two IPAny structures + * unset means unequal + * + * returns 0 when not equal or when uninitialized + */ +int ip_equal(IP *a, IP *b); + +/* ipport_equal + * compares two IPAny_Port structures + * unset means unequal + * + * returns 0 when not equal or when uninitialized + */ +int ipport_equal(IP_Port *a, IP_Port *b); + +/* nulls out ip */ +void ip_reset(IP *ip); +/* nulls out ip, sets family according to flag */ +void ip_init(IP *ip, uint8_t ipv6enabled); +/* checks if ip is valid */ +int ip_isset(IP *ip); +/* checks if ip is valid */ +int ipport_isset(IP_Port *ipport); +/* copies an ip structure */ +void ip_copy(IP *target, IP *source); +/* copies an ip_port structure */ +void ipport_copy(IP_Port *target, IP_Port *source); + +/* + * addr_resolve(): + * uses getaddrinfo to resolve an address into an IP address + * uses the first IPv4/IPv6 addresses returned by getaddrinfo + * + * input + * address: a hostname (or something parseable to an IP address) + * to: to.family MUST be initialized, either set to a specific IP version + * (AF_INET/AF_INET6) or to the unspecified AF_UNSPEC (= 0), if both + * IP versions are acceptable + * extra can be NULL and is only set in special circumstances, see returns + * + * returns in *to a valid IPAny (v4/v6), + * prefers v6 if ip.family was AF_UNSPEC and both available + * returns in *extra an IPv4 address, if family was AF_UNSPEC and *to is AF_INET6 + * returns 0 on failure + */ +int addr_resolve(const char *address, IP *to, IP *extra); + +/* + * addr_resolve_or_parse_ip + * resolves string into an IP address + * + * address: a hostname (or something parseable to an IP address) + * to: to.family MUST be initialized, either set to a specific IP version + * (AF_INET/AF_INET6) or to the unspecified AF_UNSPEC (= 0), if both + * IP versions are acceptable + * extra can be NULL and is only set in special circumstances, see returns + * + * returns in *tro a matching address (IPv6 or IPv4) + * returns in *extra, if not NULL, an IPv4 address, if to->family was AF_UNSPEC + * returns 1 on success + * returns 0 on failure + */ +int addr_resolve_or_parse_ip(const char *address, IP *to, IP *extra); /* Function to receive data, ip and port of sender is put into ip_port. * Packet data is put into data. @@ -118,13 +230,11 @@ typedef struct { typedef struct { Packet_Handles packethandlers[256]; - /* Our UDP socket. */ -#ifdef WIN32 - unsigned int sock; -#else - int sock; -#endif + /* Our UDP socket. */ + sa_family_t family; + uint16_t port; + sock_t sock; } Networking_Core; /* return current time in milleseconds since the epoch. */ @@ -137,12 +247,7 @@ uint32_t random_int(void); /* Basic network functions: */ /* Function to send packet(data) of length length to ip_port. */ -#ifdef WIN32 -int sendpacket(unsigned int sock, IP_Port ip_port, uint8_t *data, uint32_t length); -#else -int sendpacket(int sock, IP_Port ip_port, uint8_t *data, uint32_t length); -#endif - +int sendpacket(Networking_Core *net, IP_Port ip_port, uint8_t *data, uint32_t length); /* Function to call when packet beginning with byte is received. */ void networking_registerhandler(Networking_Core *net, uint8_t byte, packet_handler_callback cb, void *object); @@ -163,5 +268,4 @@ Networking_Core *new_networking(IP ip, uint16_t port); /* Function to cleanup networking stuff (doesn't do much right now). */ void kill_networking(Networking_Core *net); - #endif diff --git a/toxcore/ping.c b/toxcore/ping.c index 3a189f23..56ce2f59 100644 --- a/toxcore/ping.c +++ b/toxcore/ping.c @@ -100,7 +100,8 @@ bool is_pinging(void *ping, IP_Port ipp, uint64_t ping_id) // O(n) TODO: Repl { PING *png = ping; - if (ipp.ip.uint32 == 0 && ping_id == 0) + /* shouldn't that be an OR ? */ + if (!ip_isset(&ipp.ip) && ping_id == 0) return false; size_t i, id; @@ -111,7 +112,8 @@ bool is_pinging(void *ping, IP_Port ipp, uint64_t ping_id) // O(n) TODO: Repl id = (png->pos_pings + i) % PING_NUM_MAX; /* ping_id = 0 means match any id. */ - if ((ipp_eq(png->pings[id].ipp, ipp) || ipp.ip.uint32 == 0) && (png->pings[id].id == ping_id || ping_id == 0)) { + if ((!ip_isset(&ipp.ip) || ipport_equal(&png->pings[id].ipp, &ipp)) && + (png->pings[id].id == ping_id || ping_id == 0)) { return true; } } @@ -147,7 +149,7 @@ int send_ping_request(void *ping, Net_Crypto *c, IP_Port ipp, uint8_t *client_id if (rc != sizeof(ping_id) + ENCRYPTION_PADDING) return 1; - return sendpacket(c->lossless_udp->net->sock, ipp, pk, sizeof(pk)); + return sendpacket(c->lossless_udp->net, ipp, pk, sizeof(pk)); } int send_ping_response(Net_Crypto *c, IP_Port ipp, uint8_t *client_id, uint64_t ping_id) @@ -172,7 +174,7 @@ int send_ping_response(Net_Crypto *c, IP_Port ipp, uint8_t *client_id, uint64_t if (rc != sizeof(ping_id) + ENCRYPTION_PADDING) return 1; - return sendpacket(c->lossless_udp->net->sock, ipp, pk, sizeof(pk)); + return sendpacket(c->lossless_udp->net, ipp, pk, sizeof(pk)); } int handle_ping_request(void *object, IP_Port source, uint8_t *packet, uint32_t length) diff --git a/toxcore/tox.c b/toxcore/tox.c index 417f1af3..80d64626 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -441,14 +441,20 @@ int tox_group_message_send(void *tox, int groupnumber, uint8_t *message, uint32_ /******************END OF GROUP CHAT FUNCTIONS************************/ -/* Use this function to bootstrap the client. +/* Use these functions to bootstrap the client. * Sends a get nodes request to the given node with ip port and public_key. */ -void tox_bootstrap(void *tox, IP_Port ip_port, uint8_t *public_key) +void tox_bootstrap_from_ip(void *tox, IP_Port ip_port, uint8_t *public_key) { Messenger *m = tox; DHT_bootstrap(m->dht, ip_port, public_key); } +int tox_bootstrap_from_address(void *tox, const char *address, + uint8_t ipv6enabled, uint16_t port, uint8_t *public_key) +{ + Messenger *m = tox; + return DHT_bootstrap_from_address(m->dht, address, ipv6enabled, port, public_key); +}; /* return 0 if we are not connected to the DHT. * return 1 if we are. @@ -464,9 +470,9 @@ int tox_isconnected(void *tox) * return allocated instance of tox on success. * return 0 if there are problems. */ -void *tox_new(void) +void *tox_new(uint8_t ipv6enabled) { - return initMessenger(); + return initMessenger(ipv6enabled); } /* Run this before closing shop. diff --git a/toxcore/tox.h b/toxcore/tox.h index 6d5db49f..89242f1f 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -26,6 +26,22 @@ #include +#ifdef WIN32 +#ifndef WINVER +//Windows XP +#define WINVER 0x0501 +#endif + +#include +#include +#include + +#else + +#include + +#endif + #ifdef __cplusplus extern "C" { #endif @@ -36,19 +52,61 @@ extern "C" { #define TOX_FRIEND_ADDRESS_SIZE (TOX_CLIENT_ID_SIZE + sizeof(uint32_t) + sizeof(uint16_t)) +#define TOX_PORTRANGE_FROM 33445 +#define TOX_PORTRANGE_TO 33455 +#define TOX_PORT_DEFAULT TOX_PORTRANGE_FROM typedef union { - uint8_t c[4]; + uint8_t c[4]; uint16_t s[2]; uint32_t i; -} tox_IP; +} tox_IP4; + + +typedef union { + uint8_t uint8[16]; + uint16_t uint16[8]; + uint32_t uint32[4]; + struct in6_addr in6_addr; +} tox_IP6; typedef struct { - tox_IP ip; - uint16_t port; - /* Not used for anything right now. */ - uint16_t padding; -} tox_IP_Port; + sa_family_t family; + union { + tox_IP4 ip4; + tox_IP6 ip6; + }; +} tox_IPAny; + +typedef union { + struct { + tox_IP4 ip; + uint16_t port; + /* Not used for anything right now. */ + uint16_t padding; + }; + uint8_t uint8[8]; +} tox_IP4_Port; + +/* will replace IP_Port as soon as the complete infrastructure is in place + * removed the unused union and padding also */ +typedef struct { + tox_IPAny ip; + uint16_t port; +} tox_IPAny_Port; + +/* #undef TOX_ENABLE_IPV6 */ +#define TOX_ENABLE_IPV6 +#ifdef TOX_ENABLE_IPV6 +#define TOX_ENABLE_IPV6_DEFAULT 1 +typedef tox_IPAny tox_IP; +typedef tox_IPAny_Port tox_IP_Port; +#else +#define TOX_ENABLE_IPV6_DEFAULT 0 +typedef tox_IP4 tox_IP; +typedef tox_IP4_Port tox_IP_Port; +#endif + /* Errors for m_addfriend * FAERR - Friend Add Error @@ -342,22 +400,46 @@ int tox_group_message_send(Tox *tox, int groupnumber, uint8_t *message, uint32_t /******************END OF GROUP CHAT FUNCTIONS************************/ -/* Use this function to bootstrap the client. - * Sends a get nodes request to the given node with ip port and public_key. +/* + * Use these two functions to bootstrap the client. */ -void tox_bootstrap(Tox *tox, tox_IP_Port ip_port, uint8_t *public_key); +/* Sends a "get nodes" request to the given node with ip, port and public_key + * to setup connections + */ +void tox_bootstrap_from_ip(Tox *tox, tox_IP_Port ip_port, uint8_t *public_key); +/* Resolves address into an IP address. If successful, sends a "get nodes" + * request to the given node with ip, port and public_key to setup connections + * + * address can be a hostname or an IP address (IPv4 or IPv6). + * if ipv6enabled is 0 (zero), the resolving sticks STRICTLY to IPv4 addresses + * if ipv6enabled is not 0 (zero), the resolving looks for IPv6 addresses first, + * then IPv4 addresses. + * + * returns 1 if the address could be converted into an IP address + * returns 0 otherwise + */ +int tox_bootstrap_from_address(Tox *tox, const char *address, uint8_t ipv6enabled, + uint16_t port, uint8_t *public_key); /* return 0 if we are not connected to the DHT. * return 1 if we are. */ int tox_isconnected(Tox *tox); -/* Run this at startup. +/* + * Run this function at startup. + * + * Initializes a tox structure + * The type of communication socket depends on ipv6enabled: + * If set to 0 (zero), creates an IPv4 socket which subsequently only allows + * IPv4 communication + * If set to anything else, creates an IPv6 socket which allows both IPv4 AND + * IPv6 communication * * return allocated instance of tox on success. * return 0 if there are problems. */ -Tox *tox_new(void); +Tox *tox_new(uint8_t ipv6enabled); /* Run this before closing shop. * Free all datastructures. */ diff --git a/toxcore/util.c b/toxcore/util.c index 1728ea21..8960fe36 100644 --- a/toxcore/util.c +++ b/toxcore/util.c @@ -10,11 +10,12 @@ #endif #include -#include -#include +/* for CLIENT_ID_SIZE */ #include "DHT.h" +#include "util.h" + uint64_t now() { return time(NULL); @@ -32,11 +33,6 @@ uint64_t random_64b() return r; } -bool ipp_eq(IP_Port a, IP_Port b) -{ - return (a.ip.uint32 == b.ip.uint32) && (a.port == b.port); -} - bool id_eq(uint8_t *dest, uint8_t *src) { return memcmp(dest, src, CLIENT_ID_SIZE) == 0; @@ -46,3 +42,33 @@ void id_cpy(uint8_t *dest, uint8_t *src) { memcpy(dest, src, CLIENT_ID_SIZE); } + +#ifdef LOGGING +time_t starttime = 0; +char logbuffer[512]; +static FILE *logfile = NULL; +void loginit(uint16_t port) +{ + if (logfile) + fclose(logfile); + + sprintf(logbuffer, "%u-%u.log", ntohs(port), now()); + logfile = fopen(logbuffer, "w"); + starttime = now(); +}; +void loglog(char *text) +{ + if (logfile) { + fprintf(logfile, "%4u ", now() - starttime); + fprintf(logfile, text); + fflush(logfile); + } +}; +void logexit() +{ + if (logfile) { + fclose(logfile); + logfile = NULL; + } +}; +#endif diff --git a/toxcore/util.h b/toxcore/util.h index 90a3c8e4..71be84ca 100644 --- a/toxcore/util.h +++ b/toxcore/util.h @@ -5,8 +5,24 @@ * Copyright 2013 plutooo */ +#ifndef __UTIL_H__ +#define __UTIL_H__ + +#include +#include + uint64_t now(); uint64_t random_64b(); -bool ipp_eq(IP_Port a, IP_Port b); bool id_eq(uint8_t *dest, uint8_t *src); void id_cpy(uint8_t *dest, uint8_t *src); + +#undef LOGGING +/* #define LOGGING */ +#ifdef LOGGING +extern char logbuffer[512]; +void loginit(uint16_t port); +void loglog(char *text); +void logexit(); +#endif + +#endif /* __UTIL_H__ */