Reduce pointer indirection for global list_t variables
[tinc] / src / net_setup.c
index 17675ae..4e1f2b8 100644 (file)
@@ -1,7 +1,7 @@
 /*
     net_setup.c -- Setup.
     Copyright (C) 1998-2005 Ivo Timmermans,
-                  2000-2017 Guus Sliepen <guus@tinc-vpn.org>
+                  2000-2021 Guus Sliepen <guus@tinc-vpn.org>
                   2006      Scott Lamb <slamb@slamb.org>
                   2010      Brandon Black <blblack@gmail.com>
 
 #include "system.h"
 
 #include "cipher.h"
+#include "conf_net.h"
 #include "conf.h"
 #include "connection.h"
+#include "compression.h"
 #include "control.h"
 #include "device.h"
 #include "digest.h"
 #include "process.h"
 #include "protocol.h"
 #include "route.h"
-#include "rsa.h"
 #include "script.h"
 #include "subnet.h"
 #include "utils.h"
 #include "xalloc.h"
+#include "keys.h"
 
 #ifdef HAVE_MINIUPNPC
 #include "upnp.h"
@@ -52,10 +54,11 @@ static io_t device_io;
 devops_t devops;
 bool device_standby = false;
 
-char *proxyhost;
-char *proxyport;
-char *proxyuser;
-char *proxypass;
+char *proxyhost = NULL;
+char *proxyport = NULL;
+char *proxyuser = NULL;
+char *proxypass = NULL;
+
 proxytype_t proxytype;
 bool autoconnect;
 bool disablebuggypeers;
@@ -108,146 +111,6 @@ exit:
        return n->ecdsa;
 }
 
-bool read_ecdsa_public_key(connection_t *c) {
-       if(ecdsa_active(c->ecdsa)) {
-               return true;
-       }
-
-       FILE *fp;
-       char *fname;
-       char *p;
-
-       if(!c->config_tree) {
-               init_configuration(&c->config_tree);
-
-               if(!read_host_config(c->config_tree, c->name, true)) {
-                       return false;
-               }
-       }
-
-       /* First, check for simple Ed25519PublicKey statement */
-
-       if(get_config_string(lookup_config(c->config_tree, "Ed25519PublicKey"), &p)) {
-               c->ecdsa = ecdsa_set_base64_public_key(p);
-               free(p);
-               return c->ecdsa;
-       }
-
-       /* Else, check for Ed25519PublicKeyFile statement and read it */
-
-       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 Ed25519 public key file `%s': %s",
-                      fname, strerror(errno));
-               free(fname);
-               return false;
-       }
-
-       c->ecdsa = ecdsa_read_pem_public_key(fp);
-
-       if(!c->ecdsa && errno != ENOENT) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Parsing Ed25519 public key file `%s' failed.", fname);
-       }
-
-       fclose(fp);
-       free(fname);
-       return c->ecdsa;
-}
-
-#ifndef DISABLE_LEGACY
-bool read_rsa_public_key(connection_t *c) {
-       FILE *fp;
-       char *fname;
-       char *n;
-
-       /* First, check for simple PublicKey statement */
-
-       if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) {
-               c->rsa = rsa_set_hex_public_key(n, "FFFF");
-               free(n);
-               return c->rsa;
-       }
-
-       /* Else, check for PublicKeyFile statement and read it */
-
-       if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) {
-               xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
-       }
-
-       fp = fopen(fname, "r");
-
-       if(!fp) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
-               free(fname);
-               return false;
-       }
-
-       c->rsa = rsa_read_pem_public_key(fp);
-       fclose(fp);
-
-       if(!c->rsa) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
-       }
-
-       free(fname);
-       return c->rsa;
-}
-#endif
-
-static bool read_ecdsa_private_key(void) {
-       FILE *fp;
-       char *fname;
-
-       /* Check for PrivateKeyFile statement and read it */
-
-       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 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;
-       }
-
-#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
-       struct stat s;
-
-       if(fstat(fileno(fp), &s)) {
-               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 Ed25519 private key file `%s'!", fname);
-       }
-
-#endif
-
-       myself->connection->ecdsa = ecdsa_read_pem_private_key(fp);
-       fclose(fp);
-
-       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[PATH_MAX];
@@ -274,73 +137,6 @@ static bool read_invitation_key(void) {
 }
 
 #ifndef DISABLE_LEGACY
-static bool read_rsa_private_key(void) {
-       FILE *fp;
-       char *fname;
-       char *n, *d;
-
-       /* First, check for simple PrivateKey statement */
-
-       if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
-               if(!get_config_string(lookup_config(config_tree, "PublicKey"), &n)) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "PrivateKey used but no PublicKey found!");
-                       free(d);
-                       return false;
-               }
-
-               myself->connection->rsa = rsa_set_hex_private_key(n, "FFFF", d);
-               free(n);
-               free(d);
-               return myself->connection->rsa;
-       }
-
-       /* Else, check for PrivateKeyFile statement and read it */
-
-       if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname)) {
-               xasprintf(&fname, "%s" SLASH "rsa_key.priv", confbase);
-       }
-
-       fp = fopen(fname, "r");
-
-       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;
-       }
-
-#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
-       struct stat s;
-
-       if(fstat(fileno(fp), &s)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat RSA 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 RSA private key file `%s'!", fname);
-       }
-
-#endif
-
-       myself->connection->rsa = rsa_read_pem_private_key(fp);
-       fclose(fp);
-
-       if(!myself->connection->rsa) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA private key file `%s' failed: %s", fname, strerror(errno));
-       }
-
-       free(fname);
-       return myself->connection->rsa;
-}
-#endif
-
 static timeout_t keyexpire_timeout;
 
 static void keyexpire_handler(void *data) {
@@ -349,6 +145,7 @@ static void keyexpire_handler(void *data) {
                keylifetime, rand() % 100000
        });
 }
+#endif
 
 void regenerate_key(void) {
        logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
@@ -433,18 +230,10 @@ char *get_name(void) {
 }
 
 bool setup_myself_reloadable(void) {
-       char *proxy = NULL;
-       char *rmode = NULL;
-       char *fmode = NULL;
-       char *bmode = NULL;
-       char *afname = NULL;
-       char *space;
-       bool choice;
-
        free(scriptinterpreter);
        scriptinterpreter = NULL;
-       get_config_string(lookup_config(config_tree, "ScriptsInterpreter"), &scriptinterpreter);
 
+       get_config_string(lookup_config(config_tree, "ScriptsInterpreter"), &scriptinterpreter);
 
        free(scriptextension);
 
@@ -452,9 +241,13 @@ bool setup_myself_reloadable(void) {
                scriptextension = xstrdup("");
        }
 
+       char *proxy = NULL;
+
        get_config_string(lookup_config(config_tree, "Proxy"), &proxy);
 
        if(proxy) {
+               char *space;
+
                if((space = strchr(proxy, ' '))) {
                        *space++ = 0;
                }
@@ -473,9 +266,22 @@ bool setup_myself_reloadable(void) {
                        proxytype = PROXY_EXEC;
                } else {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type %s!", proxy);
+                       free(proxy);
                        return false;
                }
 
+               free(proxyhost);
+               proxyhost = NULL;
+
+               free(proxyport);
+               proxyport = NULL;
+
+               free(proxyuser);
+               proxyuser = NULL;
+
+               free(proxypass);
+               proxypass = NULL;
+
                switch(proxytype) {
                case PROXY_NONE:
                default:
@@ -484,10 +290,11 @@ bool setup_myself_reloadable(void) {
                case PROXY_EXEC:
                        if(!space || !*space) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Argument expected for proxy type exec!");
+                               free(proxy);
                                return false;
                        }
 
-                       proxyhost =  xstrdup(space);
+                       proxyhost = xstrdup(space);
                        break;
 
                case PROXY_SOCKS4:
@@ -500,6 +307,14 @@ bool setup_myself_reloadable(void) {
                                *space++ = 0, proxyport = space;
                        }
 
+                       if(!proxyhost || !*proxyhost || !proxyport || !*proxyport) {
+                               logger(DEBUG_ALWAYS, LOG_ERR, "Host and port argument expected for proxy!");
+                               proxyport = NULL;
+                               proxyhost = NULL;
+                               free(proxy);
+                               return false;
+                       }
+
                        if(space && (space = strchr(space, ' '))) {
                                *space++ = 0, proxyuser = space;
                        }
@@ -508,11 +323,6 @@ bool setup_myself_reloadable(void) {
                                *space++ = 0, proxypass = space;
                        }
 
-                       if(!proxyhost || !*proxyhost || !proxyport || !*proxyport) {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Host and port argument expected for proxy!");
-                               return false;
-                       }
-
                        proxyhost = xstrdup(proxyhost);
                        proxyport = xstrdup(proxyport);
 
@@ -530,6 +340,8 @@ bool setup_myself_reloadable(void) {
                free(proxy);
        }
 
+       bool choice;
+
        if(get_config_bool(lookup_config(config_tree, "IndirectData"), &choice) && choice) {
                myself->options |= OPTION_INDIRECT;
        }
@@ -553,6 +365,8 @@ bool setup_myself_reloadable(void) {
        get_config_bool(lookup_config(config_tree, "DirectOnly"), &directonly);
        get_config_bool(lookup_config(config_tree, "LocalDiscovery"), &localdiscovery);
 
+       char *rmode = NULL;
+
        if(get_config_string(lookup_config(config_tree, "Mode"), &rmode)) {
                if(!strcasecmp(rmode, "router")) {
                        routing_mode = RMODE_ROUTER;
@@ -562,12 +376,15 @@ bool setup_myself_reloadable(void) {
                        routing_mode = RMODE_HUB;
                } else {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Invalid routing mode!");
+                       free(rmode);
                        return false;
                }
 
                free(rmode);
        }
 
+       char *fmode = NULL;
+
        if(get_config_string(lookup_config(config_tree, "Forwarding"), &fmode)) {
                if(!strcasecmp(fmode, "off")) {
                        forwarding_mode = FMODE_OFF;
@@ -577,6 +394,7 @@ bool setup_myself_reloadable(void) {
                        forwarding_mode = FMODE_KERNEL;
                } else {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Invalid forwarding mode!");
+                       free(fmode);
                        return false;
                }
 
@@ -600,6 +418,8 @@ bool setup_myself_reloadable(void) {
        get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
        get_config_bool(lookup_config(config_tree, "DecrementTTL"), &decrement_ttl);
 
+       char *bmode = NULL;
+
        if(get_config_string(lookup_config(config_tree, "Broadcast"), &bmode)) {
                if(!strcasecmp(bmode, "no")) {
                        broadcast_mode = BMODE_NONE;
@@ -609,12 +429,21 @@ bool setup_myself_reloadable(void) {
                        broadcast_mode = BMODE_DIRECT;
                } else {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Invalid broadcast mode!");
+                       free(bmode);
                        return false;
                }
 
                free(bmode);
        }
 
+       /* Delete all broadcast subnets before re-adding them */
+
+       for splay_each(subnet_t, s, subnet_tree) {
+               if(!s->owner) {
+                       splay_delete_node(subnet_tree, node);
+               }
+       }
+
        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++) {
@@ -666,6 +495,8 @@ bool setup_myself_reloadable(void) {
                maxtimeout = 900;
        }
 
+       char *afname = NULL;
+
        if(get_config_string(lookup_config(config_tree, "AddressFamily"), &afname)) {
                if(!strcasecmp(afname, "IPv4")) {
                        addressfamily = AF_INET;
@@ -675,6 +506,7 @@ bool setup_myself_reloadable(void) {
                        addressfamily = AF_UNSPEC;
                } else {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Invalid address family!");
+                       free(afname);
                        return false;
                }
 
@@ -687,15 +519,8 @@ bool setup_myself_reloadable(void) {
                keylifetime = 3600;
        }
 
-       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;
-               }
+       if(!get_config_bool(lookup_config(config_tree, "AutoConnect"), &autoconnect)) {
+               autoconnect = true;
        }
 
        get_config_bool(lookup_config(config_tree, "DisableBuggyPeers"), &disablebuggypeers);
@@ -767,6 +592,7 @@ static bool add_listen_address(char *address, bool bindto) {
 
                if(listen_sockets >= MAXSOCKETS) {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
+                       freeaddrinfo(ai);
                        return false;
                }
 
@@ -829,7 +655,7 @@ void device_disable(void) {
   Configure node_t myself and set up the local sockets (listen only)
 */
 static bool setup_myself(void) {
-       char *name, *hostname, *cipher, *digest, *type;
+       char *name, *hostname, *type;
        char *address = NULL;
        bool port_specified = false;
 
@@ -858,7 +684,8 @@ static bool setup_myself(void) {
        myself->options |= PROT_MINOR << 24;
 
 #ifdef DISABLE_LEGACY
-       experimental = read_ecdsa_private_key();
+       myself->connection->ecdsa = read_ecdsa_private_key(config_tree, NULL);
+       experimental = myself->connection->ecdsa != NULL;
 
        if(!experimental) {
                logger(DEBUG_ALWAYS, LOG_ERR, "No private key available, cannot start tinc!");
@@ -868,18 +695,25 @@ static bool setup_myself(void) {
 #else
 
        if(!get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental)) {
-               experimental = read_ecdsa_private_key();
+               myself->connection->ecdsa = read_ecdsa_private_key(config_tree, NULL);
+               experimental = myself->connection->ecdsa != NULL;
 
                if(!experimental) {
                        logger(DEBUG_ALWAYS, LOG_WARNING, "Support for SPTPS disabled.");
                }
        } else {
-               if(experimental && !read_ecdsa_private_key()) {
-                       return false;
+               if(experimental) {
+                       myself->connection->ecdsa = read_ecdsa_private_key(config_tree, NULL);
+
+                       if(!myself->connection->ecdsa) {
+                               return false;
+                       }
                }
        }
 
-       if(!read_rsa_private_key()) {
+       myself->connection->rsa = read_rsa_private_key(config_tree, NULL);
+
+       if(!myself->connection->rsa) {
                if(experimental) {
                        logger(DEBUG_ALWAYS, LOG_WARNING, "Support for legacy protocol disabled.");
                } else {
@@ -940,6 +774,8 @@ static bool setup_myself(void) {
                        logger(DEBUG_ALWAYS, LOG_ERR, "UDPRcvBuf cannot be negative!");
                        return false;
                }
+
+               udp_rcvbuf_warnings = true;
        }
 
        if(get_config_int(lookup_config(config_tree, "UDPSndBuf"), &udp_sndbuf)) {
@@ -947,8 +783,20 @@ static bool setup_myself(void) {
                        logger(DEBUG_ALWAYS, LOG_ERR, "UDPSndBuf cannot be negative!");
                        return false;
                }
+
+               udp_sndbuf_warnings = true;
        }
 
+       get_config_int(lookup_config(config_tree, "FWMark"), &fwmark);
+#ifndef SO_MARK
+
+       if(fwmark) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "FWMark not supported on this platform!");
+               return false;
+       }
+
+#endif
+
        int replaywin_int;
 
        if(get_config_int(lookup_config(config_tree, "ReplayWindow"), &replaywin_int)) {
@@ -964,6 +812,8 @@ static bool setup_myself(void) {
 #ifndef DISABLE_LEGACY
        /* Generate packet encryption key */
 
+       char *cipher;
+
        if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher)) {
                cipher = xstrdup("aes-256-cbc");
        }
@@ -972,6 +822,7 @@ static bool setup_myself(void) {
                myself->incipher = NULL;
        } else if(!(myself->incipher = cipher_open_by_name(cipher))) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized cipher type!");
+               free(cipher);
                return false;
        }
 
@@ -991,6 +842,8 @@ static bool setup_myself(void) {
                return false;
        }
 
+       char *digest;
+
        if(!get_config_string(lookup_config(config_tree, "Digest"), &digest)) {
                digest = xstrdup("sha256");
        }
@@ -999,6 +852,7 @@ static bool setup_myself(void) {
                myself->indigest = NULL;
        } else if(!(myself->indigest = digest_open_by_name(digest, maclength))) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized digest type!");
+               free(digest);
                return false;
        }
 
@@ -1006,17 +860,57 @@ static bool setup_myself(void) {
 #endif
 
        /* Compression */
-
        if(get_config_int(lookup_config(config_tree, "Compression"), &myself->incompression)) {
-               if(myself->incompression < 0 || myself->incompression > 11) {
+               switch(myself->incompression) {
+               case COMPRESS_LZ4:
+#ifdef HAVE_LZ4
+                       break;
+#else
                        logger(DEBUG_ALWAYS, LOG_ERR, "Bogus compression level!");
+                       logger(DEBUG_ALWAYS, LOG_ERR, "LZ4 compression is unavailable on this node.");
+                       return false;
+#endif
+
+               case COMPRESS_LZO_HI:
+               case COMPRESS_LZO_LO:
+#ifdef HAVE_LZO
+                       break;
+#else
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Bogus compression level!");
+                       logger(DEBUG_ALWAYS, LOG_ERR, "LZO compression is unavailable on this node.");
+                       return false;
+#endif
+
+               case COMPRESS_ZLIB_9:
+               case COMPRESS_ZLIB_8:
+               case COMPRESS_ZLIB_7:
+               case COMPRESS_ZLIB_6:
+               case COMPRESS_ZLIB_5:
+               case COMPRESS_ZLIB_4:
+               case COMPRESS_ZLIB_3:
+               case COMPRESS_ZLIB_2:
+               case COMPRESS_ZLIB_1:
+#ifdef HAVE_ZLIB
+                       break;
+#else
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Bogus compression level!");
+                       logger(DEBUG_ALWAYS, LOG_ERR, "ZLIB compression is unavailable on this node.");
+                       return false;
+#endif
+
+               case COMPRESS_NONE:
+                       break;
+
+               default:
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Bogus compression level!");
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Compression level %i is unrecognized by this node.", myself->incompression);
                        return false;
                }
        } else {
-               myself->incompression = 0;
+               myself->incompression = COMPRESS_NONE;
        }
 
-       myself->connection->outcompression = 0;
+       myself->connection->outcompression = COMPRESS_NONE;
 
        /* Done */
 
@@ -1042,10 +936,14 @@ static bool setup_myself(void) {
                        devops = raw_socket_devops;
                } else if(!strcasecmp(type, "multicast")) {
                        devops = multicast_devops;
-               } else if(!strcasecmp(type, "fd")) {
+               }
+
+#ifdef HAVE_SYS_UN_H
+               else if(!strcasecmp(type, "fd")) {
                        devops = fd_devops;
                }
 
+#endif
 #ifdef ENABLE_UML
                else if(!strcasecmp(type, "uml")) {
                        devops = uml_devops;
@@ -1251,7 +1149,7 @@ bool setup_network(void) {
   close all open network connections
 */
 void close_network_connections(void) {
-       for(list_node_t *node = connection_list->head, *next; node; node = next) {
+       for(list_node_t *node = connection_list.head, *next; node; node = next) {
                next = node->next;
                connection_t *c = node->data;
 
@@ -1264,13 +1162,11 @@ void close_network_connections(void) {
                terminate_connection(c, false);
        }
 
-       if(outgoing_list) {
-               list_delete_list(outgoing_list);
-       }
+       list_empty_list(&outgoing_list);
 
        if(myself && myself->connection) {
                subnet_update(myself, NULL, false);
-               connection_del(myself->connection);
+               free_connection(myself->connection);
        }
 
        for(int i = 0; i < listen_sockets; i++) {