Choose a different Port when 655 isn't available when doing "tinc init".
[tinc] / src / tincctl.c
index a0061dc..16fa4b1 100644 (file)
@@ -40,7 +40,6 @@
 #include "top.h"
 
 #ifdef HAVE_MINGW
-#define mkdir(a, b) mkdir(a)
 #define SCRIPTEXTENSION ".bat"
 #else
 #define SCRIPTEXTENSION ""
@@ -117,7 +116,7 @@ static void usage(bool status) {
                                "  del VARIABLE [VALUE]       Remove VARIABLE [only ones with watching VALUE]\n"
                                "  start [tincd options]      Start tincd.\n"
                                "  stop                       Stop tincd.\n"
-                               "  restart                    Restart tincd.\n"
+                               "  restart [tincd options]    Restart tincd.\n"
                                "  reload                     Partially reload configuration of running tincd.\n"
                                "  pid                        Show PID of currently running tincd.\n"
                                "  generate-keys [bits]       Generate new RSA and ECDSA public/private keypairs.\n"
@@ -228,6 +227,16 @@ static void disable_old_keys(const char *filename, const char *what) {
 
        w = fopen(tmpfile, "w");
 
+#ifdef HAVE_FCHMOD
+       /* Let the temporary file have the same permissions as the original. */
+
+       if(w) {
+               struct stat st = {.st_mode = 0600};
+               fstat(fileno(r), &st);
+               fchmod(fileno(w), st.st_mode);
+       }
+#endif
+
        while(fgets(buf, sizeof buf, r)) {
                if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
                        if((strstr(buf, " EC ") && strstr(what, "ECDSA")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
@@ -325,8 +334,6 @@ static FILE *ask_and_open(const char *filename, const char *what, const char *mo
                filename = buf2;
        }
 
-       umask(0077); /* Disallow everything for group and other */
-
        disable_old_keys(filename, what);
 
        /* Open it first to keep the inode busy */
@@ -469,7 +476,8 @@ bool recvline(int fd, char *line, size_t len) {
        char *newline = NULL;
 
        if(!fd)
-abort();
+               abort();
+
        while(!(newline = memchr(buffer, '\n', blen))) {
                int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
                if(result == -1 && errno == EINTR)
@@ -822,8 +830,16 @@ static int cmd_start(int argc, char *argv[]) {
 
        free(nargv);
 
-       int status = -1;
-       if(waitpid(pid, &status, 0) != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
+       int status = -1, result;
+#ifdef SIGINT
+       signal(SIGINT, SIG_IGN);
+#endif
+       result = waitpid(pid, &status, 0);
+#ifdef SIGINT
+       signal(SIGINT, SIG_DFL);
+#endif
+
+       if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
                fprintf(stderr, "Error starting %s\n", c);
                return 1;
        }
@@ -871,7 +887,7 @@ static int cmd_stop(int argc, char *argv[]) {
 }
 
 static int cmd_restart(int argc, char *argv[]) {
-       cmd_stop(argc, argv);
+       cmd_stop(1, argv);
        return cmd_start(argc, argv);
 }
 
@@ -1187,6 +1203,13 @@ static int cmd_pcap(int argc, char *argv[]) {
        return 0;
 }
 
+#ifdef SIGINT
+static void sigint_handler(int sig) {
+       fprintf(stderr, "\n");
+       shutdown(fd, SHUT_RDWR);
+}
+#endif
+
 static int cmd_log(int argc, char *argv[]) {
        if(argc > 2) {
                fprintf(stderr, "Too many arguments!\n");
@@ -1196,7 +1219,18 @@ static int cmd_log(int argc, char *argv[]) {
        if(!connect_tincd(true))
                return 1;
 
+#ifdef SIGINT
+       signal(SIGINT, sigint_handler);
+#endif
+
        logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
+
+#ifdef SIGINT
+       signal(SIGINT, SIG_DFL);
+#endif
+
+       close(fd);
+       fd = -1;
        return 0;
 }
 
@@ -1277,6 +1311,7 @@ const var_t variables[] = {
        {"KeyExpire", VAR_SERVER},
        {"LocalDiscovery", VAR_SERVER},
        {"MACExpire", VAR_SERVER},
+       {"MaxConnectionBurst", VAR_SERVER},
        {"MaxOutputBufferSize", VAR_SERVER},
        {"MaxTimeout", VAR_SERVER},
        {"Mode", VAR_SERVER | VAR_SAFE},
@@ -1387,6 +1422,7 @@ static int cmd_config(int argc, char *argv[]) {
 
        /* Some simple checks. */
        bool found = false;
+       bool warnonremove = false;
 
        for(int i = 0; variables[i].name; i++) {
                if(strcasecmp(variables[i].name, variable))
@@ -1425,6 +1461,16 @@ static int cmd_config(int argc, char *argv[]) {
                                return 1;
                }
 
+               /* Change "add" into "set" for variables that do not allow multiple occurences.
+                  Turn on warnings when it seems variables might be removed unintentionally. */
+
+               if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
+                       warnonremove = true;
+                       action = 0;
+               } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
+                       warnonremove = true;
+               }
+
                break;
        }
 
@@ -1507,9 +1553,14 @@ static int cmd_config(int argc, char *argv[]) {
                                }
                        // Set
                        } else if(action == 0) {
+                               // Warn if "set" was used for variables that can occur multiple times
+                               if(warnonremove && strcasecmp(bvalue, value))
+                                       fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
+
                                // Already set? Delete the rest...
                                if(set)
                                        continue;
+
                                // Otherwise, replace.
                                if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
                                        fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
@@ -1606,6 +1657,35 @@ bool check_id(const char *name) {
        return true;
 }
 
+static bool try_bind(int port) {
+       struct addrinfo *ai = NULL;
+       struct addrinfo hint = {
+               .ai_flags = AI_PASSIVE,
+               .ai_family = AF_UNSPEC,
+               .ai_socktype = SOCK_STREAM,
+               .ai_protocol = IPPROTO_TCP,
+       };
+
+       char portstr[16];
+       snprintf(portstr, sizeof portstr, "%d", port);
+
+       if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
+               return false;
+
+       while(ai) {
+               int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
+               if(!fd)
+                       return false;
+               int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
+               closesocket(fd);
+               if(result)
+                       return false;
+               ai = ai->ai_next;
+       }
+
+       return true;
+}
+
 static int cmd_init(int argc, char *argv[]) {
        if(!access(tinc_conf, F_OK)) {
                fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
@@ -1674,6 +1754,35 @@ static int cmd_init(int argc, char *argv[]) {
        if(!rsa_keygen(2048, false) || !ecdsa_keygen(false))
                return 1;
 
+
+       if(!try_bind(655)) {
+               srand(time(NULL));
+               int port = 0;
+               for(int i = 0; i < 100; i++) {
+                       port = 0x1000 + (rand() & 0x7fff);
+                       if(try_bind(port))
+                               break;
+                       port = 0;
+               }
+               if(port) {
+                       char *filename;
+                       xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
+                       FILE *f = fopen(filename, "a");
+                       free(filename);
+                       if(!f) {
+                               port = 0;
+                       } else {
+                               fprintf(f, "Port = %d\n", port);
+                               fclose(f);
+                       }
+               }
+
+               if(!port)
+                       fprintf(stderr, "Warning: could not bind to port 655. Please change tinc's Port manually.\n");
+               else
+                       fprintf(stderr, "Warning: could not bind to port 655. Tinc will instead listen on port %d.\n", port);
+       }
+
 #ifndef HAVE_MINGW
        char *filename;
        xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
@@ -1683,7 +1792,9 @@ static int cmd_init(int argc, char *argv[]) {
                        fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
                        return 1;
                }
-               fchmod(fileno(f), 0755);
+               mode_t mask = umask(0);
+               umask(mask);
+               fchmod(fileno(f), 0755 & ~mask);
                fprintf(f, "#!/bin/sh\n\necho 'Unconfigured tinc-up script, please edit!'\n\n#ifconfig $INTERFACE <your vpn IP address> netmask <netmask of whole VPN>\n");
                fclose(f);
        }