/* DHT boostrap * * A simple DHT boostrap server for tox - daemon edition. * * Copyright (C) 2013 Tox project All Rights Reserved. * * This file is part of Tox. * * Tox 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. * * Tox 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 Tox. If not, see . * */ #include /* pid_t */ #include /* umask */ #include /* POSIX things */ #include #include #include #include #include /* htons() */ #include /* strcpy() */ #include "../../toxcore/DHT.h" #include "../../toxcore/friend_requests.h" #define DEFAULT_PORT 33445 #define DEFAULT_PID_FILE "bootstrap_server.pid" #define DEFAULT_KEYS_FILE "bootstrap_server.keys" /* Server info struct */ struct server_info_s { int valid; IP_Port conn; uint8_t bs_pk[32]; }; /* This is the struct configure_server() uses to return its data to */ struct server_conf_s { int err; int port; char pid_file[512]; char keys_file[512]; struct server_info_s info[32]; }; int b16_to_key(char b16_string[], uint8_t *bs_pubkey) { int i; unsigned int num1 = 0, num2 = 0; for (i = 0; i < 32; ++i) { sscanf(&b16_string[i * 2], "%1X", &num1); sscanf(&b16_string[i * 2 + 1], "%1X", &num2); num1 = num1 << 4; bs_pubkey[i] = bs_pubkey[i] | num1; bs_pubkey[i] = bs_pubkey[i] | num2; } return 0; } /* resolve_addr(): address should represent IPv4 or a hostname with A record returns a data in network byte order that can be used to set IP.i or IP_Port.ip.i returns 0 on failure TODO: Fix ipv6 support */ uint32_t resolve_addr(const char *address) { struct addrinfo *server = NULL; struct addrinfo hints; int rc; uint32_t addr; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; // IPv4 only right now. hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses. rc = getaddrinfo(address, "echo", &hints, &server); // Lookup failed. if (rc != 0) { return 0; } // IPv4 records only.. if (server->ai_family != AF_INET) { freeaddrinfo(server); return 0; } addr = ((struct sockaddr_in *)server->ai_addr)->sin_addr.s_addr; freeaddrinfo(server); return addr; } /* This unction connects to all specified servers and connect to them. returns 1 if the connection to the DHT is up returns -1 if all attempts failed */ int connect_to_servers(DHT *dht, struct server_info_s *info) { int i; int c; for (i = 0; i < 32; ++i) { if (info[i].valid) { /* Actual bootstrapping code goes here */ //puts("Calling DHT_bootstrap"); DHT_bootstrap(dht, info[i].conn, info[i].bs_pk); } } /* Check if we're connected to the DHT */ for (c = 0; c != 100; ++c) { usleep(10000); if (DHT_isconnected(dht)) { //puts("Connected"); return 1; break; } if (DHT_isconnected(dht) == 0 && c == 99) { //puts("Not connected"); return -1; break; } do_DHT(dht); networking_poll(dht->c->lossless_udp->net); } /* This probably never happens */ return 0; } void manage_keys(DHT *dht, char *keys_file) { const uint32_t KEYS_SIZE = crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; uint8_t keys[KEYS_SIZE]; struct stat existence; FILE *keysf; /* Check if file exits, proceed to open and load keys */ if (stat(keys_file, &existence) >= 0) { keysf = fopen(keys_file, "r"); size_t read_size = fread(keys, sizeof(uint8_t), KEYS_SIZE, keysf); if (read_size != KEYS_SIZE) { printf("Error while reading the key file\nExiting.\n"); exit(1); } else { printf("Keys loaded successfully\n"); } load_keys(dht->c, keys); } else { /* otherwise save new keys */ /* Silly work-around to ignore any errors coming from new_keys() */ new_keys(dht->c); save_keys(dht->c, keys); keysf = fopen(keys_file, "w"); if (fwrite(keys, sizeof(uint8_t), KEYS_SIZE, keysf) != KEYS_SIZE) { printf("Error while writing the key file.\nExiting.\n"); exit(1); } else { printf("Keys saved successfully\n"); } } fclose(keysf); } /* This reads the configuration file, and returns a struct server_conf_s with: *an error number: *-1 = file wasn't read, for whatever reason *-2 = no bootstrap servers found *the port *the location of the keys file *the location of the PID file *the list of bootstrap servers */ struct server_conf_s configure_server(char *cfg_file) { config_t cfg; config_setting_t *server_list; /* This one will be strcpy'd into the pid_file array in server_conf */ const char *pid_file_tmp; const char *keys_file_tmp; /* Remote bootstrap server variables */ int bs_port; const char *bs_ip; const char *bs_pk; /* The big struct */ static struct server_conf_s server_conf; /* Set both to their default values. If there's an error with opening/reading the config file, we return right away */ server_conf.port = DEFAULT_PORT; strcpy(server_conf.pid_file, DEFAULT_PID_FILE); strcpy(server_conf.keys_file, DEFAULT_KEYS_FILE); config_init(&cfg); /* Read the file. If there is an error, report it and exit. */ if (! config_read_file(&cfg, cfg_file)) { fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); config_destroy(&cfg); server_conf.err = -1; return server_conf; } /* Get the port to listen on */ if (config_lookup_int(&cfg, "port", &server_conf.port)) { //printf("Port: %d\n", port); } else { fprintf(stderr, "No 'port' setting in configuration file.\n"); } /* Get PID file location */ if (config_lookup_string(&cfg, "pid_file", &pid_file_tmp)) { //printf("PID file: %s\n", pid_file_tmp); strcpy(server_conf.pid_file, pid_file_tmp); } else { fprintf(stderr, "No 'pid_file' setting in configuration file.\n"); } /* Get keys file location */ if (config_lookup_string(&cfg, "keys_file", &keys_file_tmp)) { //printf("Keys file: %s\n", keys_file_tmp); strcpy(server_conf.keys_file, keys_file_tmp); } else { fprintf(stderr, "No 'keys_file' setting in configuration file.\n"); } /* Get all the servers in the list */ server_list = config_lookup(&cfg, "bootstrap_servers"); if (server_list != NULL) { int count = config_setting_length(server_list); int i; char tmp_ip[30]; /* IP */ char tmp_pk[64]; /* bs_pk */ for (i = 0; i < count; ++i) { config_setting_t *server = config_setting_get_elem(server_list, i); /* Get a pointer on the key aray */ uint8_t *bs_pk_p = server_conf.info[i].bs_pk; /* Only output the record if all of the expected fields are present. */ if (!(config_setting_lookup_string(server, "ip", &bs_ip) && config_setting_lookup_int(server, "port", &bs_port) && config_setting_lookup_string(server, "bs_pk", &bs_pk))) continue; /* Converting all that stuff into usable formats and storing it away in the server_info struct */ server_conf.info[i].valid = 1; if (resolve_addr(strcpy(tmp_ip, bs_ip)) == 0) { server_conf.info[i].valid = 0; printf("bootstrap_server %d: Invalid IP\n", i); } if (strlen(bs_pk) != 64) { server_conf.info[i].valid = 0; printf("bootstrap_server %d: Invalid public key\n", i); } if (!bs_port) { server_conf.info[i].valid = 0; printf("bootstrap_server %d: Invalid port\n", i); } server_conf.info[i].conn.ip.i = resolve_addr(strcpy(tmp_ip, bs_ip)); server_conf.info[i].conn.port = htons(bs_port); b16_to_key(strcpy(tmp_pk, bs_pk), bs_pk_p); } /* Check if at least one server entry is valid */ for (i = 0; i < 32; ++i) { if (server_conf.info[i].valid) break; else server_conf.err = -2; } } else { server_conf.err = -2; } config_destroy(&cfg); return server_conf; } int main(int argc, char *argv[]) { pid_t pid, sid; /* Process- and Session-ID */ struct server_conf_s server_conf; FILE *pidf; /* The PID file */ if (argc < 2) { printf("Please specify a configuration file.\n"); exit(EXIT_FAILURE); } server_conf = configure_server(argv[1]); /* initialize networking bind to ip 0.0.0.0:PORT */ IP ip; ip.i = 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); printf("Key file: %s\n", server_conf.keys_file); if (server_conf.err == -1) printf("Config file not read.\n"); if (server_conf.err == -2) printf("No valid servers in list.\n"); /* Open PID file for writing - if an error happens, it will be caught down the line */ pidf = fopen(server_conf.pid_file, "w"); /* Manage the keys */ /* for now, just ignore any errors after this call. */ int tmperr = errno; manage_keys(dht, server_conf.keys_file); errno = tmperr; /* Public key */ int i; printf("\nPublic Key: "); for (i = 0; i < 32; ++i) { uint8_t ln, hn; ln = 0x0F & dht->c->self_public_key[i]; hn = 0xF0 & dht->c->self_public_key[i]; hn = hn >> 4; printf("%X%X", hn, ln); } printf("\n"); /* Bootstrap the DHT This one throws odd errors, too. Ignore. I assume they come from somewhere in the core. */ tmperr = errno; connect_to_servers(dht, server_conf.info); errno = tmperr; if (!DHT_isconnected(dht)) { puts("Could not establish DHT connection. Check server settings.\n"); exit(EXIT_FAILURE); } else { printf("Connected to DHT successfully.\n"); } /* If there's been an error, exit before forking off */ if (errno != 0) { perror("Error"); printf("Error(s) occured during start-up. Exiting.\n"); exit(EXIT_FAILURE); } /* Things that make the daemon work come past here. There should be nothing here but the daemon code and the main loop. */ /* Fork off from the parent process */ pid = fork(); if (pid < 0) { printf("Forking failed.\n"); exit(EXIT_FAILURE); } /* If we got a good PID, then we can exit the parent process. */ if (pid > 0) { printf("Forked successfully: %d\n", pid); /* Write the PID file */ fprintf(pidf, "%d\n", pid); fclose(pidf); /* Exit parent */ exit(EXIT_SUCCESS); } /* Change the file mode mask */ umask(0); /* Create a new SID for the child process */ sid = setsid(); if (sid < 0) { printf("SID creation failure.\n"); exit(EXIT_FAILURE); } /* Change the current working directory */ if ((chdir("/")) < 0) { exit(EXIT_FAILURE); } /* Go quiet */ close(STDOUT_FILENO); close(STDIN_FILENO); close(STDERR_FILENO); while (1) { do_DHT(dht); networking_poll(dht->c->lossless_udp->net); usleep(10000); } shutdown_networking(); exit(EXIT_SUCCESS); }