/*
net_setup.c -- Setup.
Copyright (C) 1998-2005 Ivo Timmermans,
- 2000-2013 Guus Sliepen <guus@tinc-vpn.org>
+ 2000-2014 Guus Sliepen <guus@tinc-vpn.org>
2006 Scott Lamb <slamb@slamb.org>
2010 Brandon Black <blblack@gmail.com>
#include "protocol.h"
#include "route.h"
#include "rsa.h"
+#include "script.h"
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
char *myport;
+static char *myname;
static io_t device_io;
devops_t devops;
+bool device_standby = false;
char *proxyhost;
char *proxyport;
char *proxyuser;
char *proxypass;
proxytype_t proxytype;
-int autoconnect;
+bool autoconnect;
+bool disablebuggypeers;
char *scriptinterpreter;
char *scriptextension;
bool node_read_ecdsa_public_key(node_t *n) {
- if(ecdsa_active(&n->ecdsa))
+ if(ecdsa_active(n->ecdsa))
return true;
splay_tree_t *config_tree;
FILE *fp;
- char *pubname = NULL, *hcfname = NULL;
+ char *pubname = NULL;
char *p;
- bool result = false;
-
- xasprintf(&hcfname, "%s" SLASH "hosts" SLASH "%s", confbase, n->name);
init_configuration(&config_tree);
- if(!read_config_file(config_tree, hcfname))
+ if(!read_host_config(config_tree, n->name))
goto exit;
- /* First, check for simple ECDSAPublicKey statement */
+ /* First, check for simple Ed25519PublicKey statement */
- if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) {
- result = ecdsa_set_base64_public_key(&n->ecdsa, p);
+ if(get_config_string(lookup_config(config_tree, "Ed25519PublicKey"), &p)) {
+ n->ecdsa = ecdsa_set_base64_public_key(p);
free(p);
goto exit;
}
- /* Else, check for ECDSAPublicKeyFile statement and read it */
+ /* Else, check for Ed25519PublicKeyFile statement and read it */
- if(!get_config_string(lookup_config(config_tree, "ECDSAPublicKeyFile"), &pubname))
+ if(!get_config_string(lookup_config(config_tree, "Ed25519PublicKeyFile"), &pubname))
xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, n->name);
fp = fopen(pubname, "r");
- if(!fp) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s", pubname, strerror(errno));
+ if(!fp)
goto exit;
- }
- result = ecdsa_read_pem_public_key(&n->ecdsa, fp);
+ n->ecdsa = ecdsa_read_pem_public_key(fp);
fclose(fp);
exit:
exit_configuration(&config_tree);
- free(hcfname);
free(pubname);
- return result;
+ return n->ecdsa;
}
bool read_ecdsa_public_key(connection_t *c) {
+ if(ecdsa_active(c->ecdsa))
+ return true;
+
FILE *fp;
char *fname;
char *p;
- bool result;
- /* First, check for simple ECDSAPublicKey statement */
+ if(!c->config_tree) {
+ init_configuration(&c->config_tree);
+ if(!read_host_config(c->config_tree, c->name))
+ return false;
+ }
+
+ /* First, check for simple Ed25519PublicKey statement */
- if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) {
- result = ecdsa_set_base64_public_key(&c->ecdsa, p);
+ if(get_config_string(lookup_config(c->config_tree, "Ed25519PublicKey"), &p)) {
+ c->ecdsa = ecdsa_set_base64_public_key(p);
free(p);
- return result;
+ return c->ecdsa;
}
- /* Else, check for ECDSAPublicKeyFile statement and read it */
+ /* Else, check for Ed25519PublicKeyFile statement and read it */
- if(!get_config_string(lookup_config(c->config_tree, "ECDSAPublicKeyFile"), &fname))
+ if(!get_config_string(lookup_config(c->config_tree, "Ed25519PublicKeyFile"), &fname))
xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
fp = fopen(fname, "r");
if(!fp) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s",
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error reading Ed25519 public key file `%s': %s",
fname, strerror(errno));
free(fname);
return false;
}
- result = ecdsa_read_pem_public_key(&c->ecdsa, fp);
- fclose(fp);
+ c->ecdsa = ecdsa_read_pem_public_key(fp);
- if(!result)
- logger(DEBUG_ALWAYS, LOG_ERR, "Parsing ECDSA public key file `%s' failed.", fname);
+ if(!c->ecdsa && errno != ENOENT)
+ logger(DEBUG_ALWAYS, LOG_ERR, "Parsing Ed25519 public key file `%s' failed.", fname);
+
+ fclose(fp);
free(fname);
- return result;
+ return c->ecdsa;
}
+#ifndef DISABLE_LEGACY
bool read_rsa_public_key(connection_t *c) {
FILE *fp;
char *fname;
char *n;
- bool result;
/* First, check for simple PublicKey statement */
if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) {
- result = rsa_set_hex_public_key(&c->rsa, n, "FFFF");
+ c->rsa = rsa_set_hex_public_key(n, "FFFF");
free(n);
- return result;
+ return c->rsa;
}
/* Else, check for PublicKeyFile statement and read it */
return false;
}
- result = rsa_read_pem_public_key(&c->rsa, fp);
+ c->rsa = rsa_read_pem_public_key(fp);
fclose(fp);
- if(!result)
+ if(!c->rsa)
logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
free(fname);
- return result;
+ return c->rsa;
}
+#endif
static bool read_ecdsa_private_key(void) {
FILE *fp;
char *fname;
- bool result;
/* Check for PrivateKeyFile statement and read it */
- if(!get_config_string(lookup_config(config_tree, "ECDSAPrivateKeyFile"), &fname))
- xasprintf(&fname, "%s" SLASH "ecdsa_key.priv", confbase);
+ if(!get_config_string(lookup_config(config_tree, "Ed25519PrivateKeyFile"), &fname))
+ xasprintf(&fname, "%s" SLASH "ed25519_key.priv", confbase);
fp = fopen(fname, "r");
if(!fp) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA private key file `%s': %s", fname, strerror(errno));
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error reading Ed25519 private key file `%s': %s", fname, strerror(errno));
+ if(errno == ENOENT)
+ logger(DEBUG_ALWAYS, LOG_INFO, "Create an Ed25519 keypair with `tinc -n %s generate-ed25519-keys'.", netname ?: ".");
free(fname);
return false;
}
struct stat s;
if(fstat(fileno(fp), &s)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat ECDSA private key file `%s': %s'", fname, strerror(errno));
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat Ed25519 private key file `%s': %s'", fname, strerror(errno));
free(fname);
return false;
}
if(s.st_mode & ~0100700)
- logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for ECDSA private key file `%s'!", fname);
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for Ed25519 private key file `%s'!", fname);
#endif
- result = ecdsa_read_pem_private_key(&myself->connection->ecdsa, fp);
+ myself->connection->ecdsa = ecdsa_read_pem_private_key(fp);
fclose(fp);
- if(!result)
- logger(DEBUG_ALWAYS, LOG_ERR, "Reading ECDSA private key file `%s' failed: %s", fname, strerror(errno));
+ if(!myself->connection->ecdsa)
+ logger(DEBUG_ALWAYS, LOG_ERR, "Reading Ed25519 private key file `%s' failed", fname);
+ free(fname);
+ return myself->connection->ecdsa;
+}
+
+static bool read_invitation_key(void) {
+ FILE *fp;
+ char *fname;
+
+ if(invitation_key) {
+ ecdsa_free(invitation_key);
+ invitation_key = NULL;
+ }
+
+ xasprintf(&fname, "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase);
+
+ fp = fopen(fname, "r");
+
+ if(fp) {
+ invitation_key = ecdsa_read_pem_private_key(fp);
+ fclose(fp);
+ if(!invitation_key)
+ logger(DEBUG_ALWAYS, LOG_ERR, "Reading Ed25519 private key file `%s' failed", fname);
+ }
+
free(fname);
- return result;
+ return invitation_key;
}
+#ifndef DISABLE_LEGACY
static bool read_rsa_private_key(void) {
FILE *fp;
char *fname;
char *n, *d;
- bool result;
/* First, check for simple PrivateKey statement */
free(d);
return false;
}
- result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d);
+ myself->connection->rsa = rsa_set_hex_private_key(n, "FFFF", d);
free(n);
free(d);
- return result;
+ return myself->connection->rsa;
}
/* Else, check for PrivateKeyFile statement and read it */
if(!fp) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA private key file `%s': %s",
fname, strerror(errno));
+ if(errno == ENOENT)
+ logger(DEBUG_ALWAYS, LOG_INFO, "Create an RSA keypair with `tinc -n %s generate-rsa-keys'.", netname ?: ".");
free(fname);
return false;
}
logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for RSA private key file `%s'!", fname);
#endif
- result = rsa_read_pem_private_key(&myself->connection->rsa, fp);
+ myself->connection->rsa = rsa_read_pem_private_key(fp);
fclose(fp);
- if(!result)
+ if(!myself->connection->rsa)
logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA private key file `%s' failed: %s", fname, strerror(errno));
free(fname);
- return result;
+ return myself->connection->rsa;
}
+#endif
static timeout_t keyexpire_timeout;
void regenerate_key(void) {
logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
send_key_changed();
+ for splay_each(node_t, n, node_tree)
+ n->status.validkey_in = false;
}
/*
// continue;
#endif
- char *fname;
- xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
-
splay_tree_t *config_tree;
init_configuration(&config_tree);
read_config_options(config_tree, ent->d_name);
- read_config_file(config_tree, fname);
- free(fname);
+ read_host_config(config_tree, ent->d_name);
if(!n) {
n = new_node();
char *get_name(void) {
char *name = NULL;
+ char *returned_name;
get_config_string(lookup_config(config_tree, "Name"), &name);
if(!name)
return NULL;
- if(*name == '$') {
- char *envname = getenv(name + 1);
- if(!envname) {
- if(strcmp(name + 1, "HOST")) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Invalid Name: environment variable %s does not exist\n", name + 1);
- return false;
- }
- char envname[32];
- if(gethostname(envname, 32)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not get hostname: %s\n", strerror(errno));
- return false;
- }
- envname[31] = 0;
- }
- free(name);
- name = xstrdup(envname);
- for(char *c = name; *c; c++)
- if(!isalnum(*c))
- *c = '_';
- }
-
- if(!check_id(name)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
- free(name);
- return false;
- }
-
- return name;
+ returned_name = replace_name(name);
+ free(name);
+ return returned_name;
}
bool setup_myself_reloadable(void) {
free(scriptextension);
if(!get_config_string(lookup_config(config_tree, "ScriptsExtension"), &scriptextension))
-#ifdef HAVE_MINGW
- scriptextension = xstrdup(".bat");
-#else
scriptextension = xstrdup("");
-#endif
get_config_string(lookup_config(config_tree, "Proxy"), &proxy);
if(proxy) {
if(myself->options & OPTION_TCPONLY)
myself->options |= OPTION_INDIRECT;
+ get_config_bool(lookup_config(config_tree, "UDPDiscovery"), &udp_discovery);
+ get_config_int(lookup_config(config_tree, "UDPDiscoveryKeepaliveInterval"), &udp_discovery_keepalive_interval);
+ get_config_int(lookup_config(config_tree, "UDPDiscoveryInterval"), &udp_discovery_interval);
+ get_config_int(lookup_config(config_tree, "UDPDiscoveryTimeout"), &udp_discovery_timeout);
+
+ get_config_int(lookup_config(config_tree, "MTUInfoInterval"), &mtu_info_interval);
+ get_config_int(lookup_config(config_tree, "UDPInfoInterval"), &udp_info_interval);
+
get_config_bool(lookup_config(config_tree, "DirectOnly"), &directonly);
get_config_bool(lookup_config(config_tree, "LocalDiscovery"), &localdiscovery);
free(bmode);
}
+ const char* const DEFAULT_BROADCAST_SUBNETS[] = { "ff:ff:ff:ff:ff:ff", "255.255.255.255", "224.0.0.0/4", "ff00::/8" };
+ for (size_t i = 0; i < sizeof(DEFAULT_BROADCAST_SUBNETS) / sizeof(*DEFAULT_BROADCAST_SUBNETS); i++) {
+ subnet_t *s = new_subnet();
+ if (!str2net(s, DEFAULT_BROADCAST_SUBNETS[i]))
+ abort();
+ subnet_add(NULL, s);
+ }
+ for (config_t* cfg = lookup_config(config_tree, "BroadcastSubnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
+ subnet_t *s;
+ if (!get_config_subnet(cfg, &s))
+ continue;
+ subnet_add(NULL, s);
+ }
+
#if !defined(SOL_IP) || !defined(IP_TOS)
if(priorityinheritance)
logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
keylifetime = 3600;
- get_config_int(lookup_config(config_tree, "AutoConnect"), &autoconnect);
+ config_t *cfg = lookup_config(config_tree, "AutoConnect");
+ if(cfg) {
+ if(!get_config_bool(cfg, &autoconnect)) {
+ // Some backwards compatibility with when this option was an int
+ int val = 0;
+ get_config_int(cfg, &val);
+ autoconnect = val;
+ }
+ }
+
+ get_config_bool(lookup_config(config_tree, "DisableBuggyPeers"), &disablebuggypeers);
+
+ read_invitation_key();
return true;
}
+/*
+ Add listening sockets.
+*/
+static bool add_listen_address(char *address, bool bindto) {
+ char *port = myport;
+
+ if(address) {
+ char *space = strchr(address, ' ');
+ if(space) {
+ *space++ = 0;
+ port = space;
+ }
+
+ if(!strcmp(address, "*"))
+ *address = 0;
+ }
+
+ struct addrinfo *ai, hint = {0};
+ hint.ai_family = addressfamily;
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_protocol = IPPROTO_TCP;
+ hint.ai_flags = AI_PASSIVE;
+
+#if HAVE_DECL_RES_INIT
+ res_init();
+#endif
+ int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
+ free(address);
+
+ if(err || !ai) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
+ return false;
+ }
+
+ for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
+ // Ignore duplicate addresses
+ bool found = false;
+
+ for(int i = 0; i < listen_sockets; i++)
+ if(!memcmp(&listen_socket[i].sa, aip->ai_addr, aip->ai_addrlen)) {
+ found = true;
+ break;
+ }
+
+ if(found)
+ continue;
+
+ if(listen_sockets >= MAXSOCKETS) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
+ return false;
+ }
+
+ int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
+
+ if(tcp_fd < 0)
+ continue;
+
+ int udp_fd = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
+
+ if(tcp_fd < 0) {
+ close(tcp_fd);
+ continue;
+ }
+
+ io_add(&listen_socket[listen_sockets].tcp, handle_new_meta_connection, &listen_socket[listen_sockets], tcp_fd, IO_READ);
+ io_add(&listen_socket[listen_sockets].udp, handle_incoming_vpn_data, &listen_socket[listen_sockets], udp_fd, IO_READ);
+
+ if(debug_level >= DEBUG_CONNECTIONS) {
+ char *hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
+ logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
+ free(hostname);
+ }
+
+ listen_socket[listen_sockets].bindto = bindto;
+ memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
+ listen_sockets++;
+ }
+
+ freeaddrinfo(ai);
+ return true;
+}
+
+void device_enable(void) {
+ if (devops.enable)
+ devops.enable();
+
+ /* Run tinc-up script to further initialize the tap interface */
+
+ char *envp[5] = {NULL};
+ xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
+ xasprintf(&envp[1], "DEVICE=%s", device ? : "");
+ xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
+ xasprintf(&envp[3], "NAME=%s", myname);
+
+ execute_script("tinc-up", envp);
+
+ for(int i = 0; i < 4; i++)
+ free(envp[i]);
+}
+
+void device_disable(void) {
+ char *envp[5] = {NULL};
+ xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
+ xasprintf(&envp[1], "DEVICE=%s", device ? : "");
+ xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
+ xasprintf(&envp[3], "NAME=%s", myname);
+
+ execute_script("tinc-down", envp);
+
+ for(int i = 0; i < 4; i++)
+ free(envp[i]);
+
+ if (devops.disable)
+ devops.disable();
+}
+
/*
Configure node_t myself and set up the local sockets (listen only)
*/
static bool setup_myself(void) {
char *name, *hostname, *cipher, *digest, *type;
- char *fname = NULL;
char *address = NULL;
+ bool port_specified = false;
if(!(name = get_name())) {
logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
return false;
}
+ myname = xstrdup(name);
myself = new_node();
myself->connection = new_connection();
myself->name = name;
myself->connection->name = xstrdup(name);
- xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
- read_config_options(config_tree, name);
- read_config_file(config_tree, fname);
- free(fname);
+ read_host_config(config_tree, name);
if(!get_config_string(lookup_config(config_tree, "Port"), &myport))
myport = xstrdup("655");
-
- xasprintf(&myself->hostname, "MYSELF port %s", myport);
- myself->connection->hostname = xstrdup(myself->hostname);
+ else
+ port_specified = true;
myself->connection->options = 0;
myself->connection->protocol_major = PROT_MAJOR;
myself->options |= PROT_MINOR << 24;
- get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental);
-
- if(experimental && !read_ecdsa_private_key())
+#ifdef DISABLE_LEGACY
+ experimental = read_ecdsa_private_key();
+ if(!experimental) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "No private key available, cannot start tinc!");
return false;
+ }
+#else
+ if(!get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental)) {
+ experimental = read_ecdsa_private_key();
+ if(!experimental)
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Support for SPTPS disabled.");
+ } else {
+ if(experimental && !read_ecdsa_private_key())
+ return false;
+ }
- if(!read_rsa_private_key())
- return false;
+ if(!read_rsa_private_key()) {
+ if(experimental) {
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Support for legacy protocol disabled.");
+ } else {
+ logger(DEBUG_ALWAYS, LOG_ERR, "No private keys available, cannot start tinc!");
+ return false;
+ }
+ }
+#endif
+
+ /* Ensure myport is numeric */
if(!atoi(myport)) {
struct addrinfo *ai = str2addrinfo("localhost", myport, SOCK_DGRAM);
get_config_bool(lookup_config(config_tree, "TunnelServer"), &tunnelserver);
strictsubnets |= tunnelserver;
-
+ if(get_config_int(lookup_config(config_tree, "MaxConnectionBurst"), &max_connection_burst)) {
+ if(max_connection_burst <= 0) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "MaxConnectionBurst cannot be negative!");
+ return false;
+ }
+ }
if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) {
- if(udp_rcvbuf <= 0) {
+ if(udp_rcvbuf < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "UDPRcvBuf cannot be negative!");
return false;
}
}
if(get_config_int(lookup_config(config_tree, "UDPSndBuf"), &udp_sndbuf)) {
- if(udp_sndbuf <= 0) {
+ if(udp_sndbuf < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "UDPSndBuf cannot be negative!");
return false;
}
sptps_replaywin = replaywin;
}
+#ifndef DISABLE_LEGACY
/* Generate packet encryption key */
if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
cipher = xstrdup("blowfish");
- if(!cipher_open_by_name(&myself->incipher, cipher)) {
+ if(!strcasecmp(cipher, "none")) {
+ myself->incipher = NULL;
+ } else if(!(myself->incipher = cipher_open_by_name(cipher))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized cipher type!");
return false;
}
free(cipher);
- send_key_changed();
timeout_add(&keyexpire_timeout, keyexpire_handler, &keyexpire_timeout, &(struct timeval){keylifetime, rand() % 100000});
/* Check if we want to use message authentication codes... */
if(!get_config_string(lookup_config(config_tree, "Digest"), &digest))
digest = xstrdup("sha1");
- if(!digest_open_by_name(&myself->indigest, digest, maclength)) {
+ if(!strcasecmp(digest, "none")) {
+ myself->indigest = NULL;
+ } else if(!(myself->indigest = digest_open_by_name(digest, maclength))) {
logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized digest type!");
return false;
}
free(digest);
+#endif
/* Compression */
myself->nexthop = myself;
myself->via = myself;
myself->status.reachable = true;
- myself->last_state_change = time(NULL);
+ myself->last_state_change = now.tv_sec;
myself->status.sptps = experimental;
node_add(myself);
#endif
}
+ get_config_bool(lookup_config(config_tree, "DeviceStandby"), &device_standby);
+
if(!devops.setup())
return false;
if(device_fd >= 0)
io_add(&device_io, handle_device_data, NULL, device_fd, IO_READ);
- /* Run tinc-up script to further initialize the tap interface */
- char *envp[5];
- xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
- xasprintf(&envp[1], "DEVICE=%s", device ? : "");
- xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
- xasprintf(&envp[3], "NAME=%s", myself->name);
- envp[4] = NULL;
-
- execute_script("tinc-up", envp);
-
- for(int i = 0; i < 4; i++)
- free(envp[i]);
-
- /* Run subnet-up scripts for our own subnets */
-
- subnet_update(myself, NULL, true);
-
/* Open sockets */
-#ifndef HAVE_MINGW
- int unix_fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if(unix_fd < 0) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not create UNIX socket: %s", sockstrerror(errno));
- return false;
- }
-
- struct sockaddr_un sa;
- sa.sun_family = AF_UNIX;
- strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
-
- if(connect(unix_fd, (struct sockaddr *)&sa, sizeof sa) >= 0) {
- logger(DEBUG_ALWAYS, LOG_ERR, "UNIX socket %s is still in use!", unixsocketname);
- return false;
- }
-
- unlink(unixsocketname);
-
- if(bind(unix_fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not bind UNIX socket to %s: %s", unixsocketname, sockstrerror(errno));
- return false;
- }
-
- if(listen(unix_fd, 3) < 0) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not listen on UNIX socket %s: %s", unixsocketname, sockstrerror(errno));
- return false;
- }
-
- io_add(&unix_socket, handle_new_unix_connection, &unix_socket, unix_fd, IO_READ);
-#endif
-
if(!do_detach && getenv("LISTEN_FDS")) {
sockaddr_t sa;
socklen_t salen;
for(int i = 0; i < listen_sockets; i++) {
salen = sizeof sa;
if(getsockname(i + 3, &sa.sa, &salen) < 0) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(errno));
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(sockerrno));
return false;
}
}
} else {
listen_sockets = 0;
- config_t *cfg = lookup_config(config_tree, "BindToAddress");
+ int cfgs = 0;
- do {
+ for(config_t *cfg = lookup_config(config_tree, "BindToAddress"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
+ cfgs++;
get_config_string(cfg, &address);
- if(cfg)
- cfg = lookup_config_next(config_tree, cfg);
-
- char *port = myport;
-
- if(address) {
- char *space = strchr(address, ' ');
- if(space) {
- *space++ = 0;
- port = space;
- }
-
- if(!strcmp(address, "*"))
- *address = 0;
- }
-
- struct addrinfo *ai, hint = {0};
- hint.ai_family = addressfamily;
- hint.ai_socktype = SOCK_STREAM;
- hint.ai_protocol = IPPROTO_TCP;
- hint.ai_flags = AI_PASSIVE;
-
- int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
- free(address);
-
- if(err || !ai) {
- logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
- gai_strerror(err));
+ if(!add_listen_address(address, true))
return false;
- }
-
- for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
- if(listen_sockets >= MAXSOCKETS) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
- return false;
- }
-
- int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
-
- if(tcp_fd < 0)
- continue;
-
- int udp_fd = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
-
- if(tcp_fd < 0) {
- close(tcp_fd);
- continue;
- }
-
- io_add(&listen_socket[listen_sockets].tcp, handle_new_meta_connection, &listen_socket[listen_sockets], tcp_fd, IO_READ);
- io_add(&listen_socket[listen_sockets].udp, handle_incoming_vpn_data, &listen_socket[listen_sockets], udp_fd, IO_READ);
-
- if(debug_level >= DEBUG_CONNECTIONS) {
- hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
- logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
- free(hostname);
- }
+ }
- memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
- listen_sockets++;
- }
+ for(config_t *cfg = lookup_config(config_tree, "ListenAddress"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
+ cfgs++;
+ get_config_string(cfg, &address);
+ if(!add_listen_address(address, false))
+ return false;
+ }
- freeaddrinfo(ai);
- } while(cfg);
+ if(!cfgs)
+ if(!add_listen_address(address, NULL))
+ return false;
}
- if(listen_sockets)
- logger(DEBUG_ALWAYS, LOG_NOTICE, "Ready");
- else {
+ if(!listen_sockets) {
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to create any listening socket!");
return false;
}
- last_config_check = time(NULL);
+ /* If no Port option was specified, set myport to the port used by the first listening socket. */
+
+ if(!port_specified || atoi(myport) == 0) {
+ sockaddr_t sa;
+ socklen_t salen = sizeof sa;
+ if(!getsockname(listen_socket[0].udp.fd, &sa.sa, &salen)) {
+ free(myport);
+ sockaddr2str(&sa, NULL, &myport);
+ if(!myport)
+ myport = xstrdup("655");
+ }
+ }
+
+ xasprintf(&myself->hostname, "MYSELF port %s", myport);
+ myself->connection->hostname = xstrdup(myself->hostname);
+
+ /* Done. */
+
+ last_config_check = now.tv_sec;
return true;
}
if(!setup_myself())
return false;
+ if(!init_control())
+ return false;
+
+ if (!device_standby)
+ device_enable();
+
+ /* Run subnet-up scripts for our own subnets */
+
+ subnet_update(myself, NULL, true);
+
return true;
}
terminate_connection(c, false);
}
- list_delete_list(outgoing_list);
+ if(outgoing_list)
+ list_delete_list(outgoing_list);
if(myself && myself->connection) {
subnet_update(myself, NULL, false);
close(listen_socket[i].udp.fd);
}
-#ifndef HAVE_MINGW
- io_del(&unix_socket);
- close(unix_socket.fd);
-#endif
-
- char *envp[5];
- xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
- xasprintf(&envp[1], "DEVICE=%s", device ? : "");
- xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
- xasprintf(&envp[3], "NAME=%s", myself->name);
- envp[4] = NULL;
-
exit_requests();
exit_edges();
exit_subnets();
exit_nodes();
exit_connections();
- execute_script("tinc-down", envp);
+ if (!device_standby)
+ device_disable();
- if(myport) free(myport);
+ free(myport);
- for(int i = 0; i < 4; i++)
- free(envp[i]);
+ if (device_fd >= 0)
+ io_del(&device_io);
+ if (devops.close)
+ devops.close();
- devops.close();
+ exit_control();
+
+ free(myname);
+ free(scriptextension);
+ free(scriptinterpreter);
return;
}