1
0
mirror of https://github.com/Tha14/toxic.git synced 2024-11-22 20:53:01 +01:00

Automatically update DHT nodeslist

List is now automatically fetched from nodes.tox.chat and placed in the
user config directory. The list is updated once every 30 days, and
will attempt to detect problems and re-fetch the list if necessary.

Also fixed a couple file descriptor leaks and cleaned some things up.
This commit is contained in:
Jfreegman 2016-09-20 00:00:09 -04:00
parent 1f8c11a33a
commit a009f11c0c
No known key found for this signature in database
GPG Key ID: 3627F3144076AE63
15 changed files with 440 additions and 144 deletions

View File

@ -11,10 +11,10 @@ CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
CFLAGS += $(USER_CFLAGS) CFLAGS += $(USER_CFLAGS)
LDFLAGS = $(USER_LDFLAGS) LDFLAGS = $(USER_LDFLAGS)
OBJ = chat.o chat_commands.o configdir.o execute.o file_transfers.o notify.o OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
OBJ += group_commands.o term_mplex.o avatars.o name_lookup.o qr_code.o bootstrap.o OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
# Check on wich system we are running # Check on wich system we are running
UNAME_S = $(shell uname -s) UNAME_S = $(shell uname -s)

View File

@ -16,7 +16,7 @@ MISC_DIR = $(BASE_DIR)/misc
# Project files # Project files
MANFILES = toxic.1 toxic.conf.5 MANFILES = toxic.1 toxic.conf.5
DATAFILES = DHTnodes nameservers toxic.conf.example DATAFILES = nameservers toxic.conf.example
DESKFILE = toxic.desktop DESKFILE = toxic.desktop
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav

View File

