From d2b572ede15d4b1684e1fdc15638f144cdbfd416 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Wed, 21 Sep 2016 21:22:05 -0400 Subject: [PATCH] Add ipv6 support for bootstrap nodes and refactor parsing code --- src/bootstrap.c | 250 ++++++++++++++++++++++++++++++----------------- src/bootstrap.h | 5 + src/curl_util.h | 5 + src/misc_tools.c | 26 +++++ src/misc_tools.h | 9 ++ 5 files changed, 204 insertions(+), 91 deletions(-) diff --git a/src/bootstrap.c b/src/bootstrap.c index 22aa5f0..51fd760 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -53,6 +53,7 @@ extern struct user_settings *user_settings; #define NODE_OFFLINE_TIMOUT (60*60*24*2) #define IP_MAX_SIZE 45 +#define IP_MIN_SIZE 7 #define PORT_MAX_SIZE 5 #define LAST_SCAN_JSON_KEY "\"last_scan\":" @@ -61,6 +62,9 @@ extern struct user_settings *user_settings; #define IPV4_JSON_KEY "\"ipv4\":\"" #define IPV4_JSON_KEY_LEN (sizeof(IPV4_JSON_KEY) - 1) +#define IPV6_JSON_KEY "\"ipv6\":\"" +#define IPV6_JSON_KEY_LEN (sizeof(IPV6_JSON_KEY) - 1) + #define PORT_JSON_KEY "\"port\":" #define PORT_JSON_KEY_LEN (sizeof(PORT_JSON_KEY) - 1) @@ -76,7 +80,11 @@ extern struct user_settings *user_settings; #define MAXNODES 50 struct Node { char ip4[IP_MAX_SIZE + 1]; + bool have_ip4; + char ip6[IP_MAX_SIZE + 1]; + bool have_ip6; + char key[TOX_PUBLIC_KEY_SIZE]; uint16_t port; }; @@ -257,6 +265,142 @@ static void get_nodeslist_path(char *buf, size_t buf_size) } } +/* Return true if json encoded string s contains a valid IP address and puts address in ip_buf. + * + * ip_type should be set to 1 for ipv4 address, or 0 for ipv6 addresses. + * ip_buf must have room for at least IP_MAX_SIZE + 1 bytes. + */ +static bool extract_val_ip(const char *s, char *ip_buf, unsigned short int ip_type) +{ + int ip_len = char_find(0, s, '"'); + + if (ip_len < IP_MIN_SIZE || ip_len > IP_MAX_SIZE) { + return false; + } + + memcpy(ip_buf, s, ip_len); + ip_buf[ip_len] = 0; + + return (ip_type == 1) ? is_ip4_address(ip_buf) : is_ip6_address(ip_buf); +} + +/* Extracts the port from json encoded string s. + * + * Return port number on success. + * Return 0 on failure. + */ +static uint16_t extract_val_port(const char *s) +{ + long int port = strtol(s, NULL, 10); + return (port > 0 && port <= MAX_PORT_RANGE) ? port : 0; +} + +/* Extracts the last pinged value from json encoded string s. + * + * Return timestamp on success. + * Return -1 on failure. + */ +static long long int extract_val_last_pinged(const char *s) +{ + long long int last_pinged = strtoll(s, NULL, 10); + return (last_pinged <= 0) ? -1 : last_pinged; +} + +/* Extracts DHT public key from json encoded string s and puts key in key_buf. + * key_buf must have room for at least TOX_PUBLIC_KEY_SIZE * 2 + 1 bytes. + * + * Return number of bytes copied to key_buf on success. + * Return -1 on failure. + */ +static int extract_val_pk(const char *s, char *key_buf) +{ + + int key_len = char_find(0, s, '"'); + + if (key_len != TOX_PUBLIC_KEY_SIZE * 2) { + return -1; + } + + memcpy(key_buf, s, key_len); + key_buf[key_len] = 0; + + return key_len; +} + +/* Extracts values from json formatted string, validats them, and puts them in node. + * + * Return 0 on success. + * Return -1 if line is empty. + * Return -2 if line does not appear to be a valid nodes list entry. + * Return -3 if node appears to be offline. + * Return -4 if entry does not contain either a valid ipv4 or ipv6 address. + * Return -5 if port value is invalid. + * Return -6 if public key is invalid. + */ +static int extract_node(const char *line, struct Node *node) +{ + if (!line) { + return -1; + } + + const char *ip4_start = strstr(line, IPV4_JSON_KEY); + const char *ip6_start = strstr(line, IPV6_JSON_KEY); + const char *port_start = strstr(line, PORT_JSON_KEY); + const char *key_start = strstr(line, PK_JSON_KEY); + const char *last_pinged_str = strstr(line, LAST_PING_JSON_KEY); + + if (!ip4_start || !ip6_start || !port_start || !key_start || !last_pinged_str) { + return -2; + } + + long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN); + + if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) { + return -3; + } + + char ip4_string[IP_MAX_SIZE + 1]; + bool have_ip4 = extract_val_ip(ip4_start + IPV4_JSON_KEY_LEN, ip4_string, 1); + + char ip6_string[IP_MAX_SIZE + 1]; + bool have_ip6 = extract_val_ip(ip6_start + IPV6_JSON_KEY_LEN, ip6_string, 0); + + if (!have_ip6 && !have_ip4) { + return -4; + } + + uint16_t port = extract_val_port(port_start + PORT_JSON_KEY_LEN); + + if (port == 0) { + return -5; + } + + char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1]; + int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string); + + if (key_len == -1) { + return -6; + } + + if (hex_string_to_bin(key_string, key_len, node->key, TOX_PUBLIC_KEY_SIZE) == -1) { + return -6; + } + + if (have_ip4) { + snprintf(node->ip4, sizeof(node->ip4), "%s", ip4_string); + node->have_ip4 = true; + } + + if (have_ip6) { + snprintf(node->ip6, sizeof(node->ip6), "%s", ip6_string); + node->have_ip6 = true; + } + + node->port = port; + + return 0; +} + /* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL. * TODO: Parse json using a proper library? * @@ -296,92 +440,11 @@ int load_DHT_nodeslist(void) const char *line_start = line; while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY)) && Nodes.count < MAXNODES) { - /* Extract IPv4 address */ - const char *ip_start = strstr(line_start, IPV4_JSON_KEY); + size_t idx = Nodes.count; - if (ip_start == NULL) { - continue; + if (extract_node(line_start, &Nodes.list[idx]) == 0) { + ++Nodes.count; } - - ip_start += IPV4_JSON_KEY_LEN; - int ip_len = char_find(0, ip_start, '"'); - - if (ip_len == 0 || ip_len > IP_MAX_SIZE) { - continue; - } - - char ipv4_string[ip_len + 1]; - memcpy(ipv4_string, ip_start, ip_len); - ipv4_string[ip_len] = 0; - - /* ignore domains because we don't want toxcore doing DNS requests during bootstrap. */ - if (!is_ip4_address(ipv4_string)) { - continue; - } - - /* Extract port */ - const char *port_start = strstr(ip_start, PORT_JSON_KEY); - - if (!port_start) { - continue; - } - - port_start += PORT_JSON_KEY_LEN; - int port_len = char_find(0, port_start, ','); - - if (port_len == 0 || port_len > PORT_MAX_SIZE) { - continue; - } - - char port_string[port_len + 1]; - memcpy(port_string, port_start, port_len); - port_string[port_len] = 0; - - long int port = strtol(port_string, NULL, 10); - - if (port <= 0 || port > MAX_PORT_RANGE) { - continue; - } - - /* Extract key */ - const char *key_start = strstr(port_start, PK_JSON_KEY); - - if (!key_start) { - continue; - } - - key_start += PK_JSON_KEY_LEN; - int key_len = char_find(0, key_start, '"'); - - if (key_len != TOX_PUBLIC_KEY_SIZE * 2) { - continue; - } - - char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1]; - memcpy(key_string, key_start, TOX_PUBLIC_KEY_SIZE * 2); - key_string[TOX_PUBLIC_KEY_SIZE * 2] = 0; - - /* Check last pinged value and ignore nodes that appear offline */ - const char *last_pinged_str = strstr(key_start, LAST_PING_JSON_KEY); - - if (!last_pinged_str) { - continue; - } - - last_pinged_str += LAST_PING_JSON_KEY_LEN; - long long int last_pinged = strtoll(last_pinged_str, NULL, 10); - - if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) { - continue; - } - - /* Add entry to nodes list */ - size_t idx = Nodes.count++; - snprintf(Nodes.list[idx].ip4, sizeof(Nodes.list[idx].ip4), "%s", ipv4_string); - Nodes.list[idx].port = port; - - if (hex_string_to_bin(key_string, key_len, Nodes.list[idx].key, TOX_PUBLIC_KEY_SIZE) == -1) - continue; } /* If nodeslist does not contain any valid entries we set the last_scan value @@ -409,19 +472,24 @@ static void DHT_bootstrap(Tox *m) size_t i; for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) { - size_t node = rand() % Nodes.count; - TOX_ERR_BOOTSTRAP err; - tox_bootstrap(m, Nodes.list[node].ip4, Nodes.list[node].port, (uint8_t *) Nodes.list[node].key, &err); + struct Node *node = &Nodes.list[rand() % Nodes.count]; + const char *addr = node->have_ip4 ? node->ip4 : node->ip6; - if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to bootstrap %s:%d\n", Nodes.list[node].ip4, Nodes.list[node].port); + if (!addr) { + continue; } - tox_add_tcp_relay(m, Nodes.list[node].ip4, Nodes.list[node].port, (uint8_t *) Nodes.list[node].key, &err); + tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err); if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to add TCP relay %s:%d\n", Nodes.list[node].ip4, Nodes.list[node].port); + fprintf(stderr, "Failed to bootstrap %s:%d\n", addr, node->port); + } + + tox_add_tcp_relay(m, addr, node->port, (uint8_t *) node->key, &err); + + if (err != TOX_ERR_BOOTSTRAP_OK) { + fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port); } } } diff --git a/src/bootstrap.h b/src/bootstrap.h index 0159d3d..b711a24 100644 --- a/src/bootstrap.h +++ b/src/bootstrap.h @@ -20,6 +20,9 @@ * */ +#ifndef BOOTSTRAP_H +#define BOOTSTRAP_H + /* Manages connection to the Tox DHT network. */ void do_tox_connection(Tox *m); @@ -32,3 +35,5 @@ void do_tox_connection(Tox *m); * Return -3 if nodeslist file does not contain any valid node entries. */ int load_DHT_nodeslist(void); + +#endif /* BOOTSTRAP_H */ diff --git a/src/curl_util.h b/src/curl_util.h index e38da56..cefb5db 100644 --- a/src/curl_util.h +++ b/src/curl_util.h @@ -20,6 +20,9 @@ * */ +#ifndef CURL_UTIL_H +#define CURL_UTIL_H + /* List based on Mozilla's recommended configurations for modern browsers */ #define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" @@ -48,3 +51,5 @@ int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uin * Returns size of bytes written to the data buffer. */ size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer); + +#endif /* CURL_UTIL_H */ diff --git a/src/misc_tools.c b/src/misc_tools.c index 98bfdaf..8602538 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -487,3 +487,29 @@ bool is_ip4_address(const char *address) struct sockaddr_in s_addr; return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0; } + +/* Return true if address roughly appears to be a valid ipv6 address. + * + * TODO: Improve this function (inet_pton behaves strangely with ipv6). + * for now the only guarantee is that it won't return true if the + * address is a domain or ipv4 address, and should only be used if you're + * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). + */ +bool is_ip6_address(const char *address) +{ + size_t i; + size_t num_colons = 0; + char ch = 0; + + for (i = 0; (ch = address[i]); ++i) { + if (ch == '.') { + return false; + } + + if (ch == ':') { + ++num_colons; + } + } + + return num_colons > 1 && num_colons < 8; +} diff --git a/src/misc_tools.h b/src/misc_tools.h index dbd4277..ef450c9 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -164,4 +164,13 @@ void set_window_title(ToxWindow *self, const char *title, int len); /* Return true if address appears to be a valid ipv4 address. */ bool is_ip4_address(const char *address); +/* Return true if address roughly appears to be a valid ipv6 address. + * + * TODO: Improve this function (inet_pton behaves strangely with ipv6). + * for now the only guarantee is that it won't return true if the + * address is a domain or ipv4 address, and should only be used if you're + * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). + */ +bool is_ip6_address(const char *address); + #endif /* #define MISC_TOOLS_H */