Fix crash when no netname is specified.
[tinc] / src / tincctl.c
index 057ff7c..ca354bc 100644 (file)
 #include "tincctl.h"
 #include "top.h"
 
+#ifdef HAVE_MINGW
+#define mkdir(a, b) mkdir(a)
+#endif
+
 /* The name this program was run with. */
 static char *program_name = NULL;
 
@@ -183,11 +187,15 @@ static bool parse_options(int argc, char **argv) {
                }
        }
 
-       if(!netname) {
-               netname = getenv("NETNAME");
-               if(netname)
-                       netname = xstrdup(netname);
-       }
+        if(!netname && (netname = getenv("NETNAME")))
+                netname = xstrdup(netname);
+
+        /* netname "." is special: a "top-level name" */
+
+        if(netname && !strcmp(netname, ".")) {
+                free(netname);
+                netname = NULL;
+        }
 
        return true;
 }
@@ -684,10 +692,8 @@ static int cmd_start(int argc, char *argv[]) {
                slash = c;
 #endif
 
-       if (slash++) {
-               c = xmalloc((slash - argv[0]) + sizeof("tincd"));
-               sprintf(c, "%.*stincd", (int)(slash - argv[0]), argv[0]);
-       }
+       if (slash++)
+               xasprintf(&c, "%.*stincd", (int)(slash - argv[0]), argv[0]);
        else
                c = "tincd";
 
@@ -971,12 +977,65 @@ static char *get_my_name() {
        return NULL;
 }
 
-static char *hostvariables[] = {
-       "Address",
-       "Port",
-       "PublicKey",
-       "Subnet",
-       NULL,
+#define VAR_SERVER 1    /* Should be in tinc.conf */
+#define VAR_HOST 2      /* Can be in host config file */
+#define VAR_MULTIPLE 4  /* Multiple statements allowed */
+#define VAR_OBSOLETE 8  /* Should not be used anymore */
+
+static struct {
+       const char *name;
+       int type;
+} const variables[] = {
+       /* Server configuration */
+       {"AddressFamily", VAR_SERVER},
+       {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
+       {"BindToInterface", VAR_SERVER},
+       {"Broadcast", VAR_SERVER},
+       {"ConnectTo", VAR_SERVER | VAR_MULTIPLE},
+       {"DecrementTTL", VAR_SERVER},
+       {"Device", VAR_SERVER},
+       {"DeviceType", VAR_SERVER},
+       {"DirectOnly", VAR_SERVER},
+       {"ECDSAPrivateKeyFile", VAR_SERVER},
+       {"ExperimentalProtocol", VAR_SERVER},
+       {"Forwarding", VAR_SERVER},
+       {"GraphDumpFile", VAR_SERVER},
+       {"Hostnames", VAR_SERVER},
+       {"IffOneQueue", VAR_SERVER},
+       {"Interface", VAR_SERVER},
+       {"KeyExpire", VAR_SERVER},
+       {"LocalDiscovery", VAR_SERVER},
+       {"MACExpire", VAR_SERVER},
+       {"MaxTimeout", VAR_SERVER},
+       {"Mode", VAR_SERVER},
+       {"Name", VAR_SERVER},
+       {"PingInterval", VAR_SERVER},
+       {"PingTimeout", VAR_SERVER},
+       {"PriorityInheritance", VAR_SERVER},
+       {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
+       {"PrivateKeyFile", VAR_SERVER},
+       {"ProcessPriority", VAR_SERVER},
+       {"ReplayWindow", VAR_SERVER},
+       {"StrictSubnets", VAR_SERVER},
+       {"TunnelServer", VAR_SERVER},
+       {"UDPRcvBuf", VAR_SERVER},
+       {"UDPSndBuf", VAR_SERVER},
+       /* Host configuration */
+       {"Address", VAR_HOST | VAR_MULTIPLE},
+       {"Cipher", VAR_SERVER | VAR_HOST},
+       {"ClampMSS", VAR_SERVER | VAR_HOST},
+       {"Compression", VAR_SERVER | VAR_HOST},
+       {"Digest", VAR_SERVER | VAR_HOST},
+       {"IndirectData", VAR_SERVER | VAR_HOST},
+       {"MACLength", VAR_SERVER | VAR_HOST},
+       {"PMTU", VAR_SERVER | VAR_HOST},
+       {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
+       {"Port", VAR_HOST},
+       {"PublicKey", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
+       {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
+       {"Subnet", VAR_HOST | VAR_MULTIPLE},
+       {"TCPOnly", VAR_SERVER | VAR_HOST},
+       {NULL, 0}
 };
 
 static int cmd_config(int argc, char *argv[]) {
@@ -1038,16 +1097,47 @@ static int cmd_config(int argc, char *argv[]) {
                return 1;
        }
 
-       // Should this go into our own host config file?
-       if(!node) {
-               for(int i = 0; hostvariables[i]; i++) {
-                       if(!strcasecmp(hostvariables[i], variable)) {
-                               node = get_my_name();
-                               if(!node)
-                                       return 1;
-                               break;
+       /* Some simple checks. */
+       bool found = false;
+
+       for(int i = 0; variables[i].name; i++) {
+               if(strcasecmp(variables[i].name, variable))
+                       continue;
+
+               found = true;
+               variable = (char *)variables[i].name;
+
+               /* Discourage use of obsolete variables. */
+
+               if(variables[i].type & VAR_OBSOLETE && action >= 0) {
+                       if(force) {
+                               fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
+                       } else {
+                               fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
+                               return 1;
+                       }
+               }
+
+               /* Don't put server variables in host config files */
+
+               if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
+                       if(force) {
+                               fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
+                       } else {
+                               fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
+                               return 1;
                        }
                }
+
+               /* Should this go into our own host config file? */
+
+               if(!node && !(variables[i].type & VAR_SERVER)) {
+                       node = get_my_name();
+                       if(!node)
+                               return 1;
+               }
+
+               break;
        }
 
        if(node && !check_id(node)) {
@@ -1055,6 +1145,15 @@ static int cmd_config(int argc, char *argv[]) {
                return 1;
        }
 
+       if(!found && action >= 0) {
+               if(force) {
+                       fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
+               } else {
+                       fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
+                       return 1;
+               }
+       }
+
        // Open the right configuration file.
        char *filename;
        if(node)
@@ -1095,7 +1194,7 @@ static int cmd_config(int argc, char *argv[]) {
 
        while(fgets(buf1, sizeof buf1, f)) {
                buf1[sizeof buf1 - 1] = 0;
-               strcpy(buf2, buf1);
+               strncpy(buf2, buf1, sizeof buf2);
 
                // Parse line in a simple way
                char *bvalue;
@@ -1139,6 +1238,14 @@ static int cmd_config(int argc, char *argv[]) {
                        fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
                        return 1;
                }
+
+               // Add newline if it is missing...
+               if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
+                       if(fputc('\n', tf) < 0) {
+                               fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
+                               return 1;
+                       }
+               }
        }
 
        // Make sure we read everything...
@@ -1354,7 +1461,7 @@ static int cmd_edit(int argc, char *argv[]) {
 #ifndef HAVE_MINGW
        char *editor = getenv("VISUAL") ?: getenv("EDITOR") ?: "vi";
 #else
-       char *editor = "edit"
+       char *editor = "edit";
 #endif
 
        char *command;
@@ -1383,8 +1490,10 @@ static int export(const char *name, FILE *out) {
 
        fprintf(out, "Name = %s\n", name);
        char buf[4096];
-       while(fgets(buf, sizeof buf, in))
-               fputs(buf, out);
+       while(fgets(buf, sizeof buf, in)) {
+               if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
+                       fputs(buf, out);
+       }
 
        if(ferror(in)) {
                fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));