@ -2,12 +2,12 @@
.\" Title: toxic .\" Title: toxic
.\" Author: [see the "AUTHORS" section] .\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2015-12-07 .\" Date: 2016-04-25
.\" Manual: Toxic Manual .\" Manual: Toxic Manual
.\" Source: toxic __VERSION__ .\" Source: toxic __VERSION__
.\" Language: English .\" Language: English
.\" .\"
.TH "TOXIC" "1" "2015\-12\-07" "toxic __VERSION__" "Toxic Manual" .TH "TOXIC" "1" "2016\-04\-25" "toxic __VERSION__" "Toxic Manual"
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
.\" * Define some portability stuff .\" * Define some portability stuff
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
@ -82,8 +82,7 @@ Show help message
.RS 4 .RS 4
Use specified Use specified
\fInodes\-file\fR \fInodes\-file\fR
for DHT bootstrap nodes, instead of for DHT bootstrap nodes, instead of the default
\fI__DATADIR__/DHTnodes\fR
.RE .RE
.PP .PP
\-o, \-\-noconnect \-o, \-\-noconnect
@ -122,9 +121,10 @@ Unencrypt a data file\&. A warning will appear if this option is used with a dat
.RE .RE
.SH "FILES" .SH "FILES"
.PP .PP
__DATADIR__/DHTnodes ~/\&.config/tox/DHTnodes
.RS 4 .RS 4
Default list of DHT bootstrap nodes\&. Default location for list of DHT bootstrap nodes (list obtained at
https://nodes\&.tox\&.chat)\&. This list is automatically updated every 30 days\&.
.RE .RE
.PP .PP
~/\&.config/tox/toxic_profile\&.tox ~/\&.config/tox/toxic_profile\&.tox

View File

@ -41,8 +41,7 @@ OPTIONS
Show help message Show help message
-n, --nodes nodes-file:: -n, --nodes nodes-file::
Use specified 'nodes-file' for DHT bootstrap nodes, instead of Use specified 'nodes-file' for DHT bootstrap nodes, instead of the default
'{datadir}/DHTnodes'
-o, --noconnect:: -o, --noconnect::
Do not connect to the DHT network Do not connect to the DHT network
@ -68,8 +67,9 @@ OPTIONS
FILES FILES
----- -----
{datadir}/DHTnodes:: ~/.config/tox/DHTnodes::
Default list of DHT bootstrap nodes. Default location for list of DHT bootstrap nodes (list obtained at https://nodes.tox.chat).
This list is automatically updated every 30 days.
~/.config/tox/toxic_profile.tox:: ~/.config/tox/toxic_profile.tox::
Savestate which contains your personal info (nickname, Tox ID, contacts, Savestate which contains your personal info (nickname, Tox ID, contacts,

File diff suppressed because one or more lines are too long

View File

@ -23,23 +23,40 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <limits.h>
#include <curl/curl.h>
#include <tox/tox.h> #include <tox/tox.h>
#include "line_info.h" #include "line_info.h"
#include "windows.h" #include "windows.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "configdir.h"
#include "curl_util.h"
extern struct arg_opts arg_opts; extern struct arg_opts arg_opts;
/* URL that we get the JSON encoded nodes list from. */
#define NODES_LIST_URL "https://nodes.tox.chat/json"
#define DEFAULT_NODES_FILENAME "DHTnodes"
/* Time to wait between bootstrap attempts */ /* Time to wait between bootstrap attempts */
#define TRY_BOOTSTRAP_INTERVAL 5 #define TRY_BOOTSTRAP_INTERVAL 5
/* Number of nodes to bootstrap to per try */
#define NUM_BOOTSTRAP_NODES 5
/* How often we should update the nodeslist file. */
#define NODELIST_UPDATE_TIMEOUT (60*24*30)
#define IPv4_MAX_SIZE 64 #define IPv4_MAX_SIZE 64
#define PORT_MAX_SIZE 5 #define PORT_MAX_SIZE 5
#define LAST_SCAN_JSON_VALUE "\"last_scan\":"
#define LAST_SCAN_JSON_VALUE_LEN (sizeof(LAST_SCAN_JSON_VALUE) - 1)
#define IPV4_JSON_VALUE "\"ipv4\":\"" #define IPV4_JSON_VALUE "\"ipv4\":\""
#define IPV4_JSON_VALUE_LEN (sizeof(IPV4_JSON_VALUE) - 1) #define IPV4_JSON_VALUE_LEN (sizeof(IPV4_JSON_VALUE) - 1)
@ -62,24 +79,198 @@ static struct toxNodes {
char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE]; char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE];
} toxNodes; } toxNodes;
/* Load the DHT nodelist to memory from json formatted nodes file obtained at https://nodes.tox.chat/json.
/* 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,
* or if the file is older than the given timeout.
*/
static bool nodeslist_needs_update(const char *nodes_path)
{
FILE *fp = fopen(nodes_path, "r+");
if (fp == NULL) {
return false;
}
/* last_scan value should be at beginning of file */
char line[LAST_SCAN_JSON_VALUE_LEN + 32];
if (fgets(line, sizeof(line), fp) == NULL) {
fclose(fp);
return true;
}
fclose(fp);
const char *last_scan_val = strstr(line, LAST_SCAN_JSON_VALUE);
if (last_scan_val == NULL) {
return true;
}
last_scan_val += LAST_SCAN_JSON_VALUE_LEN;
long long int last_scan = strtoll(last_scan_val, NULL, 10);
if (timed_out(last_scan, NODELIST_UPDATE_TIMEOUT)) {
return true;
}
return false;
}
/* Fetches the JSON encoded DHT nodeslist from NODES_LIST_URL.
* *
* Return 0 on success. * Return 0 on success.
* Return -1 if nodelist file cannot be opened. * Return -1 on failure.
* Return -2 if nodelist file cannot be parsed.
* Return -3 if nodelist file does not contain any valid node entries.
*/ */
int load_DHT_nodelist(void) static int curl_fetch_nodes_JSON(struct Recv_Data *recv_data)
{ {
const char *filename = !arg_opts.nodes_path[0] ? PACKAGE_DATADIR "/DHTnodes" : arg_opts.nodes_path; CURL *c_handle = curl_easy_init();
FILE *fp = fopen(filename, "r");
if (fp == NULL) if (c_handle == NULL) {
return -1; return -1;
}
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "charsets: utf-8");
curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(c_handle, CURLOPT_URL, NODES_LIST_URL);
curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, write_lookup_data);
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data);
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
curl_easy_setopt(c_handle, CURLOPT_HTTPGET, 1L);
int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
if (proxy_ret != 0) {
fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret);
return -1;
}
int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
if (ret != CURLE_OK) {
fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret);
return -1;
}
ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
if (ret != CURLE_OK) {
fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret);
return -1;
}
ret = curl_easy_perform(c_handle);
if (ret != CURLE_OK) {
/* If system doesn't support any of the specified ciphers suites, fall back to default */
if (ret == CURLE_SSL_CIPHER) {
curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL);
ret = curl_easy_perform(c_handle);
}
if (ret != CURLE_OK) {
fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret);
return -1;
}
}
return 0;
}
/* Attempts to update the DHT nodeslist.
*
* Return 1 if list was updated successfully.
* Return 0 if list does not need to be updated.
* Return -1 if file cannot be opened.
* Return -2 if http lookup failed.
* Return -3 if http reponse was empty.
* Return -4 if data could not be written to disk.
*/
static int update_DHT_nodeslist(const char *nodes_path)
{
if (!nodeslist_needs_update(nodes_path)) {
return 0;
}
FILE *fp = fopen(nodes_path, "r+");
if (fp == NULL) {
return -1;
}
struct Recv_Data recv_data;
memset(&recv_data, 0, sizeof(struct Recv_Data));
if (curl_fetch_nodes_JSON(&recv_data) == -1) {
fclose(fp);
return -2;
}
if (recv_data.length == 0) {
fclose(fp);
return -3;
}
if (fwrite(recv_data.data, recv_data.length, 1, fp) != 1) {
fclose(fp);
return -4;
}
fclose(fp);
return 1;
}
static void get_nodeslist_path(char *buf, size_t buf_size)
{
char *config_dir = NULL;
if (arg_opts.nodes_path[0]) {
snprintf(buf, buf_size, "%s", arg_opts.nodes_path);
} else if ((config_dir = get_user_config_dir()) != NULL) {
snprintf(buf, buf_size, "%s%s%s", config_dir, CONFIGDIR, DEFAULT_NODES_FILENAME);
free(config_dir);
} else {
snprintf(buf, buf_size, "%s", DEFAULT_NODES_FILENAME);
}
}
/* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL.
* TODO: Parse json using a proper library?
*
* 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];
get_nodeslist_path(nodes_path, sizeof(nodes_path));
FILE *fp = NULL;
if (!file_exists(nodes_path)) {
if ((fp = fopen(nodes_path, "w+")) == NULL) {
return -1;
}
} else if ((fp = fopen(nodes_path, "r+")) == NULL) {
return -1;
}
int update_err = update_DHT_nodeslist(nodes_path);
if (update_err < 0) {
fprintf(stderr, "update_DHT_nodeslist() failed with error %d\n", update_err);
}
char line[MAX_NODELIST_SIZE]; char line[MAX_NODELIST_SIZE];
if (fgets(line, sizeof(line), fp) == NULL) { if (fgets(line, sizeof(line), fp) == NULL) {
fclose(fp);
return -2; return -2;
} }
@ -124,8 +315,9 @@ int load_DHT_nodelist(void)
long int port = strtol(port_string, NULL, 10); long int port = strtol(port_string, NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE) if (port <= 0 || port > MAX_PORT_RANGE) {
continue; continue;
}
/* Extract key */ /* Extract key */
const char *key_start = strstr(port_start, KEY_JSON_VALUE); const char *key_start = strstr(port_start, KEY_JSON_VALUE);
@ -145,7 +337,7 @@ int load_DHT_nodelist(void)
memcpy(key_string, key_start, TOX_PUBLIC_KEY_SIZE * 2); memcpy(key_string, key_start, TOX_PUBLIC_KEY_SIZE * 2);
key_string[TOX_PUBLIC_KEY_SIZE * 2] = 0; key_string[TOX_PUBLIC_KEY_SIZE * 2] = 0;
/* Add IP-Port-Key to nodes list */ /* Add entry to nodes list */
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", ipv4_string); snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", ipv4_string);
toxNodes.ports[toxNodes.lines] = port; toxNodes.ports[toxNodes.lines] = port;
@ -155,21 +347,31 @@ int load_DHT_nodelist(void)
toxNodes.lines++; toxNodes.lines++;
} }
/* 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.
*/
if (toxNodes.lines == 0) {
const char *s = "{\"last_scan\":0}";
rewind(fp);
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails
fclose(fp); fclose(fp);
if (toxNodes.lines == 0)
return -3; return -3;
}
fclose(fp);
return 0; return 0;
} }
/* Connects to a random DHT node 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 (toxNodes.lines == 0) { if (toxNodes.lines == 0) {
return; return;
} }
size_t i;
for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) {
size_t node = rand() % toxNodes.lines; size_t node = rand() % toxNodes.lines;
TOX_ERR_BOOTSTRAP err; TOX_ERR_BOOTSTRAP err;
@ -185,6 +387,7 @@ static void DHT_bootstrap(Tox *m)
fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]); fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]);
} }
} }
}
/* 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)

View File

@ -23,11 +23,12 @@
/* 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 nodelist to memory from json formatted nodes file obtained attempts https://nodes.tox.chat/json. /* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL.
* TODO: Parse json using a proper library?
* *
* Return 0 on success. * Return 0 on success.
* Return -1 if nodelist file cannot be opened. * Return -1 if nodeslist file cannot be opened or created.
* Return -2 if nodelist file cannot be parsed. * Return -2 if nodeslist file cannot be parsed.
* Return -3 if nodelist file does not contain any valid node entries. * Return -3 if nodeslist file does not contain any valid node entries.
*/ */
int load_DHT_nodelist(void); int load_DHT_nodeslist(void);

