X-Git-Url: https://www.tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Ftincctl.c;h=7b44881f3cf842c1a8295e6f2430c59741c2820d;hb=cc3c69c892b0dad9a6ece0a0f4ccd429a22fcbff;hp=0d773e423916c16b870923d6b9300a062aa44c72;hpb=6dfdb323612184529b4b83c1be914dda8262de47;p=tinc diff --git a/src/tincctl.c b/src/tincctl.c index 0d773e42..7b44881f 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -1,6 +1,6 @@ /* tincctl.c -- Controlling a running tincd - Copyright (C) 2007-2012 Guus Sliepen + Copyright (C) 2007-2013 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,6 +31,7 @@ #include "control_common.h" #include "ecdsagen.h" #include "info.h" +#include "names.h" #include "rsagen.h" #include "utils.h" #include "tincctl.h" @@ -40,10 +41,6 @@ #define mkdir(a, b) mkdir(a) #endif - -/* The name this program was run with. */ -static char *program_name = NULL; - static char **orig_argv; static int orig_argc; @@ -54,14 +51,10 @@ static bool show_help = false; 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; +static char controlcookie[1025]; static char *tinc_conf = NULL; static char *hosts_dir = NULL; +struct timeval now; // Horrible global variables... static int pid = 0; @@ -106,10 +99,9 @@ static void version(void) { } static void usage(bool status) { - if(status) - fprintf(stderr, "Try `%s --help\' for more information.\n", - program_name); - else { + if(status) { + fprintf(stderr, "Try `%s --help\' for more information.\n", program_name); + } else { printf("Usage: %s [options] command\n\n", program_name); printf("Valid options are:\n" " -c, --config=DIR Read configuration options from DIR.\n" @@ -134,7 +126,7 @@ static void usage(bool status) { " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n" " generate-ecdsa-keys Generate a new ECDSA public/private keypair.\n" " dump Dump a list of one of the following things:\n" - " nodes - all known nodes in the VPN\n" + " [reachable] nodes - all known nodes in the VPN\n" " edges - all known connections in the VPN\n" " subnets - all known subnets in the VPN\n" " connections - all meta connections with ourself\n" @@ -152,6 +144,8 @@ static void usage(bool status) { " export Export host configuration of local node to standard output\n" " export-all Export all host configuration files to standard output\n" " import [--force] Import host configuration file(s) from standard input\n" + " exchange [--force] Same as export followed by import\n" + " exchange-all [--force] Same as export-all followed by import\n" "\n"); printf("Report bugs to tinc@tinc-vpn.org.\n"); } @@ -163,34 +157,34 @@ static bool parse_options(int argc, char **argv) { while((r = getopt_long(argc, argv, "c:n:Dd::Lo:RU:", long_options, &option_index)) != EOF) { switch (r) { - case 0: /* long option */ + case 0: /* long option */ break; - case 'c': /* config file */ + case 'c': /* config file */ confbase = xstrdup(optarg); break; - case 'n': /* net name given */ + case 'n': /* net name given */ netname = xstrdup(optarg); break; - case 1: /* show help */ + case 1: /* show help */ show_help = true; break; - case 2: /* show version */ + case 2: /* show version */ show_version = true; break; - case 5: /* open control socket here */ + case 5: /* open control socket here */ pidfilename = xstrdup(optarg); break; - case 6: + case 6: /* force */ force = true; break; - case '?': + case '?': /* wrong options */ usage(true); return false; @@ -199,15 +193,15 @@ static bool parse_options(int argc, char **argv) { } } - if(!netname && (netname = getenv("NETNAME"))) - netname = xstrdup(netname); + if(!netname && (netname = getenv("NETNAME"))) + netname = xstrdup(netname); - /* netname "." is special: a "top-level name" */ + /* netname "." is special: a "top-level name" */ - if(netname && (!*netname || !strcmp(netname, "."))) { - free(netname); - netname = NULL; - } + if(netname && (!*netname || !strcmp(netname, "."))) { + free(netname); + netname = NULL; + } if(netname && (strpbrk(netname, "\\/") || *netname == '.')) { fprintf(stderr, "Invalid character in netname!\n"); @@ -332,7 +326,7 @@ static FILE *ask_and_open(const char *filename, const char *what, const char *mo filename = buf2; } - umask(0077); /* Disallow everything for group and other */ + umask(0077); /* Disallow everything for group and other */ disable_old_keys(filename, what); @@ -355,7 +349,7 @@ static FILE *ask_and_open(const char *filename, const char *what, const char *mo static bool ecdsa_keygen(bool ask) { ecdsa_t key; FILE *f; - char *filename; + char *pubname, *privname; fprintf(stderr, "Generating ECDSA keypair:\n"); @@ -365,28 +359,29 @@ static bool ecdsa_keygen(bool ask) { } else fprintf(stderr, "Done.\n"); - xasprintf(&filename, "%s" SLASH "ecdsa_key.priv", confbase); - f = ask_and_open(filename, "private ECDSA key", "a", ask); + xasprintf(&privname, "%s" SLASH "ecdsa_key.priv", confbase); + f = ask_and_open(privname, "private ECDSA key", "a", ask); + free(privname); if(!f) return false; - + #ifdef HAVE_FCHMOD /* Make it unreadable for others. */ fchmod(fileno(f), 0600); #endif - + ecdsa_write_pem_private_key(&key, f); fclose(f); - free(filename); if(name) - xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name); + xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name); else - xasprintf(&filename, "%s" SLASH "ecdsa_key.pub", confbase); + xasprintf(&pubname, "%s" SLASH "ecdsa_key.pub", confbase); - f = ask_and_open(filename, "public ECDSA key", "a", ask); + f = ask_and_open(pubname, "public ECDSA key", "a", ask); + free(pubname); if(!f) return false; @@ -396,7 +391,6 @@ static bool ecdsa_keygen(bool ask) { free(pubkey); fclose(f); - free(filename); return true; } @@ -408,7 +402,7 @@ static bool ecdsa_keygen(bool ask) { static bool rsa_keygen(int bits, bool ask) { rsa_t key; FILE *f; - char *filename; + char *pubname, *privname; fprintf(stderr, "Generating %d bits keys:\n", bits); @@ -418,28 +412,29 @@ static bool rsa_keygen(int bits, bool ask) { } else fprintf(stderr, "Done.\n"); - xasprintf(&filename, "%s" SLASH "rsa_key.priv", confbase); - f = ask_and_open(filename, "private RSA key", "a", ask); + xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase); + f = ask_and_open(privname, "private RSA key", "a", ask); + free(privname); if(!f) return false; - + #ifdef HAVE_FCHMOD /* Make it unreadable for others. */ fchmod(fileno(f), 0600); #endif - + rsa_write_pem_private_key(&key, f); fclose(f); - free(filename); if(name) - xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name); + xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name); else - xasprintf(&filename, "%s" SLASH "rsa_key.pub", confbase); + xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase); - f = ask_and_open(filename, "public RSA key", "a", ask); + f = ask_and_open(pubname, "public RSA key", "a", ask); + free(pubname); if(!f) return false; @@ -447,67 +442,10 @@ static bool rsa_keygen(int bits, bool ask) { rsa_write_pem_public_key(&key, f); fclose(f); - free(filename); return true; } -/* - Set all files and paths according to netname -*/ -static void make_names(void) { -#ifdef HAVE_MINGW - HKEY key; - char installdir[1024] = ""; - long len = sizeof installdir; -#endif - - if(netname) - xasprintf(&identname, "tinc.%s", netname); - else - identname = xstrdup("tinc"); - -#ifdef HAVE_MINGW - if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) { - if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) { - if(!confbase) { - if(netname) - xasprintf(&confbase, "%s" SLASH "%s", installdir, netname); - else - xasprintf(&confbase, "%s", installdir); - } - } - if(!pidfilename) - xasprintf(&pidfilename, "%s" SLASH "pid", confbase); - RegCloseKey(key); - } - - if(!*installdir) { -#endif - confdir = xstrdup(CONFDIR); - - if(!pidfilename) - xasprintf(&pidfilename, "%s" SLASH "run" SLASH "%s.pid", LOCALSTATEDIR, identname); - - if(netname) { - if(!confbase) - 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 SLASH "tinc"); - } - -#ifdef HAVE_MINGW - } else - confdir = xstrdup(installdir); -#endif - - xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase); - xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase); -} - static char buffer[4096]; static size_t blen = 0; @@ -579,7 +517,7 @@ bool sendline(int fd, char *format, ...) { blen -= result; } - return true; + return true; } static void pcap(int fd, FILE *out, int snaplen) { @@ -708,8 +646,8 @@ static bool connect_tincd(bool verbose) { return false; } - char host[128]; - char port[128]; + char host[129]; + char port[129]; if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) { if(verbose) @@ -728,6 +666,26 @@ static bool connect_tincd(bool verbose) { } #endif +#ifndef HAVE_MINGW + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if(fd < 0) { + if(verbose) + fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno)); + return false; + } + + if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) { + if(verbose) + fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno)); + close(fd); + fd = -1; + return false; + } +#else struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, @@ -768,6 +726,7 @@ static bool connect_tincd(bool verbose) { } freeaddrinfo(res); +#endif char data[4096]; int version; @@ -781,7 +740,7 @@ static bool connect_tincd(bool verbose) { } sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT); - + if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) { if(verbose) fprintf(stderr, "Could not fully establish control socket connection\n"); @@ -817,9 +776,10 @@ static int cmd_start(int argc, char *argv[]) { c = "tincd"; int nargc = 0; - char **nargv = xmalloc_and_zero((orig_argc + argc) * sizeof *nargv); + char **nargv = xmalloc_and_zero((optind + argc) * sizeof *nargv); - for(int i = 0; i < orig_argc; i++) + nargv[nargc++] = c; + for(int i = 1; i < optind; i++) nargv[nargc++] = orig_argv[i]; for(int i = 1; i < argc; i++) nargv[nargc++] = argv[i]; @@ -832,12 +792,15 @@ static int cmd_start(int argc, char *argv[]) { pid_t pid = fork(); if(pid == -1) { fprintf(stderr, "Could not fork: %s\n", strerror(errno)); + free(nargv); return 1; } if(!pid) exit(execvp(c, nargv)); - + + free(nargv); + int status = -1; if(waitpid(pid, &status, 0) != pid || !WIFEXITED(status) || WEXITSTATUS(status)) { fprintf(stderr, "Error starting %s\n", c); @@ -849,12 +812,21 @@ static int cmd_start(int argc, char *argv[]) { } static int cmd_stop(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + #ifndef HAVE_MINGW if(!connect_tincd(true)) { if(pid) { - if(kill(pid, SIGTERM)) + if(kill(pid, SIGTERM)) { + fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno)); return 1; + } + fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid); + waitpid(pid, NULL, 0); return 0; } @@ -866,6 +838,12 @@ static int cmd_stop(int argc, char *argv[]) { fprintf(stderr, "Could not stop tinc daemon.\n"); return 1; } + + // Wait for tincd to close the connection... + fd_set r; + FD_ZERO(&r); + FD_SET(fd, &r); + select(fd + 1, &r, NULL, NULL, NULL); #else if(!remove_service()) return 1; @@ -883,6 +861,11 @@ static int cmd_restart(int argc, char *argv[]) { } static int cmd_reload(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + if(!connect_tincd(true)) return 1; @@ -897,6 +880,19 @@ static int cmd_reload(int argc, char *argv[]) { } static int cmd_dump(int argc, char *argv[]) { + bool only_reachable = false; + + if(argc > 2 && !strcasecmp(argv[1], "reachable")) { + if(strcasecmp(argv[2], "nodes")) { + fprintf(stderr, "`reachable' only supported for nodes.\n"); + usage(true); + return 1; + } + only_reachable = true; + argv++; + argc--; + } + if(argc != 2) { fprintf(stderr, "Invalid number of arguments.\n"); usage(true); @@ -950,71 +946,90 @@ static int cmd_dump(int argc, char *argv[]) { if(n < 2) break; - if(!do_graph) { - char node[4096]; - char from[4096]; - char to[4096]; - char subnet[4096]; - char host[4096]; - char port[4096]; - char via[4096]; - char nexthop[4096]; - int cipher, digest, maclength, compression, distance, socket, weight; - short int pmtu, minmtu, maxmtu; - unsigned int options, status; - long int last_state_change; - - switch(req) { - case REQ_DUMP_NODES: { - int n = sscanf(line, "%*d %*d %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, host, port, &cipher, &digest, &maclength, &compression, &options, &status, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change); - if(n != 16) { - fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line); - return 1; - } + char node[4096]; + char from[4096]; + char to[4096]; + char subnet[4096]; + char host[4096]; + char port[4096]; + char via[4096]; + char nexthop[4096]; + int cipher, digest, maclength, compression, distance, socket, weight; + short int pmtu, minmtu, maxmtu; + unsigned int options, status_int; + node_status_t status; + long int last_state_change; + + switch(req) { + case REQ_DUMP_NODES: { + int n = sscanf(line, "%*d %*d %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change); + if(n != 16) { + fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line); + return 1; + } + + memcpy(&status, &status_int, sizeof status); + + if(do_graph) { + const char *color = "black"; + if(!strcmp(host, "MYSELF")) + color = "green"; + else if(!status.reachable) + color = "red"; + else if(strcmp(via, node)) + color = "orange"; + else if(!status.validkey) + color = "black"; + else if(minmtu > 0) + color = "green"; + printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\""); + } else { + if(only_reachable && !status.reachable) + continue; printf("%s at %s port %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s distance %d pmtu %hd (min %hd max %hd)\n", - node, host, port, cipher, digest, maclength, compression, options, status, nexthop, via, distance, pmtu, minmtu, maxmtu); - } break; - - case REQ_DUMP_EDGES: { - int n = sscanf(line, "%*d %*d %s %s %s port %s %x %d", from, to, host, port, &options, &weight); - if(n != 6) { - fprintf(stderr, "Unable to parse edge dump from tincd.\n"); - return 1; - } + node, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu); + } + } break; + + case REQ_DUMP_EDGES: { + int n = sscanf(line, "%*d %*d %s %s %s port %s %x %d", from, to, host, port, &options, &weight); + if(n != 6) { + fprintf(stderr, "Unable to parse edge dump from tincd.\n"); + return 1; + } + + if(do_graph) { + float w = 1 + 65536.0 / weight; + if(do_graph == 1 && strcmp(node1, node2) > 0) + printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w); + else if(do_graph == 2) + printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w); + } else { printf("%s to %s at %s port %s options %x weight %d\n", from, to, host, port, options, weight); - } break; - - case REQ_DUMP_SUBNETS: { - int n = sscanf(line, "%*d %*d %s %s", subnet, node); - if(n != 2) { - fprintf(stderr, "Unable to parse subnet dump from tincd.\n"); - return 1; - } - printf("%s owner %s\n", strip_weight(subnet), node); - } break; - - case REQ_DUMP_CONNECTIONS: { - int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status); - if(n != 6) { - fprintf(stderr, "Unable to parse connection dump from tincd.\n"); - return 1; - } - printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status); - } break; - - default: - fprintf(stderr, "Unable to parse dump from tincd.\n"); + } + } break; + + case REQ_DUMP_SUBNETS: { + int n = sscanf(line, "%*d %*d %s %s", subnet, node); + if(n != 2) { + fprintf(stderr, "Unable to parse subnet dump from tincd.\n"); return 1; - } - } else { - if(req == REQ_DUMP_NODES) - printf(" %s [label = \"%s\"];\n", node1, node1); - else { - if(do_graph == 1 && strcmp(node1, node2) > 0) - printf(" %s -- %s;\n", node1, node2); - else if(do_graph == 2) - printf(" %s -> %s;\n", node1, node2); - } + } + printf("%s owner %s\n", strip_weight(subnet), node); + } break; + + case REQ_DUMP_CONNECTIONS: { + int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int); + if(n != 6) { + fprintf(stderr, "Unable to parse connection dump from tincd.\n"); + return 1; + } + printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int); + } break; + + default: + fprintf(stderr, "Unable to parse dump from tincd.\n"); + return 1; } } @@ -1023,6 +1038,11 @@ static int cmd_dump(int argc, char *argv[]) { } static int cmd_purge(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + if(!connect_tincd(true)) return 1; @@ -1058,6 +1078,11 @@ static int cmd_debug(int argc, char *argv[]) { } static int cmd_retry(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + if(!connect_tincd(true)) return 1; @@ -1117,6 +1142,11 @@ static int cmd_disconnect(int argc, char *argv[]) { } static int cmd_top(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + #ifdef HAVE_CURSES if(!connect_tincd(true)) return 1; @@ -1130,6 +1160,11 @@ static int cmd_top(int argc, char *argv[]) { } static int cmd_pcap(int argc, char *argv[]) { + if(argc > 2) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + if(!connect_tincd(true)) return 1; @@ -1138,6 +1173,11 @@ static int cmd_pcap(int argc, char *argv[]) { } static int cmd_log(int argc, char *argv[]) { + if(argc > 2) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + if(!connect_tincd(true)) return 1; @@ -1146,6 +1186,11 @@ static int cmd_log(int argc, char *argv[]) { } static int cmd_pid(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + if(!connect_tincd(true) && !pid) return 1; @@ -1204,6 +1249,7 @@ static struct { } const variables[] = { /* Server configuration */ {"AddressFamily", VAR_SERVER}, + {"AutoConnect", VAR_SERVER}, {"BindToAddress", VAR_SERVER | VAR_MULTIPLE}, {"BindToInterface", VAR_SERVER}, {"Broadcast", VAR_SERVER}, @@ -1215,7 +1261,7 @@ static struct { {"ECDSAPrivateKeyFile", VAR_SERVER}, {"ExperimentalProtocol", VAR_SERVER}, {"Forwarding", VAR_SERVER}, - {"GraphDumpFile", VAR_SERVER}, + {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE}, {"Hostnames", VAR_SERVER}, {"IffOneQueue", VAR_SERVER}, {"Interface", VAR_SERVER}, @@ -1234,6 +1280,8 @@ static struct { {"ProcessPriority", VAR_SERVER}, {"Proxy", VAR_SERVER}, {"ReplayWindow", VAR_SERVER}, + {"ScriptsExtension", VAR_SERVER}, + {"ScriptsInterpreter", VAR_SERVER}, {"StrictSubnets", VAR_SERVER}, {"TunnelServer", VAR_SERVER}, {"UDPRcvBuf", VAR_SERVER}, @@ -1296,14 +1344,14 @@ static int cmd_config(int argc, char *argv[]) { char *value; int len; - len = strcspn(line, "\t ="); - value = line + len; - value += strspn(value, "\t "); - if(*value == '=') { - value++; - value += strspn(value, "\t "); - } - line[len] = '\0'; + len = strcspn(line, "\t ="); + value = line + len; + value += strspn(value, "\t "); + if(*value == '=') { + value++; + value += strspn(value, "\t "); + } + line[len] = '\0'; variable = strchr(line, '.'); if(variable) { node = line; @@ -1563,7 +1611,10 @@ static int cmd_init(int argc, char *argv[]) { return 1; } - if(argc < 2) { + if(argc > 2) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } else if(argc < 2) { if(tty) { char buf[1024]; fprintf(stdout, "Enter the Name you want your tinc node to have: "); @@ -1642,14 +1693,29 @@ static int cmd_init(int argc, char *argv[]) { } static int cmd_generate_keys(int argc, char *argv[]) { + if(argc > 2) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ecdsa_keygen(true)); } static int cmd_generate_rsa_keys(int argc, char *argv[]) { + if(argc > 2) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true); } static int cmd_generate_ecdsa_keys(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + return !ecdsa_keygen(true); } @@ -1659,6 +1725,11 @@ static int cmd_help(int argc, char *argv[]) { } static int cmd_version(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + version(); return 0; } @@ -1761,14 +1832,27 @@ static int export(const char *name, FILE *out) { } static int cmd_export(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + char *name = get_my_name(); if(!name) return 1; - return export(name, stdout); + int result = export(name, stdout); + if(!tty) + fclose(stdout); + return result; } static int cmd_export_all(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + DIR *dir = opendir(hosts_dir); if(!dir) { fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno)); @@ -1792,21 +1876,30 @@ static int cmd_export_all(int argc, char *argv[]) { } closedir(dir); + if(!tty) + fclose(stdout); return result; } static int cmd_import(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + FILE *in = stdin; FILE *out = NULL; char buf[4096]; char name[4096]; - char *filename; + char *filename = NULL; int count = 0; bool firstline = true; while(fgets(buf, sizeof buf, in)) { if(sscanf(buf, "Name = %s", name) == 1) { + firstline = false; + if(!check_id(name)) { fprintf(stderr, "Invalid Name in input!\n"); return 1; @@ -1831,7 +1924,6 @@ static int cmd_import(int argc, char *argv[]) { } count++; - firstline = false; continue; } else if(firstline) { fprintf(stderr, "Junk at the beginning of the input, ignoring.\n"); @@ -1862,6 +1954,14 @@ static int cmd_import(int argc, char *argv[]) { } } +static int cmd_exchange(int argc, char *argv[]) { + return cmd_export(argc, argv) ?: cmd_import(argc, argv); +} + +static int cmd_exchange_all(int argc, char *argv[]) { + return cmd_export_all(argc, argv) ?: cmd_import(argc, argv); +} + static const struct { const char *command; int (*function)(int argc, char *argv[]); @@ -1892,6 +1992,8 @@ static const struct { {"export", cmd_export}, {"export-all", cmd_export_all}, {"import", cmd_import}, + {"exchange", cmd_exchange}, + {"exchange-all", cmd_exchange_all}, {NULL, NULL}, }; @@ -1914,7 +2016,7 @@ static char *complete_command(const char *text, int state) { } static char *complete_dump(const char *text, int state) { - const char *matches[] = {"nodes", "edges", "subnets", "connections", "graph", NULL}; + const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL}; static int i; if(!state) @@ -2037,7 +2139,6 @@ static int cmd_shell(int argc, char *argv[]) { char *line = NULL; int maxargs = argc + 16; char **nargv = xmalloc(maxargs * sizeof *nargv); - optind = argc; for(int i = 0; i < argc; i++) nargv[i] = argv[i]; @@ -2123,6 +2224,8 @@ static int cmd_shell(int argc, char *argv[]) { } } + free(nargv); + if(tty) printf("\n"); return result; @@ -2136,8 +2239,10 @@ int main(int argc, char *argv[]) { if(!parse_options(argc, argv)) return 1; - + make_names(); + xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase); + xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase); if(show_version) { version();