1
0
mirror of https://github.com/Tha14/toxic.git synced 2024-06-29 16:27:45 +02:00

Thread nodeslist loading

This is necessary because DNS/http lookups block, and can do so for a very long time
This commit is contained in:
Jfreegman 2016-09-22 14:09:07 -04:00
parent d2b572ede1
commit 38ec96e96a
No known key found for this signature in database
GPG Key ID: 3627F3144076AE63
4 changed files with 124 additions and 45 deletions

View File

@ -37,6 +37,7 @@
extern struct arg_opts arg_opts; extern struct arg_opts arg_opts;
extern struct user_settings *user_settings; extern struct user_settings *user_settings;
extern struct Winthread Winthread;
/* URL that we get the JSON encoded nodes list from. */ /* URL that we get the JSON encoded nodes list from. */
#define NODES_LIST_URL "https://nodes.tox.chat/json" #define NODES_LIST_URL "https://nodes.tox.chat/json"
@ -77,7 +78,15 @@ extern struct user_settings *user_settings;
/* Maximum allowable size of the nodes list */ /* Maximum allowable size of the nodes list */
#define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE) #define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE)
#define MAXNODES 50
struct Thread_Data {
pthread_t tid;
pthread_attr_t attr;
pthread_mutex_t lock;
volatile bool active;
} thread_data;
#define MAX_NODES 50
struct Node { struct Node {
char ip4[IP_MAX_SIZE + 1]; char ip4[IP_MAX_SIZE + 1];
bool have_ip4; bool have_ip4;
@ -90,7 +99,7 @@ struct Node {
}; };
static struct DHT_Nodes { static struct DHT_Nodes {
struct Node list[MAXNODES]; struct Node list[MAX_NODES];
size_t count; size_t count;
uint64_t last_updated; uint64_t last_updated;
} Nodes; } Nodes;
@ -101,7 +110,6 @@ static struct DHT_Nodes {
*/ */
#define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping)) #define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping))
/* Return true if nodeslist pointed to by fp needs to be updated. /* Return true if nodeslist pointed to by fp needs to be updated.
* This will be the case if the file is empty, has an invalid format, * This will be the case if the file is empty, has an invalid format,
* or if the file is older than the given timeout. * or if the file is older than the given timeout.
@ -134,11 +142,17 @@ static bool nodeslist_needs_update(const char *nodes_path)
return true; return true;
} }
last_scan_val += LAST_SCAN_JSON_KEY_LEN; long long int last_scan = strtoll(last_scan_val + LAST_SCAN_JSON_KEY_LEN, NULL, 10);
long long int last_scan = strtoll(last_scan_val, NULL, 10);
Nodes.last_updated = last_scan;
if (timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60)) { pthread_mutex_lock(&thread_data.lock);
Nodes.last_updated = last_scan;
pthread_mutex_unlock(&thread_data.lock);
pthread_mutex_lock(&Winthread.lock);
bool is_timeout = timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60);
pthread_mutex_unlock(&Winthread.lock);
if (is_timeout) {
return true; return true;
} }
@ -158,6 +172,8 @@ static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data)
return -1; return -1;
} }
int err = -1;
struct curl_slist *headers = NULL; struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json"); headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "charsets: utf-8"); headers = curl_slist_append(headers, "charsets: utf-8");
@ -173,21 +189,21 @@ static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data)
if (proxy_ret != 0) { if (proxy_ret != 0) {
fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret); fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret);
return -1; goto on_exit;
} }
int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
if (ret != CURLE_OK) { if (ret != CURLE_OK) {
fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret); fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret);
return -1; goto on_exit;
} }
ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST); ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
if (ret != CURLE_OK) { if (ret != CURLE_OK) {
fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret); fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret);
return -1; goto on_exit;
} }
ret = curl_easy_perform(c_handle); ret = curl_easy_perform(c_handle);
@ -201,11 +217,16 @@ static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data)
if (ret != CURLE_OK) { if (ret != CURLE_OK) {
fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret); fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret);
return -1; goto on_exit;
} }
} }
return 0; err = 0;
on_exit:
curl_slist_free_all(headers);
curl_easy_cleanup(c_handle);
return err;
} }
/* Attempts to update the DHT nodeslist. /* Attempts to update the DHT nodeslist.
@ -401,15 +422,8 @@ static int extract_node(const char *line, struct Node *node)
return 0; return 0;
} }
/* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL. /* Loads the DHT nodeslist to memory from json encoded nodes file. */
* TODO: Parse json using a proper library? void *load_nodeslist_thread(void *data)
*
* Return 0 on success.
* Return -1 if nodeslist file cannot be opened or created.
* Return -2 if nodeslist file cannot be parsed.
* Return -3 if nodeslist file does not contain any valid node entries.
*/
int load_DHT_nodeslist(void)
{ {
char nodes_path[PATH_MAX]; char nodes_path[PATH_MAX];
get_nodeslist_path(nodes_path, sizeof(nodes_path)); get_nodeslist_path(nodes_path, sizeof(nodes_path));
@ -418,10 +432,12 @@ int load_DHT_nodeslist(void)
if (!file_exists(nodes_path)) { if (!file_exists(nodes_path)) {
if ((fp = fopen(nodes_path, "w+")) == NULL) { if ((fp = fopen(nodes_path, "w+")) == NULL) {
return -1; fprintf(stderr, "nodeslist load error: failed to create file '%s'\n", nodes_path);
goto on_exit;
} }
} else if ((fp = fopen(nodes_path, "r+")) == NULL) { } else if ((fp = fopen(nodes_path, "r+")) == NULL) {
return -1; fprintf(stderr, "nodeslist load error: failed to open file '%s'\n", nodes_path);
goto on_exit;
} }
int update_err = update_DHT_nodeslist(nodes_path); int update_err = update_DHT_nodeslist(nodes_path);
@ -434,45 +450,103 @@ int load_DHT_nodeslist(void)
if (fgets(line, sizeof(line), fp) == NULL) { if (fgets(line, sizeof(line), fp) == NULL) {
fclose(fp); fclose(fp);
return -2; fprintf(stderr, "nodeslist load error: file empty.\n");
goto on_exit;
} }
size_t idx = 0;
const char *line_start = line; const char *line_start = line;
while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY)) && Nodes.count < MAXNODES) { while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY))) {
size_t idx = Nodes.count; pthread_mutex_lock(&thread_data.lock);
idx = Nodes.count;
if (idx >= MAX_NODES) {
pthread_mutex_unlock(&thread_data.lock);
break;
}
if (extract_node(line_start, &Nodes.list[idx]) == 0) { if (extract_node(line_start, &Nodes.list[idx]) == 0) {
++Nodes.count; ++Nodes.count;
} }
pthread_mutex_unlock(&thread_data.lock);
} }
/* If nodeslist does not contain any valid entries we set the last_scan value /* If nodeslist does not contain any valid entries we set the last_scan value
* to 0 so that it will fetch a new list the next time this function is called. * to 0 so that it will fetch a new list the next time this function is called.
*/ */
if (Nodes.count == 0) { if (idx == 0) {
const char *s = "{\"last_scan\":0}"; const char *s = "{\"last_scan\":0}";
rewind(fp); rewind(fp);
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails
fclose(fp); fclose(fp);
return -3; fprintf(stderr, "nodeslist load error: List did not contain any valid entries.\n");
goto on_exit;
} }
fclose(fp); fclose(fp);
on_exit:
thread_data.active = false;
pthread_attr_destroy(&thread_data.attr);
pthread_exit(0);
}
/* Creates a new thread that will load the DHT nodeslist to memory
* from json encoded nodes file obtained at NODES_LIST_URL. Only one
* thread may run at a time.
*
* Return 0 on success.
* Return -1 if a thread is already active.
* Return -2 if mutex fails to init.
* Return -3 if pthread attribute fails to init.
* Return -4 if pthread fails to set detached state.
* Return -5 if thread creation fails.
*/
int load_DHT_nodeslist(void)
{
if (thread_data.active) {
return -1;
}
if (pthread_mutex_init(&thread_data.lock, NULL) != 0) {
return -2;
}
if (pthread_attr_init(&thread_data.attr) != 0) {
return -3;
}
if (pthread_attr_setdetachstate(&thread_data.attr, PTHREAD_CREATE_DETACHED) != 0) {
return -4;
}
thread_data.active = true;
if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) {
thread_data.active = false;
return -5;
}
return 0; return 0;
} }
/* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */ /* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */
static void DHT_bootstrap(Tox *m) static void DHT_bootstrap(Tox *m)
{ {
if (Nodes.count == 0) { pthread_mutex_lock(&thread_data.lock);
size_t num_nodes = Nodes.count;
pthread_mutex_unlock(&thread_data.lock);
if (num_nodes == 0) {
return; return;
} }
size_t i; size_t i;
pthread_mutex_lock(&thread_data.lock);
for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) { for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) {
TOX_ERR_BOOTSTRAP err;
struct Node *node = &Nodes.list[rand() % Nodes.count]; struct Node *node = &Nodes.list[rand() % Nodes.count];
const char *addr = node->have_ip4 ? node->ip4 : node->ip6; const char *addr = node->have_ip4 ? node->ip4 : node->ip6;
@ -480,6 +554,7 @@ static void DHT_bootstrap(Tox *m)
continue; continue;
} }
TOX_ERR_BOOTSTRAP err;
tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err); tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err);
if (err != TOX_ERR_BOOTSTRAP_OK) { if (err != TOX_ERR_BOOTSTRAP_OK) {
@ -492,6 +567,8 @@ static void DHT_bootstrap(Tox *m)
fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port); fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port);
} }
} }
pthread_mutex_unlock(&thread_data.lock);
} }
/* Manages connection to the Tox DHT network. */ /* Manages connection to the Tox DHT network. */