View File

@ -33,7 +33,7 @@
#include "configdir.h" #include "configdir.h"
#include "misc_tools.h" #include "misc_tools.h"
/* get the user's home directory */ /* get the user's home directory. */
void get_home_dir(char *home, int size) void get_home_dir(char *home, int size)
{ {
struct passwd pwd; struct passwd pwd;
@ -102,8 +102,10 @@ char *get_user_config_dir(void)
return user_config_dir; return user_config_dir;
} }
/* /* Creates the config and chatlog directories.
* Creates the config and chatlog directories. *
* Returns 0 on success.
* Returns -1 on failure.
*/ */
int create_user_config_dirs(char *path) int create_user_config_dirs(char *path)
{ {

View File

@ -34,8 +34,23 @@
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif #endif
/**
* @brief Get the user's config directory.
*
* This is without a trailing slash. Resulting string must be freed.
*
* @return The users config dir or NULL on error.
*/
char *get_user_config_dir(void); char *get_user_config_dir(void);
/* get the user's home directory. */
void get_home_dir(char *home, int size); void get_home_dir(char *home, int size);
/* Creates the config and chatlog directories.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int create_user_config_dirs(char *path); int create_user_config_dirs(char *path);
#endif /* #define CONFIGDIR_H */ #endif /* #define CONFIGDIR_H */

93
src/curl_util.c Normal file
View File

@ -0,0 +1,93 @@
/* curl_util.c
*
*
* Copyright (C) 2016 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <string.h>
#include <curl/curl.h>
#include <tox/tox.h>
#include "curl_util.h"
/* Sets proxy info for given CURL handler.
*
* Returns 0 on success or if no proxy is set by the client.
* Returns -1 if proxy info is invalid.
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
*/
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
{
if (proxy_type == TOX_PROXY_TYPE_NONE)
return 0;
if (proxy_address == NULL || port == 0) {
return -1;
}
int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
if (ret != CURLE_OK) {
return ret;
}
long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP;
ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type);
if (ret != CURLE_OK) {
return ret;
}
ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
if (ret != CURLE_OK) {
return ret;
}
return 0;
}
/* Callback function for CURL to write received data.
*
* This function will append data from an http request to the data buffer
* until the request is complete or the buffer is full. Buffer will be null terminated.
*
* Returns number of bytes received from http request on success (don't change this).
* Returns 0 if data exceeds buffer size.
*/
size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer)
{
struct Recv_Data *recv_data = (struct Recv_Data *) user_pointer;
size_t length = size * nmemb;
size_t total_size = length + recv_data->length;
if (total_size > MAX_RECV_CURL_DATA_SIZE) {
return 0;
}
memcpy(recv_data->data + recv_data->length, data, length);
recv_data->data[total_size] = '\0';
recv_data->length += length;
return length;
}

