#ifndef __TINC_HAVE_H__
#define __TINC_HAVE_H__
+ #ifdef HAVE_MINGW
+ #ifdef WITH_WINDOWS2000
+ #define WINVER Windows2000
+ #else
+ #define WINVER WindowsXP
+ #endif
++#define WIN32_LEAN_AND_MEAN
+ #endif
+
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "system.h"
-#include "avl_tree.h"
+#include "splay_tree.h"
+ #include "connection.h"
#include "conf.h"
#include "logger.h"
#include "netutl.h" /* for str2address */
if(result)
return result;
else
- return strcmp(a->file, b->file);
+ return a->file ? strcmp(a->file, b->file) : 0;
}
-void init_configuration(avl_tree_t ** config_tree) {
- *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config);
+void init_configuration(splay_tree_t ** config_tree) {
+ *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config);
}
-void exit_configuration(avl_tree_t ** config_tree) {
- avl_delete_tree(*config_tree);
+void exit_configuration(splay_tree_t ** config_tree) {
+ splay_delete_tree(*config_tree);
*config_tree = NULL;
}
config_t cfg, *found;
cfg.variable = variable;
- cfg.file = "";
+ cfg.file = NULL;
cfg.line = 0;
- found = avl_search_closest_greater(config_tree, &cfg);
+ found = splay_search_closest_greater(config_tree, &cfg);
if(!found)
return NULL;
Parse a configuration file and put the results in the configuration tree
starting at *base.
*/
- int read_config_file(splay_tree_t *config_tree, const char *fname) {
-bool read_config_file(avl_tree_t *config_tree, const char *fname) {
++bool read_config_file(splay_tree_t *config_tree, const char *fname) {
FILE *fp;
char buffer[MAX_STRING_SIZE];
char *line;
return result;
}
-void read_config_options(avl_tree_t *config_tree, const char *prefix) {
++void read_config_options(splay_tree_t *config_tree, const char *prefix) {
+ list_node_t *node, *next;
+ size_t prefix_len = prefix ? strlen(prefix) : 0;
+
+ for(node = cmdline_conf->tail; node; node = next) {
+ config_t *cfg = (config_t *)node->data;
+ next = node->prev;
+
+ if(!prefix && strchr(cfg->variable, '.'))
+ continue;
+
+ if(prefix && (strncmp(prefix, cfg->variable, prefix_len) || cfg->variable[prefix_len] != '.'))
+ continue;
+
+ config_add(config_tree, cfg);
+ node->data = NULL;
+ list_unlink_node(cmdline_conf, node);
+ }
+ }
+
bool read_server_config() {
char *fname;
bool x;
return x;
}
-FILE *ask_and_open(const char *filename, const char *what) {
- FILE *r;
- char *directory;
- char line[PATH_MAX];
- const char *fn;
-
- /* Check stdin and stdout */
- if(!isatty(0) || !isatty(1)) {
- /* Argh, they are running us from a script or something. Write
- the files to the current directory and let them burn in hell
- for ever. */
- fn = filename;
- } else {
- /* Ask for a file and/or directory name. */
- fprintf(stdout, "Please enter a file to save %s to [%s]: ",
- what, filename);
- fflush(stdout);
-
- fn = readline(stdin, line, sizeof line);
-
- if(!fn) {
- fprintf(stderr, "Error while reading stdin: %s\n",
- strerror(errno));
- return NULL;
- }
-
- if(!strlen(fn))
- /* User just pressed enter. */
- fn = filename;
- }
-
-#ifdef HAVE_MINGW
- if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) {
-#else
- if(fn[0] != '/') {
-#endif
- /* The directory is a relative path or a filename. */
- char *p;
-
- directory = get_current_dir_name();
- xasprintf(&p, "%s/%s", directory, fn);
- free(directory);
- fn = p;
- }
-
- umask(0077); /* Disallow everything for group and other */
-
- /* Open it first to keep the inode busy */
-
- r = fopen(fn, "r+") ?: fopen(fn, "w+");
-
- if(!r) {
- fprintf(stderr, "Error opening file `%s': %s\n",
- fn, strerror(errno));
- return NULL;
- }
-
- return r;
-}
-
+ bool read_connection_config(connection_t *c) {
+ char *fname;
+ bool x;
+
+ read_config_options(c->config_tree, c->name);
+
+ xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
+ x = read_config_file(c->config_tree, fname);
+ free(fname);
+
+ return x;
+ }
+
bool disable_old_keys(FILE *f) {
char buf[100];
long pos;
#ifndef __TINC_CONF_H__
#define __TINC_CONF_H__
-#include "avl_tree.h"
+#include "splay_tree.h"
+ #include "list.h"
typedef struct config_t {
char *variable;
extern bool bypass_security;
extern char *confbase;
extern char *netname;
+ extern list_t *cmdline_conf;
-extern void init_configuration(avl_tree_t **);
-extern void exit_configuration(avl_tree_t **);
+extern void init_configuration(splay_tree_t **);
+extern void exit_configuration(splay_tree_t **);
extern config_t *new_config(void) __attribute__ ((__malloc__));
extern void free_config(config_t *);
-extern void config_add(avl_tree_t *, config_t *);
-extern config_t *lookup_config(avl_tree_t *, char *);
-extern config_t *lookup_config_next(avl_tree_t *, const config_t *);
+extern void config_add(splay_tree_t *, config_t *);
+extern config_t *lookup_config(splay_tree_t *, char *);
+extern config_t *lookup_config_next(splay_tree_t *, const config_t *);
extern bool get_config_bool(const config_t *, bool *);
extern bool get_config_int(const config_t *, int *);
extern bool get_config_string(const config_t *, char **);
extern bool get_config_address(const config_t *, struct addrinfo **);
extern bool get_config_subnet(const config_t *, struct subnet_t **);
- extern int read_config_file(splay_tree_t *, const char *);
+ extern config_t *parse_config_line(char *, const char *, int);
-extern bool read_config_file(avl_tree_t *, const char *);
-extern void read_config_options(avl_tree_t *, const char *);
++extern bool read_config_file(splay_tree_t *, const char *);
++extern void read_config_options(splay_tree_t *, const char *);
extern bool read_server_config(void);
-extern FILE *ask_and_open(const char *, const char *);
+ extern bool read_connection_config(struct connection_t *);
+extern FILE *ask_and_open(const char *, const char *, const char *);
extern bool is_safe_path(const char *);
extern bool disable_old_keys(FILE *);
}
void connection_del(connection_t *c) {
- avl_delete(connection_tree, c);
+ splay_delete(connection_tree, c);
}
-void dump_connections(void) {
- avl_node_t *node;
+bool dump_connections(connection_t *cdump) {
+ splay_node_t *node;
connection_t *c;
- logger(LOG_DEBUG, "Connections:");
-
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
- logger(LOG_DEBUG, " %s at %s options %x socket %d status %04x outbuf %d/%d/%d",
- c->name, c->hostname, c->options, c->socket, bitfield_to_int(&c->status, sizeof c->status),
- c->outbufsize, c->outbufstart, c->outbuflen);
+ send_request(cdump, "%d %d %s at %s options %x socket %d status %04x",
+ CONTROL, REQ_DUMP_CONNECTIONS,
+ c->name, c->hostname, c->options, c->socket,
+ bitfield_to_int(&c->status, sizeof c->status));
}
- logger(LOG_DEBUG, "End of connections.");
+ return send_request(cdump, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
}
-
- bool read_connection_config(connection_t *c) {
- char *fname;
- bool x;
-
- xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
- x = read_config_file(c->config_tree, fname);
- free(fname);
-
- return x;
- }
extern void free_connection(connection_t *);
extern void connection_add(connection_t *);
extern void connection_del(connection_t *);
-extern void dump_connections(void);
+extern bool dump_connections(struct connection_t *);
- extern bool read_connection_config(connection_t *);
#endif /* __TINC_CONNECTION_H__ */
#ifdef HAVE_MINGW
{
char message[4096];
- char *messages[] = {message};
+ const char *messages[] = {message};
- vsnprintf(message, sizeof(message), format, ap);
+ vsnprintf(message, sizeof message, format, ap);
ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
}
#else
#include "subnet.h"
#include "xalloc.h"
-bool do_purge = false;
-volatile bool running = false;
-
-time_t now = 0;
+ int contradicting_add_edge = 0;
+ int contradicting_del_edge = 0;
+
/* Purge edges and subnets of unreachable nodes. Use carefully. */
-static void purge(void) {
- avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
+void purge(void) {
+ splay_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
node_t *n;
edge_t *e;
subnet_t *s;
}
}
}
-
- if(c->outbuflen > 0 && c->last_flushed_time + pingtimeout < now) {
- if(c->status.active) {
- ifdebug(CONNECTIONS) logger(LOG_INFO,
- "%s (%s) could not flush for %ld seconds (%d bytes remaining)",
- c->name, c->hostname, now - c->last_flushed_time, c->outbuflen);
- c->status.timeout = true;
- terminate_connection(c, true);
- }
- }
- }
-}
-
-/*
- check all connections to see if anything
- happened on their sockets
-*/
-static void check_network_activity(fd_set * readset, fd_set * writeset) {
- connection_t *c;
- avl_node_t *node;
- int result, i;
- socklen_t len = sizeof(result);
- vpn_packet_t packet;
- static int errors = 0;
-
- /* check input from kernel */
- if(device_fd >= 0 && FD_ISSET(device_fd, readset)) {
- if(read_packet(&packet)) {
- errors = 0;
- packet.priority = 0;
- route(myself, &packet);
- } else {
- usleep(errors * 50000);
- errors++;
- if(errors > 10) {
- logger(LOG_ERR, "Too many errors from %s, exiting!", device);
- running = false;
- }
- }
}
- /* check meta connections */
- for(node = connection_tree->head; node; node = node->next) {
- c = node->data;
++ if(contradicting_del_edge && contradicting_add_edge) {
++ logger(LOG_WARNING, "Possible node with same Name as us!");
+
- if(c->status.remove)
- continue;
-
- if(FD_ISSET(c->socket, readset)) {
- if(c->status.connecting) {
- c->status.connecting = false;
- getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&result, &len);
++ if(rand() % 3 == 0) {
++ logger(LOG_ERR, "Shutting down, check configuration of all nodes for duplicate Names!");
++ event_loopexit(NULL);
++ return;
++ }
+
- if(!result)
- finish_connecting(c);
- else {
- ifdebug(CONNECTIONS) logger(LOG_DEBUG,
- "Error while connecting to %s (%s): %s",
- c->name, c->hostname, sockstrerror(result));
- closesocket(c->socket);
- do_outgoing_connection(c);
- continue;
- }
- }
++ contradicting_add_edge = 0;
++ contradicting_del_edge = 0;
++ }
+
- if(!receive_meta(c)) {
- terminate_connection(c, c->status.active);
- continue;
- }
- }
+ event_add(event, &(struct timeval){pingtimeout, 0});
+}
- if(FD_ISSET(c->socket, writeset)) {
- if(!flush_meta(c)) {
- terminate_connection(c, c->status.active);
- continue;
- }
+void handle_meta_connection_data(int fd, short events, void *data) {
+ connection_t *c = data;
+ int result;
+ socklen_t len = sizeof result;
+
+ if(c->status.connecting) {
+ c->status.connecting = false;
+
+ getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
+
+ if(!result)
+ finish_connecting(c);
+ else {
+ ifdebug(CONNECTIONS) logger(LOG_DEBUG,
+ "Error while connecting to %s (%s): %s",
+ c->name, c->hostname, sockstrerror(result));
+ closesocket(c->socket);
+ do_outgoing_connection(c);
+ return;
}
}
}
}
-/*
- this is where it all happens...
-*/
-int main_loop(void) {
- fd_set readset, writeset;
- struct timeval tv;
- int r, maxfd;
- time_t last_ping_check, last_config_check, last_graph_dump;
- event_t *event;
-
- last_ping_check = now;
- last_config_check = now;
- last_graph_dump = now;
-
- srand(now);
-
- running = true;
+static void sigterm_handler(int signal, short events, void *data) {
+ logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
+ event_loopexit(NULL);
+}
- while(running) {
- now = time(NULL);
+static void sighup_handler(int signal, short events, void *data) {
+ logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
+ reload_configuration();
+}
- // tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
- tv.tv_sec = 1;
- tv.tv_usec = 0;
+int reload_configuration(void) {
+ connection_t *c;
+ splay_node_t *node, *next;
+ char *fname;
+ struct stat s;
+ static time_t last_config_check = 0;
- maxfd = build_fdset(&readset, &writeset);
+ /* Reread our own configuration file */
-#ifdef HAVE_MINGW
- LeaveCriticalSection(&mutex);
-#endif
- r = select(maxfd + 1, &readset, &writeset, NULL, &tv);
-#ifdef HAVE_MINGW
- EnterCriticalSection(&mutex);
-#endif
+ exit_configuration(&config_tree);
+ init_configuration(&config_tree);
- if(r < 0) {
- if(!sockwouldblock(sockerrno)) {
- logger(LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno));
- dump_connections();
- return 1;
- }
- }
-
- if(r > 0)
- check_network_activity(&readset, &writeset);
+ if(!read_server_config()) {
+ logger(LOG_ERR, "Unable to reread configuration file, exitting.");
+ event_loopexit(NULL);
+ return EINVAL;
+ }
- if(do_purge) {
- purge();
- do_purge = false;
+ /* Close connections to hosts that have a changed or deleted host config file */
+
+ for(node = connection_tree->head; node; node = next) {
+ c = node->data;
+ next = node->next;
+
+ if(c->outgoing) {
+ free(c->outgoing->name);
+ if(c->outgoing->ai)
+ freeaddrinfo(c->outgoing->ai);
+ free(c->outgoing);
+ c->outgoing = NULL;
}
+
+ xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
+ if(stat(fname, &s) || s.st_mtime > last_config_check)
+ terminate_connection(c, c->status.active);
+ free(fname);
+ }
- /* Let's check if everybody is still alive */
-
- if(last_ping_check + pingtimeout < now) {
- check_dead_connections();
- last_ping_check = now;
-
- if(routing_mode == RMODE_SWITCH)
- age_subnets();
-
- age_past_requests();
-
- /* Should we regenerate our key? */
-
- if(keyexpires < now) {
- avl_node_t *node;
- node_t *n;
+ last_config_check = time(NULL);
- ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
+ /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
- for(node = node_tree->head; node; node = node->next) {
- n = node->data;
- if(n->inkey) {
- free(n->inkey);
- n->inkey = NULL;
- }
- }
+ if(strictsubnets) {
+ subnet_t *subnet;
- send_key_changed(broadcast, myself);
- keyexpires = now + keylifetime;
- }
+
- if(contradicting_del_edge && contradicting_add_edge) {
- logger(LOG_WARNING, "Possible node with same Name as us!");
-
- if(rand() % 3 == 0) {
- logger(LOG_ERR, "Shutting down, check configuration of all nodes for duplicate Names!");
- running = false;
- break;
- }
-
- contradicting_add_edge = 0;
- contradicting_del_edge = 0;
- }
+ for(node = subnet_tree->head; node; node = node->next) {
+ subnet = node->data;
+ subnet->expires = 1;
}
- if(sigalrm) {
- avl_node_t *node;
- logger(LOG_INFO, "Flushing event queue");
- expire_events();
- for(node = connection_tree->head; node; node = node->next) {
- connection_t *c = node->data;
- send_ping(c);
+ load_all_subnets();
+
+ for(node = subnet_tree->head; node; node = next) {
+ next = node->next;
+ subnet = node->data;
+ if(subnet->expires == 1) {
+ send_del_subnet(broadcast, subnet);
+ if(subnet->owner->status.reachable)
+ subnet_update(subnet->owner, subnet, false);
+ subnet_del(subnet->owner, subnet);
+ } else if(subnet->expires == -1) {
+ subnet->expires = 0;
+ } else {
+ send_add_subnet(broadcast, subnet);
+ if(subnet->owner->status.reachable)
+ subnet_update(subnet->owner, subnet, true);
}
- sigalrm = false;
- }
-
- while((event = get_expired_event())) {
- event->handler(event->data);
- free_event(event);
}
+ }
- if(sighup) {
- connection_t *c;
- avl_node_t *node, *next;
- char *fname;
- struct stat s;
-
- sighup = false;
-
- /* Reread our own configuration file */
-
- exit_configuration(&config_tree);
- init_configuration(&config_tree);
-
- if(!read_server_config()) {
- logger(LOG_ERR, "Unable to reread configuration file, exitting.");
- return 1;
- }
-
- /* Cancel non-active outgoing connections */
-
- for(node = connection_tree->head; node; node = next) {
- next = node->next;
- c = node->data;
-
- c->outgoing = NULL;
-
- if(c->status.connecting) {
- terminate_connection(c, false);
- connection_del(c);
- }
- }
-
- /* Wipe list of outgoing connections */
-
- for(list_node_t *node = outgoing_list->head; node; node = node->next) {
- outgoing_t *outgoing = node->data;
-
- if(outgoing->event)
- event_del(outgoing->event);
- }
-
- list_delete_list(outgoing_list);
-
- /* Close connections to hosts that have a changed or deleted host config file */
-
- for(node = connection_tree->head; node; node = node->next) {
- c = node->data;
-
- xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
- if(stat(fname, &s) || s.st_mtime > last_config_check)
- terminate_connection(c, c->status.active);
- free(fname);
- }
+ /* Try to make outgoing connections */
+
+ try_outgoing_connections();
- last_config_check = now;
+ return 0;
+}
- /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
+void retry(void) {
+ connection_t *c;
+ splay_node_t *node;
- if(strictsubnets) {
- subnet_t *subnet;
+ for(node = connection_tree->head; node; node = node->next) {
+ c = node->data;
+
+ if(c->outgoing && !c->node) {
+ if(timeout_initialized(&c->outgoing->ev))
+ event_del(&c->outgoing->ev);
+ if(c->status.connecting)
+ close(c->socket);
+ c->outgoing->timeout = 0;
+ do_outgoing_connection(c);
+ }
+ }
+}
- for(node = subnet_tree->head; node; node = node->next) {
- subnet = node->data;
- subnet->expires = 1;
- }
+/*
+ this is where it all happens...
+*/
+int main_loop(void) {
+ struct event timeout_event;
+ struct event sighup_event;
+ struct event sigterm_event;
+ struct event sigquit_event;
- load_all_subnets();
-
- for(node = subnet_tree->head; node; node = next) {
- next = node->next;
- subnet = node->data;
- if(subnet->expires == 1) {
- send_del_subnet(broadcast, subnet);
- if(subnet->owner->status.reachable)
- subnet_update(subnet->owner, subnet, false);
- subnet_del(subnet->owner, subnet);
- } else if(subnet->expires == -1) {
- subnet->expires = 0;
- } else {
- send_add_subnet(broadcast, subnet);
- if(subnet->owner->status.reachable)
- subnet_update(subnet->owner, subnet, true);
- }
- }
- }
+ timeout_set(&timeout_event, timeout_handler, &timeout_event);
+ event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
- /* Try to make outgoing connections */
-
- try_outgoing_connections();
- }
-
- /* Dump graph if wanted every 60 seconds*/
+#ifdef SIGHUP
+ signal_set(&sighup_event, SIGHUP, sighup_handler, NULL);
+ signal_add(&sighup_event, NULL);
+#endif
+#ifdef SIGTERM
+ signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL);
+ signal_add(&sigterm_event, NULL);
+#endif
+#ifdef SIGQUIT
+ signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
+ signal_add(&sigquit_event, NULL);
+#endif
- if(last_graph_dump + 60 < now) {
- dump_graph();
- last_graph_dump = now;
- }
+ if(event_loop(0) < 0) {
+ logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno));
+ return 1;
}
+ signal_del(&sighup_event);
+ signal_del(&sigterm_event);
+ signal_del(&sigquit_event);
+ event_del(&timeout_event);
+
return 0;
}
extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;
-extern int keyexpires;
extern int keylifetime;
extern bool do_prune;
-extern bool do_purge;
extern char *myport;
-extern time_t now;
+ extern int contradicting_add_edge;
+ extern int contradicting_del_edge;
/* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
#include "connection.h"
int nextpkt = 0;
vpn_packet_t *outpkt;
int origlen;
- int outlen, outpad;
+ size_t outlen;
+ #if defined(SOL_IP) && defined(IP_TOS)
static int priority = 0;
+ #endif
int origpriority;
int sock;
return false;
}
- /* Else, check if a harnessed public key is in the config file */
-
- xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
- fp = fopen(fname, "r");
-
- if(fp) {
- c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
- fclose(fp);
- }
-
- free(fname);
-
- if(c->rsa_key)
- return true;
-
- /* Try again with PEM_read_RSA_PUBKEY. */
-
- xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
- fp = fopen(fname, "r");
-
- if(fp) {
- c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
-// RSA_blinding_on(c->rsa_key, NULL);
- fclose(fp);
- }
+ result = rsa_read_pem_public_key(&c->rsa, fp);
+ fclose(fp);
+ if(!result)
+ logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
free(fname);
-
- if(c->rsa_key)
- return true;
-
- logger(LOG_ERR, "No public key for %s specified!", c->name);
-
- return false;
+ return result;
}
-bool read_rsa_private_key(void) {
+bool read_rsa_private_key() {
FILE *fp;
- char *fname, *key, *pubkey;
- struct stat s;
+ char *fname;
+ char *n, *d;
+ bool result;
- if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) {
- if(!get_config_string(lookup_config(config_tree, "PublicKey"), &pubkey)) {
+ /* First, check for simple PrivateKey statement */
+
+ if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
- if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &n)) {
++ if(!get_config_string(lookup_config(config_tree, "PublicKey"), &n)) {
logger(LOG_ERR, "PrivateKey used but no PublicKey found!");
+ free(d);
return false;
}
- myself->connection->rsa_key = RSA_new();
-// RSA_blinding_on(myself->connection->rsa_key, NULL);
- BN_hex2bn(&myself->connection->rsa_key->d, key);
- BN_hex2bn(&myself->connection->rsa_key->n, pubkey);
- BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
- free(key);
- free(pubkey);
+ result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d);
+ free(n);
+ free(d);
return true;
}
/* Generate packet encryption key */
- if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher))
- if(get_config_string
- (lookup_config(config_tree, "Cipher"), &cipher)) {
- if(!strcasecmp(cipher, "none")) {
- myself->incipher = NULL;
- } else {
- myself->incipher = EVP_get_cipherbyname(cipher);
++ if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
+ cipher = xstrdup("blowfish");
- if(!myself->incipher) {
- logger(LOG_ERR, "Unrecognized cipher type!");
- return false;
- }
- }
- } else
- myself->incipher = EVP_bf_cbc();
-
- if(myself->incipher)
- myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len;
- else
- myself->inkeylength = 1;
-
- myself->connection->outcipher = EVP_bf_ofb();
+ if(!cipher_open_by_name(&myself->incipher, cipher)) {
+ logger(LOG_ERR, "Unrecognized cipher type!");
+ return false;
+ }
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
keylifetime = 3600;
- keyexpires = now + keylifetime;
-
+ regenerate_key();
+
/* Check if we want to use message authentication codes... */
- if(get_config_string(lookup_config(config_tree, "Digest"), &digest)) {
- if(!strcasecmp(digest, "none")) {
- myself->indigest = NULL;
- } else {
- myself->indigest = EVP_get_digestbyname(digest);
+ if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest))
+ digest = xstrdup("sha1");
- if(!myself->indigest) {
- logger(LOG_ERR, "Unrecognized digest type!");
- return false;
- }
- }
- } else
- myself->indigest = EVP_sha1();
-
- myself->connection->outdigest = EVP_sha1();
-
- if(get_config_int(lookup_config(config_tree, "MACLength"), &myself->inmaclength)) {
- if(myself->indigest) {
- if(myself->inmaclength > myself->indigest->md_size) {
- logger(LOG_ERR, "MAC length exceeds size of digest!");
- return false;
- } else if(myself->inmaclength < 0) {
- logger(LOG_ERR, "Bogus MAC length!");
- return false;
- }
- }
- } else
- myself->inmaclength = 4;
+ int maclength = 4;
- get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &maclength);
++ get_config_int(lookup_config(config_tree, "MACLength"), &maclength);
+
+ if(maclength < 0) {
+ logger(LOG_ERR, "Bogus MAC length!");
+ return false;
+ }
- myself->connection->outmaclength = 0;
+ if(!digest_open_by_name(&myself->indigest, digest, maclength)) {
+ logger(LOG_ERR, "Unrecognized digest type!");
+ return false;
+ }
/* Compression */
#if defined(SOL_TCP) && defined(TCP_NODELAY)
option = 1;
- setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof option);
- setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof(option));
++ setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof option);
#endif
#if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
- setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof option);
- setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof(option));
++ setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof option);
#endif
}
/* Optimize TCP settings */
option = 1;
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option);
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
++ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
+ memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
- if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr)) {
- if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) {
++ if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof ifr)) {
closesocket(nfd);
logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
strerror(sockerrno));
#endif
option = 1;
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option);
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
++ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
bool sigalrm = false;
extern char *identname;
-extern char *pidfilename;
extern char **g_argv;
extern bool use_logfile;
-extern volatile bool running;
+ #ifndef HAVE_MINGW
sigset_t emptysigset;
+ #endif
-static int saved_debug_level = -1;
-
static void memory_full(int size) {
logger(LOG_ERR, "Memory exhausted (couldn't allocate %d bytes), exitting.", size);
exit(1);
program_name);
else {
printf("Usage: %s [option]...\n\n", program_name);
- printf(" -c, --config=DIR Read configuration options from DIR.\n"
- " -D, --no-detach Don't fork and detach.\n"
- " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n"
- " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n"
- " -n, --net=NETNAME Connect to net NETNAME.\n"
- " -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n"
- " -L, --mlock Lock tinc into main memory.\n"
- " --logfile[=FILENAME] Write log entries to a logfile.\n"
- " --pidfile=FILENAME Write PID to FILENAME.\n"
- " -o [HOST.]KEY=VALUE Set global/host configuration value.\n"
- " -R, --chroot chroot to NET dir at startup.\n"
- " -U, --user=USER setuid to given USER at startup.\n"
- " --help Display this help and exit.\n"
- " --version Output version information and exit.\n\n");
+ printf( " -c, --config=DIR Read configuration options from DIR.\n"
+ " -D, --no-detach Don't fork and detach.\n"
+ " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n"
+ " -n, --net=NETNAME Connect to net NETNAME.\n"
+ " -L, --mlock Lock tinc into main memory.\n"
+ " --logfile[=FILENAME] Write log entries to a logfile.\n"
+ " --controlcookie=FILENAME Write control socket cookie to FILENAME.\n"
+ " --bypass-security Disables meta protocol security, for debugging.\n"
++ " -o [HOST.]KEY=VALUE Set global/host configuration value.\n"
+ " -R, --chroot chroot to NET dir at startup.\n"
+ " -U, --user=USER setuid to given USER at startup.\n" " --help Display this help and exit.\n"
+ " --version Output version information and exit.\n\n");
printf("Report bugs to tinc@tinc-vpn.org.\n");
}
}
static bool parse_options(int argc, char **argv) {
+ config_t *cfg;
int r;
int option_index = 0;
+ int lineno = 0;
- while((r = getopt_long(argc, argv, "c:DLd::n:RU:", long_options, &option_index)) != EOF) {
+ cmdline_conf = list_alloc((list_action_t)free_config);
+
- while((r = getopt_long(argc, argv, "c:DLd::k::n:o:K::RU:", long_options, &option_index)) != EOF) {
++ while((r = getopt_long(argc, argv, "c:DLd::n:o:RU:", long_options, &option_index)) != EOF) {
switch (r) {
case 0: /* long option */
break;
debug_level++;
break;
- case 'k': /* kill old tincds */
-#ifndef HAVE_MINGW
- if(optarg) {
- if(!strcasecmp(optarg, "HUP"))
- kill_tincd = SIGHUP;
- else if(!strcasecmp(optarg, "TERM"))
- kill_tincd = SIGTERM;
- else if(!strcasecmp(optarg, "KILL"))
- kill_tincd = SIGKILL;
- else if(!strcasecmp(optarg, "USR1"))
- kill_tincd = SIGUSR1;
- else if(!strcasecmp(optarg, "USR2"))
- kill_tincd = SIGUSR2;
- else if(!strcasecmp(optarg, "WINCH"))
- kill_tincd = SIGWINCH;
- else if(!strcasecmp(optarg, "INT"))
- kill_tincd = SIGINT;
- else if(!strcasecmp(optarg, "ALRM"))
- kill_tincd = SIGALRM;
- else {
- kill_tincd = atoi(optarg);
-
- if(!kill_tincd) {
- fprintf(stderr, "Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n",
- optarg);
- usage(true);
- return false;
- }
- }
- } else
- kill_tincd = SIGTERM;
-#else
- kill_tincd = 1;
-#endif
- break;
-
case 'n': /* net name given */
- netname = xstrdup(optarg);
+ /* netname "." is special: a "top-level name" */
+ netname = strcmp(optarg, ".") != 0 ?
+ xstrdup(optarg) : NULL;
+ break;
+
+ case 'o': /* option */
+ cfg = parse_config_line(optarg, NULL, ++lineno);
+ if (!cfg)
+ return false;
+ list_insert_tail(cmdline_conf, cfg);
break;
- case 'K': /* generate public/private keypair */
- if(optarg) {
- generate_keys = atoi(optarg);
-
- if(generate_keys < 512) {
- fprintf(stderr, "Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n",
- optarg);
- usage(true);
- return false;
- }
-
- generate_keys &= ~7; /* Round it to bytes */
- } else
- generate_keys = 2048;
- break;
-
case 'R': /* chroot to NETNAME dir */
do_chroot = true;
break;