X-Git-Url: https://tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Ftincctl.c;h=e0a6086e8f89b9b44f3ce9d328656c16206efb8c;hb=58e8f598f38dbb2f210d8a62c8fb4b46513dc39f;hp=f4379b7f8ebbe6dce5de0cdb42f5321e47d4599b;hpb=19e42b76f546dc3baee4a5d6a4f161155d279c74;p=tinc diff --git a/src/tincctl.c b/src/tincctl.c index f4379b7f..e0a6086e 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -1,6 +1,6 @@ /* tincctl.c -- Controlling a running tincd - Copyright (C) 2007-2014 Guus Sliepen + Copyright (C) 2007-2015 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 "crypto.h" #include "ecdsagen.h" +#include "fsck.h" #include "info.h" #include "invitation.h" #include "names.h" @@ -66,7 +67,7 @@ char line[4096]; static int code; static int req; static int result; -static bool force = false; +bool force = false; bool tty = true; bool confbasegiven = false; bool netnamegiven = false; @@ -75,6 +76,7 @@ char *scriptextension = ""; static char *prompt; static struct option const long_options[] = { + {"batch", no_argument, NULL, 'b'}, {"config", required_argument, NULL, 'c'}, {"net", required_argument, NULL, 'n'}, {"help", no_argument, NULL, 1}, @@ -87,7 +89,7 @@ static struct option const long_options[] = { static void version(void) { printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR); - printf("Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others.\n" + printf("Copyright (C) 1998-2014 Ivo Timmermans, Guus Sliepen and others.\n" "See the AUTHORS file for a complete list.\n\n" "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n" "and you are welcome to redistribute it under certain conditions;\n" @@ -100,9 +102,11 @@ static void usage(bool status) { } else { printf("Usage: %s [options] command\n\n", program_name); printf("Valid options are:\n" + " -b, --batch Don't ask for anything (non-interactive mode).\n" " -c, --config=DIR Read configuration options from DIR.\n" " -n, --net=NETNAME Connect to net NETNAME.\n" " --pidfile=FILENAME Read control cookie from FILENAME.\n" + " --force Force some commands to work despite warnings.\n" " --help Display this help and exit.\n" " --version Output version information and exit.\n" "\n" @@ -117,8 +121,12 @@ static void usage(bool status) { " restart [tincd options] Restart tincd.\n" " reload Partially reload configuration of running tincd.\n" " pid Show PID of currently running tincd.\n" +#ifdef DISABLE_LEGACY + " generate-keys Generate a new Ed25519 public/private keypair.\n" +#else " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n" " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n" +#endif " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n" " dump Dump a list of one of the following things:\n" " [reachable] nodes - all known nodes in the VPN\n" @@ -126,6 +134,7 @@ static void usage(bool status) { " subnets - all known subnets in the VPN\n" " connections - all meta connections with ourself\n" " [di]graph - graph of the VPN in dotty format\n" + " invitations - outstanding invitations\n" " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n" " purge Purge unreachable nodes\n" " debug N Set debug level\n" @@ -138,12 +147,13 @@ static void usage(bool status) { " log [level] Dump log output [up to the specified level]\n" " 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" + " import Import host configuration file(s) from standard input\n" + " exchange Same as export followed by import\n" + " exchange-all Same as export-all followed by import\n" " invite NODE [...] Generate an invitation for NODE\n" - " join INVITATION Join a VPN using an INVITIATION\n" + " join INVITATION Join a VPN using an INVITATION\n" " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n" + " fsck Check the configuration files for problems.\n" "\n"); printf("Report bugs to tinc@tinc-vpn.org.\n"); } @@ -158,6 +168,10 @@ static bool parse_options(int argc, char **argv) { case 0: /* long option */ break; + case 'b': + tty = false; + break; + case 'c': /* config file */ confbase = xstrdup(optarg); confbasegiven = true; @@ -409,6 +423,7 @@ static bool ed25519_keygen(bool ask) { return true; } +#ifndef DISABLE_LEGACY /* Generate a public/private RSA keypair, and ask for a file to store them in. @@ -474,6 +489,7 @@ static bool rsa_keygen(int bits, bool ask) { return true; } +#endif char buffer[4096]; size_t blen = 0; @@ -757,7 +773,7 @@ bool connect_tincd(bool verbose) { #ifdef SO_NOSIGPIPE static const int one = 1; - setsockopt(c, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one); + setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one); #endif char data[4096]; @@ -928,6 +944,65 @@ static int cmd_reload(int argc, char *argv[]) { } +static int dump_invitations(void) { + char dname[PATH_MAX]; + snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase); + DIR *dir = opendir(dname); + if(!dir) { + if(errno == ENOENT) { + fprintf(stderr, "No outstanding invitations.\n"); + return 0; + } + + fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno)); + return 1; + } + + struct dirent *ent; + bool found = false; + + while((ent = readdir(dir))) { + char buf[MAX_STRING_SIZE]; + if(b64decode(ent->d_name, buf, 24) != 18) + continue; + + char fname[PATH_MAX]; + snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name); + FILE *f = fopen(fname, "r"); + if(!f) { + fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno)); + fclose(f); + continue; + } + + buf[0] = 0; + if(!fgets(buf, sizeof buf, f)) { + fprintf(stderr, "Invalid invitation file %s", fname); + fclose(f); + continue; + } + fclose(f); + + char *eol = buf + strlen(buf); + while(strchr("\t \r\n", *--eol)) + *eol = 0; + if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) { + fprintf(stderr, "Invalid invitation file %s", fname); + continue; + } + + found = true; + printf("%s %s\n", ent->d_name, buf + 7); + } + + closedir(dir); + + if(!found) + fprintf(stderr, "No outstanding invitations.\n"); + + return 0; +} + static int cmd_dump(int argc, char *argv[]) { bool only_reachable = false; @@ -948,6 +1023,9 @@ static int cmd_dump(int argc, char *argv[]) { return 1; } + if(!strcasecmp(argv[1], "invitations")) + return dump_invitations(); + if(!connect_tincd(true)) return 1; @@ -996,6 +1074,7 @@ static int cmd_dump(int argc, char *argv[]) { break; char node[4096]; + char id[4096]; char from[4096]; char to[4096]; char subnet[4096]; @@ -1013,8 +1092,8 @@ static int cmd_dump(int argc, char *argv[]) { 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) { + int n = sscanf(line, "%*d %*d %s %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change); + if(n != 17) { fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line); return 1; } @@ -1037,8 +1116,8 @@ static int cmd_dump(int argc, char *argv[]) { } 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_int, nexthop, via, distance, pmtu, minmtu, maxmtu); + printf("%s id %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, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu); } } break; @@ -1299,7 +1378,7 @@ char *get_my_name(bool verbose) { continue; if(*value) { fclose(f); - return strdup(value); + return replace_name(value); } } @@ -1316,9 +1395,11 @@ const var_t variables[] = { {"BindToAddress", VAR_SERVER | VAR_MULTIPLE}, {"BindToInterface", VAR_SERVER}, {"Broadcast", VAR_SERVER | VAR_SAFE}, + {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE}, {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE}, {"DecrementTTL", VAR_SERVER}, {"Device", VAR_SERVER}, + {"DeviceStandby", VAR_SERVER}, {"DeviceType", VAR_SERVER}, {"DirectOnly", VAR_SERVER}, {"Ed25519PrivateKeyFile", VAR_SERVER}, @@ -1349,6 +1430,12 @@ const var_t variables[] = { {"ScriptsInterpreter", VAR_SERVER}, {"StrictSubnets", VAR_SERVER}, {"TunnelServer", VAR_SERVER}, + {"UDPDiscovery", VAR_SERVER}, + {"UDPDiscoveryKeepaliveInterval", VAR_SERVER}, + {"UDPDiscoveryInterval", VAR_SERVER}, + {"UDPDiscoveryTimeout", VAR_SERVER}, + {"MTUInfoInterval", VAR_SERVER}, + {"UDPInfoInterval", VAR_SERVER}, {"UDPRcvBuf", VAR_SERVER}, {"UDPSndBuf", VAR_SERVER}, {"VDEGroup", VAR_SERVER}, @@ -1589,6 +1676,11 @@ static int cmd_config(int argc, char *argv[]) { } set = true; continue; + // Add + } else if(action > 0) { + // Check if we've already seen this variable with the same value + if(!strcasecmp(bvalue, value)) + found = true; } } @@ -1621,7 +1713,7 @@ static int cmd_config(int argc, char *argv[]) { } // Add new variable if necessary. - if(action > 0 || (action == 0 && !set)) { + if((action > 0 && !found)|| (action == 0 && !set)) { if(fprintf(tf, "%s = %s\n", variable, value) < 0) { fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno)); return 1; @@ -1629,9 +1721,12 @@ static int cmd_config(int argc, char *argv[]) { } if(action < -1) { - if(!found) + if(found) { + return 0; + } else { fprintf(stderr, "No matching configuration variables found.\n"); - return 1; + return 1; + } } // Make sure we wrote everything... @@ -1666,18 +1761,6 @@ static int cmd_config(int argc, char *argv[]) { return 0; } -bool check_id(const char *name) { - if(!name || !*name) - return false; - - for(int i = 0; i < strlen(name); i++) { - if(!isalnum(name[i]) && name[i] != '_') - return false; - } - - return true; -} - static bool try_bind(int port) { struct addrinfo *ai = NULL; struct addrinfo hint = { @@ -1800,7 +1883,12 @@ static int cmd_init(int argc, char *argv[]) { fprintf(f, "Name = %s\n", name); fclose(f); - if(!rsa_keygen(2048, false) || !ed25519_keygen(false)) +#ifndef DISABLE_LEGACY + if(!rsa_keygen(2048, false)) + return 1; +#endif + + if(!ed25519_keygen(false)) return 1; check_port(name); @@ -1824,7 +1912,11 @@ static int cmd_init(int argc, char *argv[]) { } static int cmd_generate_keys(int argc, char *argv[]) { +#ifdef DISABLE_LEGACY + if(argc > 1) { +#else if(argc > 2) { +#endif fprintf(stderr, "Too many arguments!\n"); return 1; } @@ -1832,9 +1924,18 @@ static int cmd_generate_keys(int argc, char *argv[]) { if(!name) name = get_my_name(false); - return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ed25519_keygen(true)); +#ifndef DISABLE_LEGACY + if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) + return 1; +#endif + + if(!ed25519_keygen(true)) + return 1; + + return 0; } +#ifndef DISABLE_LEGACY static int cmd_generate_rsa_keys(int argc, char *argv[]) { if(argc > 2) { fprintf(stderr, "Too many arguments!\n"); @@ -1846,6 +1947,7 @@ static int cmd_generate_rsa_keys(int argc, char *argv[]) { return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true); } +#endif static int cmd_generate_ed25519_keys(int argc, char *argv[]) { if(argc > 1) { @@ -2125,7 +2227,6 @@ static int switch_network(char *name) { free(netname); netname = strcmp(name, ".") ? xstrdup(name) : NULL; - make_names(); xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase); xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase); xasprintf(&prompt, "%s> ", identname); @@ -2170,6 +2271,15 @@ static int cmd_network(int argc, char *argv[]) { return 0; } +static int cmd_fsck(int argc, char *argv[]) { + if(argc > 1) { + fprintf(stderr, "Too many arguments!\n"); + return 1; + } + + return fsck(orig_argv[0]); +} + static const struct { const char *command; int (*function)(int argc, char *argv[]); @@ -2180,6 +2290,7 @@ static const struct { {"restart", cmd_restart}, {"reload", cmd_reload}, {"dump", cmd_dump}, + {"list", cmd_dump}, {"purge", cmd_purge}, {"debug", cmd_debug}, {"retry", cmd_retry}, @@ -2196,7 +2307,9 @@ static const struct { {"set", cmd_config}, {"init", cmd_init}, {"generate-keys", cmd_generate_keys}, +#ifndef DISABLE_LEGACY {"generate-rsa-keys", cmd_generate_rsa_keys}, +#endif {"generate-ed25519-keys", cmd_generate_ed25519_keys}, {"help", cmd_help}, {"version", cmd_version}, @@ -2210,6 +2323,7 @@ static const struct { {"invite", cmd_invite}, {"join", cmd_join}, {"network", cmd_network}, + {"fsck", cmd_fsck}, {NULL, NULL}, }; @@ -2439,6 +2553,7 @@ int main(int argc, char *argv[]) { program_name = argv[0]; orig_argv = argv; orig_argc = argc; + tty = isatty(0) && isatty(1); if(!parse_options(argc, argv)) return 1; @@ -2469,8 +2584,6 @@ int main(int argc, char *argv[]) { srand(time(NULL)); crypto_init(); - tty = isatty(0) && isatty(1); - if(optind >= argc) return cmd_shell(argc, argv);