50
src/curl_util.h Normal file
View File

@ -0,0 +1,50 @@
/* curl_util.h
*
*
* Copyright (C) 2016 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* 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"
/* Max size of an http response that we can store in Recv_Data */
#define MAX_RECV_CURL_DATA_SIZE 32767
/* Holds data received from curl lookup */
struct Recv_Data {
char data[MAX_RECV_CURL_DATA_SIZE + 1]; /* Data received from curl write data callback */
size_t length; /* Total number of bytes written to data buffer (doesn't include null) */
};
/* Sets proxy info for given CURL handler.
*
* Returns 0 on success or if no proxy is set by the client.
* Returns -1 if proxy info is invalid.
* Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html)
*/
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type);
/* Callback function for CURL to write received data.
*
* This function will append data from an http request to the data buffer
* until the request is complete or the buffer is full. Buffer will be null terminated.
*
* Returns size of bytes written to the data buffer.
*/
size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer);

View File

@ -22,7 +22,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h> /* for u_char */
#include <curl/curl.h> #include <curl/curl.h>
#include "toxic.h" #include "toxic.h"
@ -31,6 +30,7 @@
#include "global_commands.h" #include "global_commands.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "configdir.h" #include "configdir.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;;
@ -41,9 +41,6 @@ extern struct Winthread Winthread;;
#define MAX_DOMAIN_SIZE 32 #define MAX_DOMAIN_SIZE 32
#define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3 #define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3
/* 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"
struct Nameservers { struct Nameservers {
int lines; int lines;
char names[MAX_SERVERS][MAX_DOMAIN_SIZE]; char names[MAX_SERVERS][MAX_DOMAIN_SIZE];
@ -189,29 +186,6 @@ static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_s
return false; return false;
} }
#define MAX_RECV_LOOKUP_DATA_SIZE 1024
/* Holds raw data received from name server */
struct Recv_Data {
char data[MAX_RECV_LOOKUP_DATA_SIZE];
size_t size;
};
size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer)
{
struct Recv_Data *recv_data = (struct Recv_Data *) user_pointer;
size_t real_size = size * nmemb;
if (real_size >= MAX_RECV_LOOKUP_DATA_SIZE)
return 0;
memcpy(&recv_data->data, data, real_size);
recv_data->size = real_size;
recv_data->data[real_size] = '\0';
return real_size;
}
/* Converts Tox ID string contained in recv_data to binary format and puts it in thread's ID buffer. /* Converts Tox ID string contained in recv_data to binary format and puts it in thread's ID buffer.
* *
* Returns 0 on success. * Returns 0 on success.
@ -222,7 +196,7 @@ static int process_response(struct Recv_Data *recv_data)
{ {
size_t prefix_size = strlen(ID_PREFIX); size_t prefix_size = strlen(ID_PREFIX);
if (recv_data->size < TOX_ADDRESS_SIZE * 2 + prefix_size) if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size)
return -1; return -1;
const char *IDstart = strstr(recv_data->data, ID_PREFIX); const char *IDstart = strstr(recv_data->data, ID_PREFIX);
@ -243,47 +217,6 @@ static int process_response(struct Recv_Data *recv_data)
return 0; return 0;
} }
/* Sets proxy info for given CURL handler.
*
* Returns 0 on success or if no proxy is set by the client.
* Returns -1 on failure.
*/
static int set_lookup_proxy(ToxWindow *self, CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
{
if (proxy_type == TOX_PROXY_TYPE_NONE)
return 0;
if (proxy_address == NULL || port == 0) {
lookup_error(self, "Unknown proxy error");
return -1;
}
int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
if (ret != CURLE_OK) {
lookup_error(self, "Failed to set proxy port (libcurl error %d)", ret);
return -1;
}
long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP;
ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type);
if (ret != CURLE_OK) {
lookup_error(self, "Failed to set proxy type (libcurl error %d)", ret);
return -1;
}
ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
if (ret != CURLE_OK) {
lookup_error(self, "Failed to set proxy (libcurl error %d)", ret);
return -1;
}
return 0;
}
void *lookup_thread_func(void *data) void *lookup_thread_func(void *data)
{ {
ToxWindow *self = t_data.self; ToxWindow *self = t_data.self;
@ -333,8 +266,12 @@ void *lookup_thread_func(void *data)
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data); curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data);
if (set_lookup_proxy(self, c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type) == -1) int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
if (proxy_ret != 0) {
lookup_error(self, "Failed to set proxy (error %d)\n");
goto on_exit; goto on_exit;
}
int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL); int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL);
@ -437,9 +374,9 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr,
* Returns -2 if the nameserver list cannot be found. * Returns -2 if the nameserver list cannot be found.
* Returns -3 if the nameserver list does not contain any valid entries. * Returns -3 if the nameserver list does not contain any valid entries.
*/ */
int name_lookup_init(void) int name_lookup_init(int curl_init_status)
{ {
if (curl_global_init(CURL_GLOBAL_ALL) != 0) { if (curl_init_status != 0) {
t_data.disabled = true; t_data.disabled = true;
return -1; return -1;
} }
@ -454,8 +391,3 @@ int name_lookup_init(void)
return 0; return 0;
} }
void name_lookup_cleanup(void)
{
curl_global_cleanup();
}

