X-Git-Url: https://www.tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Ftincctl.c;h=188db9222a18e14c5ef73ea1a8bc2cb6a18241b8;hb=a9caa2a6ea3aa553c9d2140ad4f5b34b7ab7297b;hp=057ff7c93f92d797b7e6b29faaaf6d360b68b62e;hpb=c52c46f8717aac6904f32766d774fa3fdf9611d8;p=tinc diff --git a/src/tincctl.c b/src/tincctl.c index 057ff7c9..188db922 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -31,6 +31,10 @@ #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; @@ -43,6 +47,7 @@ static bool show_version = false; static char *name = NULL; static char *identname = NULL; /* program name for syslog */ static char *pidfilename = NULL; /* pid file location */ +static char *confdir = NULL; static char controlcookie[1024]; char *netname = NULL; char *confbase = NULL; @@ -183,11 +188,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; } @@ -226,7 +235,7 @@ static FILE *ask_and_open(const char *filename, const char *what, const char *mo #endif /* The directory is a relative path or a filename. */ directory = get_current_dir_name(); - snprintf(buf2, sizeof buf2, "%s/%s", directory, filename); + snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename); filename = buf2; } @@ -261,7 +270,7 @@ static bool ecdsa_keygen() { } else fprintf(stderr, "Done.\n"); - xasprintf(&filename, "%s/ecdsa_key.priv", confbase); + xasprintf(&filename, "%s" SLASH "ecdsa_key.priv", confbase); f = ask_and_open(filename, "private ECDSA key", "a"); if(!f) @@ -281,9 +290,9 @@ static bool ecdsa_keygen() { free(filename); if(name) - xasprintf(&filename, "%s/hosts/%s", confbase, name); + xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name); else - xasprintf(&filename, "%s/ecdsa_key.pub", confbase); + xasprintf(&filename, "%s" SLASH "ecdsa_key.pub", confbase); f = ask_and_open(filename, "public ECDSA key", "a"); @@ -320,7 +329,7 @@ static bool rsa_keygen(int bits) { } else fprintf(stderr, "Done.\n"); - xasprintf(&filename, "%s/rsa_key.priv", confbase); + xasprintf(&filename, "%s" SLASH "rsa_key.priv", confbase); f = ask_and_open(filename, "private RSA key", "a"); if(!f) @@ -340,9 +349,9 @@ static bool rsa_keygen(int bits) { free(filename); if(name) - xasprintf(&filename, "%s/hosts/%s", confbase, name); + xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name); else - xasprintf(&filename, "%s/rsa_key.pub", confbase); + xasprintf(&filename, "%s" SLASH "rsa_key.pub", confbase); f = ask_and_open(filename, "public RSA key", "a"); @@ -380,38 +389,40 @@ static void make_names(void) { if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) { if(!confbase) { if(netname) - xasprintf(&confbase, "%s/%s", installdir, netname); + xasprintf(&confbase, "%s" SLASH "%s", installdir, netname); else xasprintf(&confbase, "%s", installdir); } } if(!pidfilename) - xasprintf(&pidfilename, "%s/pid", confbase); + xasprintf(&pidfilename, "%s" SLASH "pid", confbase); RegCloseKey(key); } if(!*installdir) { #endif + confdir = xstrdup(CONFDIR); if(!pidfilename) - xasprintf(&pidfilename, "%s/run/%s.pid", LOCALSTATEDIR, identname); + xasprintf(&pidfilename, "%s" SLASH "run" SLASH "%s.pid", LOCALSTATEDIR, identname); if(netname) { if(!confbase) - xasprintf(&confbase, CONFDIR "/tinc/%s", netname); + xasprintf(&confbase, CONFDIR SLASH "tinc" SLASH "%s", netname); else fprintf(stderr, "Both netname and configuration directory given, using the latter...\n"); } else { if(!confbase) - xasprintf(&confbase, CONFDIR "/tinc"); + xasprintf(&confbase, CONFDIR SLASH "tinc"); } #ifdef HAVE_MINGW - } + } else + confdir = xstrdup(installdir); #endif - xasprintf(&tinc_conf, "%s/tinc.conf", confbase); - xasprintf(&hosts_dir, "%s/hosts", confbase); + xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase); + xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase); } static char buffer[4096]; @@ -684,10 +695,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"; @@ -705,8 +714,16 @@ static int cmd_start(int argc, char *argv[]) { static int cmd_stop(int argc, char *argv[]) { #ifndef HAVE_MINGW - if(!connect_tincd()) + if(!connect_tincd()) { + if(pid) { + if(kill(pid, SIGTERM)) + return 1; + fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid); + return 0; + } + return 1; + } sendline(fd, "%d %d", CONTROL, REQ_STOP); if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_STOP || result) { @@ -721,7 +738,8 @@ static int cmd_stop(int argc, char *argv[]) { } static int cmd_restart(int argc, char *argv[]) { - return cmd_stop(argc, argv) ?: cmd_start(argc, argv); + cmd_stop(argc, argv); + return cmd_start(argc, argv); } static int cmd_reload(int argc, char *argv[]) { @@ -971,12 +989,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 +1109,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,10 +1157,19 @@ 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) - xasprintf(&filename, "%s/%s", hosts_dir, node); + xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node); else filename = tinc_conf; @@ -1095,7 +1206,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 +1250,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... @@ -1241,7 +1360,7 @@ static int cmd_init(int argc, char *argv[]) { return 1; } - if(mkdir(CONFDIR, 0755) && errno != EEXIST) { + if(mkdir(confdir, 0755) && errno != EEXIST) { fprintf(stderr, "Could not create directory %s: %s\n", CONFDIR, strerror(errno)); return 1; } @@ -1251,8 +1370,6 @@ static int cmd_init(int argc, char *argv[]) { return 1; } - char *hosts_dir = NULL; - xasprintf(&hosts_dir, "%s/hosts", confbase); if(mkdir(hosts_dir, 0755) && errno != EEXIST) { fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno)); return 1; @@ -1269,9 +1386,24 @@ static int cmd_init(int argc, char *argv[]) { fclose(stdin); if(!rsa_keygen(2048) || !ecdsa_keygen()) - return false; + return 1; - return true; +#ifndef HAVE_MINGW + char *filename; + xasprintf(&filename, "%s" SLASH "tinc-up", confbase); + if(access(filename, F_OK)) { + FILE *f = fopen(filename, "w"); + if(!f) { + fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno)); + return 1; + } + fchmod(fileno(f), 0755); + fprintf(f, "#!/bin/sh\n\necho 'Unconfigured tinc-up script, please edit!'\n\n#ifconfig $INTERFACE netmask \n"); + fclose(f); + } +#endif + + return 0; } @@ -1328,10 +1460,10 @@ static int cmd_edit(int argc, char *argv[]) { char *filename = NULL; - if(strncmp(argv[1], "hosts/", 6)) { + if(strncmp(argv[1], "hosts" SLASH, 6)) { for(int i = 0; conffiles[i]; i++) { if(!strcmp(argv[1], conffiles[i])) { - xasprintf(&filename, "%s/%s", confbase, argv[1]); + xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]); break; } } @@ -1340,7 +1472,7 @@ static int cmd_edit(int argc, char *argv[]) { } if(!filename) { - xasprintf(&filename, "%s/%s", hosts_dir, argv[1]); + xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]); char *dash = strchr(argv[1], '-'); if(dash) { *dash++ = 0; @@ -1351,14 +1483,12 @@ static int cmd_edit(int argc, char *argv[]) { } } + char *command; #ifndef HAVE_MINGW - char *editor = getenv("VISUAL") ?: getenv("EDITOR") ?: "vi"; + xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename); #else - char *editor = "edit" + xasprintf(&command, "edit \"%s\"", filename); #endif - - char *command; - xasprintf(&command, "\"%s\" \"%s\"", editor, filename); int result = system(command); if(result) return result; @@ -1374,7 +1504,7 @@ static int cmd_edit(int argc, char *argv[]) { static int export(const char *name, FILE *out) { char *filename; - xasprintf(&filename, "%s/%s", hosts_dir, name); + xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name); FILE *in = fopen(filename, "r"); if(!in) { fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno)); @@ -1383,8 +1513,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)); @@ -1451,7 +1583,7 @@ static int cmd_import(int argc, char *argv[]) { fclose(out); free(filename); - xasprintf(&filename, "%s/%s", hosts_dir, name); + xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name); if(!force && !access(filename, F_OK)) { fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);