View File

@ -26,13 +26,16 @@
/* Manages connection to the Tox DHT network. */ /* Manages connection to the Tox DHT network. */
void do_tox_connection(Tox *m); void do_tox_connection(Tox *m);
/* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL. /* Creates a new thread that will load the DHT nodeslist to memory
* TODO: Parse json using a proper library? * from json encoded nodes file obtained at NODES_LIST_URL. Only one
* thread may run at a time.
* *
* Return 0 on success. * Return 0 on success.
* Return -1 if nodeslist file cannot be opened or created. * Return -1 if a thread is already active.
* Return -2 if nodeslist file cannot be parsed. * Return -2 if mutex fails to init.
* Return -3 if nodeslist file does not contain any valid node entries. * Return -3 if pthread attribute fails to init.
* Return -4 if pthread fails to set detached state.
* Return -5 if thread creation fails.
*/ */
int load_DHT_nodeslist(void); int load_DHT_nodeslist(void);

View File

@ -33,7 +33,7 @@
#include "curl_util.h" #include "curl_util.h"
extern struct arg_opts arg_opts; extern struct arg_opts arg_opts;
extern struct Winthread Winthread;; extern struct Winthread Winthread;
#define NAMESERVER_API_PATH "api" #define NAMESERVER_API_PATH "api"
#define SERVER_KEY_SIZE 32 #define SERVER_KEY_SIZE 32
@ -53,8 +53,8 @@ static struct thread_data {
char id_bin[TOX_ADDRESS_SIZE]; char id_bin[TOX_ADDRESS_SIZE];
char addr[MAX_STR_SIZE]; char addr[MAX_STR_SIZE];
char msg[MAX_STR_SIZE]; char msg[MAX_STR_SIZE];
bool busy;
bool disabled; bool disabled;
volatile bool busy;
} t_data; } t_data;
static struct lookup_thread { static struct lookup_thread {
@ -234,7 +234,7 @@ void *lookup_thread_func(void *data)
if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) { if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) {
if (!strcasecmp(input_domain, "utox.org")) if (!strcasecmp(input_domain, "utox.org"))
lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic"); lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic.");
else else
lookup_error(self, "Name server domain not found."); lookup_error(self, "Name server domain not found.");

View File

@ -1140,12 +1140,6 @@ int main(int argc, char **argv)
queue_init_message("Name lookup server list does not contain any valid entries."); queue_init_message("Name lookup server list does not contain any valid entries.");
} }
int nodeslist_ret = load_DHT_nodeslist();
if (nodeslist_ret != 0) {
queue_init_message("DHT nodeslist failed to load (error %d)", nodeslist_ret);
}
#ifdef X11 #ifdef X11
if (init_xtra(DnD_callback) == -1) if (init_xtra(DnD_callback) == -1)
queue_init_message("X failed to initialize"); queue_init_message("X failed to initialize");
@ -1173,7 +1167,6 @@ int main(int argc, char **argv)
if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0)
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
#ifdef AUDIO #ifdef AUDIO
av = init_audio(prompt, m); av = init_audio(prompt, m);
@ -1202,6 +1195,12 @@ int main(int argc, char **argv)
if (init_mplex_away_timer(m) == -1) if (init_mplex_away_timer(m) == -1)
queue_init_message("Failed to init mplex auto-away."); queue_init_message("Failed to init mplex auto-away.");
int nodeslist_ret = load_DHT_nodeslist();
if (nodeslist_ret != 0) {
queue_init_message("DHT nodeslist failed to load (error %d)", nodeslist_ret);
}
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
print_init_messages(prompt); print_init_messages(prompt);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);