View File

@ -29,8 +29,7 @@
* Returns 0 on success. * Returns 0 on success.
* Returns -1 on failure. * Returns -1 on failure.
*/ */
int name_lookup_init(void); int name_lookup_init(int curl_init_status);
void name_lookup_cleanup(void);
int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message); int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message);

View File

@ -41,6 +41,7 @@
#include <termios.h> #include <termios.h>
#include <ctype.h> #include <ctype.h>
#include <curl/curl.h>
#include <tox/tox.h> #include <tox/tox.h>
#include <tox/toxencryptsave.h> #include <tox/toxencryptsave.h>
@ -164,7 +165,7 @@ void exit_toxic_success(Tox *m)
free_global_data(); free_global_data();
tox_kill(m); tox_kill(m);
endwin(); endwin();
name_lookup_cleanup(); curl_global_cleanup();
#ifdef X11 #ifdef X11
/* We have to terminate xtra last coz reasons /* We have to terminate xtra last coz reasons
@ -455,6 +456,7 @@ int store_data(Tox *m, const char *path)
char *data = malloc(data_len * sizeof(char)); char *data = malloc(data_len * sizeof(char));
if (data == NULL) { if (data == NULL) {
fclose(fp);
return -1; return -1;
} }
@ -465,6 +467,7 @@ int store_data(Tox *m, const char *path)
char *enc_data = malloc(enc_len * sizeof(char)); char *enc_data = malloc(enc_len * sizeof(char));
if (enc_data == NULL) { if (enc_data == NULL) {
fclose(fp);
free(data); free(data);
return -1; return -1;
} }
@ -580,14 +583,14 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
if (len == 0) { if (len == 0) {
fclose(fp); fclose(fp);
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
} }
char data[len]; char data[len];
if (fread(data, sizeof(data), 1, fp) != 1) { if (fread(data, sizeof(data), 1, fp) != 1) {
fclose(fp); fclose(fp);
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
} }
bool is_encrypted = tox_is_data_encrypted((uint8_t *) data); bool is_encrypted = tox_is_data_encrypted((uint8_t *) data);
@ -595,7 +598,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
/* attempt to encrypt an already encrypted data file */ /* attempt to encrypt an already encrypted data file */
if (arg_opts.encrypt_data && is_encrypted) { if (arg_opts.encrypt_data && is_encrypted) {
fclose(fp); fclose(fp);
exit_toxic_err("failed in load_toxic", FATALERR_ENCRYPT); exit_toxic_err("failed in load_tox", FATALERR_ENCRYPT);
} }
if (arg_opts.unencrypt_data && is_encrypted) if (arg_opts.unencrypt_data && is_encrypted)
@ -681,7 +684,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
fclose(fp); fclose(fp);
} else { /* Data file does not/should not exist */ } else { /* Data file does not/should not exist */
if (file_exists(data_path)) if (file_exists(data_path))
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_NONE; tox_opts->savedata_type = TOX_SAVEDATA_TYPE_NONE;
@ -691,7 +694,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
return NULL; return NULL;
if (store_data(m, data_path) == -1) if (store_data(m, data_path) == -1)
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
} }
return m; return m;
@ -920,10 +923,6 @@ static void parse_args(int argc, char *argv[])
case 'n': case 'n':
snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg); snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg);
if (!file_exists(arg_opts.nodes_path))
queue_init_message("DHTnodes file not found");
break; break;
case 'o': case 'o':
@ -1092,6 +1091,7 @@ void DnD_callback(const char* asdv, DropType dt)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
update_unix_time();
parse_args(argc, argv); parse_args(argc, argv);
/* Use the -b flag to enable stderr */ /* Use the -b flag to enable stderr */
@ -1125,16 +1125,12 @@ int main(int argc, char **argv)
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL; const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
if (settings_load(user_settings, p) == -1) if (settings_load(user_settings, p) == -1) {
queue_init_message("Failed to load user settings"); queue_init_message("Failed to load user settings");
int nodelist_ret = load_DHT_nodelist();
if (nodelist_ret != 0) {
queue_init_message("DHT nodelist failed to load (error %d). You can still connect manually with the /connect command.", nodelist_ret);
} }
int nameserver_ret = name_lookup_init(); int curl_init = curl_global_init(CURL_GLOBAL_ALL);
int nameserver_ret = name_lookup_init(curl_init);
if (nameserver_ret == -1) { if (nameserver_ret == -1) {
queue_init_message("curl failed to initialize; name lookup service is disabled."); queue_init_message("curl failed to initialize; name lookup service is disabled.");
@ -1144,6 +1140,12 @@ 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)\n", 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");