2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2014 Guus Sliepen <guus@tinc-vpn.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "readline/readline.h"
26 #include "readline/history.h"
31 #include "control_common.h"
36 #include "invitation.h"
45 #define MSG_NOSIGNAL 0
48 static char **orig_argv;
51 /* If nonzero, display usage information and exit. */
52 static bool show_help = false;
54 /* If nonzero, print the version on standard output and exit. */
55 static bool show_version = false;
57 static char *name = NULL;
58 static char controlcookie[1025];
59 char *tinc_conf = NULL;
60 char *hosts_dir = NULL;
63 // Horrible global variables...
72 bool confbasegiven = false;
73 bool netnamegiven = false;
74 char *scriptinterpreter = NULL;
75 char *scriptextension = "";
78 static struct option const long_options[] = {
79 {"batch", no_argument, NULL, 'b'},
80 {"config", required_argument, NULL, 'c'},
81 {"net", required_argument, NULL, 'n'},
82 {"help", no_argument, NULL, 1},
83 {"version", no_argument, NULL, 2},
84 {"pidfile", required_argument, NULL, 3},
85 {"force", no_argument, NULL, 4},
89 static void version(void) {
90 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
91 VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
92 printf("Copyright (C) 1998-2014 Ivo Timmermans, Guus Sliepen and others.\n"
93 "See the AUTHORS file for a complete list.\n\n"
94 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
95 "and you are welcome to redistribute it under certain conditions;\n"
96 "see the file COPYING for details.\n");
99 static void usage(bool status) {
101 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
103 printf("Usage: %s [options] command\n\n", program_name);
104 printf("Valid options are:\n"
105 " -b, --batch Don't ask for anything (non-interactive mode).\n"
106 " -c, --config=DIR Read configuration options from DIR.\n"
107 " -n, --net=NETNAME Connect to net NETNAME.\n"
108 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
109 " --help Display this help and exit.\n"
110 " --version Output version information and exit.\n"
112 "Valid commands are:\n"
113 " init [name] Create initial configuration files.\n"
114 " get VARIABLE Print current value of VARIABLE\n"
115 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
116 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
117 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
118 " start [tincd options] Start tincd.\n"
119 " stop Stop tincd.\n"
120 " restart [tincd options] Restart tincd.\n"
121 " reload Partially reload configuration of running tincd.\n"
122 " pid Show PID of currently running tincd.\n"
123 #ifdef DISABLE_LEGACY
124 " generate-keys Generate a new Ed25519 public/private keypair.\n"
126 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
127 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
129 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
130 " dump Dump a list of one of the following things:\n"
131 " [reachable] nodes - all known nodes in the VPN\n"
132 " edges - all known connections in the VPN\n"
133 " subnets - all known subnets in the VPN\n"
134 " connections - all meta connections with ourself\n"
135 " [di]graph - graph of the VPN in dotty format\n"
136 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
137 " purge Purge unreachable nodes\n"
138 " debug N Set debug level\n"
139 " retry Retry all outgoing connections\n"
140 " disconnect NODE Close meta connection with NODE\n"
142 " top Show real-time statistics\n"
144 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
145 " log [level] Dump log output [up to the specified level]\n"
146 " export Export host configuration of local node to standard output\n"
147 " export-all Export all host configuration files to standard output\n"
148 " import [--force] Import host configuration file(s) from standard input\n"
149 " exchange [--force] Same as export followed by import\n"
150 " exchange-all [--force] Same as export-all followed by import\n"
151 " invite NODE [...] Generate an invitation for NODE\n"
152 " join INVITATION Join a VPN using an INVITIATION\n"
153 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
154 " fsck Check the configuration files for problems.\n"
156 printf("Report bugs to tinc@tinc-vpn.org.\n");
160 static bool parse_options(int argc, char **argv) {
162 int option_index = 0;
164 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
166 case 0: /* long option */
173 case 'c': /* config file */
174 confbase = xstrdup(optarg);
175 confbasegiven = true;
178 case 'n': /* net name given */
179 netname = xstrdup(optarg);
182 case 1: /* show help */
186 case 2: /* show version */
190 case 3: /* open control socket here */
191 pidfilename = xstrdup(optarg);
198 case '?': /* wrong options */
207 if(!netname && (netname = getenv("NETNAME")))
208 netname = xstrdup(netname);
210 /* netname "." is special: a "top-level name" */
212 if(netname && (!*netname || !strcmp(netname, "."))) {
217 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
218 fprintf(stderr, "Invalid character in netname!\n");
225 /* Open a file with the desired permissions, minus the umask.
226 Also, if we want to create an executable file, we call fchmod()
227 to set the executable bits. */
229 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
230 mode_t mask = umask(0);
233 FILE *f = fopen(filename, mode);
235 if((perms & 0444) && f)
236 fchmod(fileno(f), perms);
242 static void disable_old_keys(const char *filename, const char *what) {
243 char tmpfile[PATH_MAX] = "";
245 bool disabled = false;
250 r = fopen(filename, "r");
254 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
256 struct stat st = {.st_mode = 0600};
257 fstat(fileno(r), &st);
258 w = fopenmask(tmpfile, "w", st.st_mode);
260 while(fgets(buf, sizeof buf, r)) {
261 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
262 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
268 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
274 if(block || ed25519pubkey)
276 if(fputs(buf, w) < 0) {
282 if(block && !strncmp(buf, "-----END ", 9))
289 if(ferror(r) || fclose(r) < 0)
294 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
301 // We cannot atomically replace files on Windows.
302 char bakfile[PATH_MAX] = "";
303 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
304 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
305 rename(bakfile, filename);
307 if(rename(tmpfile, filename)) {
309 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
314 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
321 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
327 /* Check stdin and stdout */
329 /* Ask for a file and/or directory name. */
330 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
332 if(fgets(buf, sizeof buf, stdin) == NULL) {
333 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
337 size_t len = strlen(buf);
346 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
348 if(filename[0] != '/') {
350 /* The directory is a relative path or a filename. */
351 directory = get_current_dir_name();
352 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
356 disable_old_keys(filename, what);
358 /* Open it first to keep the inode busy */
360 r = fopenmask(filename, mode, perms);
363 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
371 Generate a public/private Ed25519 keypair, and ask for a file to store
374 static bool ed25519_keygen(bool ask) {
377 char *pubname, *privname;
379 fprintf(stderr, "Generating Ed25519 keypair:\n");
381 if(!(key = ecdsa_generate())) {
382 fprintf(stderr, "Error during key generation!\n");
385 fprintf(stderr, "Done.\n");
387 xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
388 f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
394 if(!ecdsa_write_pem_private_key(key, f)) {
395 fprintf(stderr, "Error writing private key!\n");
404 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
406 xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
408 f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
414 char *pubkey = ecdsa_get_base64_public_key(key);
415 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
424 #ifndef DISABLE_LEGACY
426 Generate a public/private RSA keypair, and ask for a file to store
429 static bool rsa_keygen(int bits, bool ask) {
432 char *pubname, *privname;
434 // Make sure the key size is a multiple of 8 bits.
437 // Force them to be between 1024 and 8192 bits long.
443 fprintf(stderr, "Generating %d bits keys:\n", bits);
445 if(!(key = rsa_generate(bits, 0x10001))) {
446 fprintf(stderr, "Error during key generation!\n");
449 fprintf(stderr, "Done.\n");
451 xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
452 f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
458 if(!rsa_write_pem_private_key(key, f)) {
459 fprintf(stderr, "Error writing private key!\n");
468 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
470 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
472 f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
478 if(!rsa_write_pem_public_key(key, f)) {
479 fprintf(stderr, "Error writing public key!\n");
495 bool recvline(int fd, char *line, size_t len) {
496 char *newline = NULL;
501 while(!(newline = memchr(buffer, '\n', blen))) {
502 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
503 if(result == -1 && sockerrno == EINTR)
510 if(newline - buffer >= len)
513 len = newline - buffer;
515 memcpy(line, buffer, len);
517 memmove(buffer, newline + 1, blen - len - 1);
523 bool recvdata(int fd, char *data, size_t len) {
528 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
529 if(result == -1 && sockerrno == EINTR)
536 memcpy(data, buffer, len);
537 memmove(buffer, buffer + len, blen - len);
543 bool sendline(int fd, char *format, ...) {
544 static char buffer[4096];
549 va_start(ap, format);
550 blen = vsnprintf(buffer, sizeof buffer, format, ap);
553 if(blen < 1 || blen >= sizeof buffer)
560 int result = send(fd, p, blen, MSG_NOSIGNAL);
561 if(result == -1 && sockerrno == EINTR)
572 static void pcap(int fd, FILE *out, int snaplen) {
573 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
581 uint32_t tz_accuracy;
588 snaplen ?: sizeof data,
601 fwrite(&header, sizeof header, 1, out);
605 while(recvline(fd, line, sizeof line)) {
607 int n = sscanf(line, "%d %d %d", &code, &req, &len);
608 gettimeofday(&tv, NULL);
609 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
611 if(!recvdata(fd, data, len))
613 packet.tv_sec = tv.tv_sec;
614 packet.tv_usec = tv.tv_usec;
616 packet.origlen = len;
617 fwrite(&packet, sizeof packet, 1, out);
618 fwrite(data, len, 1, out);
623 static void logcontrol(int fd, FILE *out, int level) {
624 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
628 while(recvline(fd, line, sizeof line)) {
630 int n = sscanf(line, "%d %d %d", &code, &req, &len);
631 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
633 if(!recvdata(fd, data, len))
635 fwrite(data, len, 1, out);
642 static bool remove_service(void) {
643 SC_HANDLE manager = NULL;
644 SC_HANDLE service = NULL;
645 SERVICE_STATUS status = {0};
647 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
649 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
653 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
656 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
660 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
661 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
663 fprintf(stderr, "%s service stopped\n", identname);
665 if(!DeleteService(service)) {
666 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
670 fprintf(stderr, "%s service removed\n", identname);
676 bool connect_tincd(bool verbose) {
681 struct timeval tv = {0, 0};
682 if(select(fd + 1, &r, NULL, NULL, &tv)) {
683 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
691 FILE *f = fopen(pidfilename, "r");
694 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
701 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
703 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
711 struct sockaddr_un sa;
712 sa.sun_family = AF_UNIX;
713 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
715 fd = socket(AF_UNIX, SOCK_STREAM, 0);
718 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
722 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
724 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
730 struct addrinfo hints = {
731 .ai_family = AF_UNSPEC,
732 .ai_socktype = SOCK_STREAM,
733 .ai_protocol = IPPROTO_TCP,
737 struct addrinfo *res = NULL;
739 if(getaddrinfo(host, port, &hints, &res) || !res) {
741 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
745 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
748 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
753 unsigned long arg = 0;
755 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
757 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
761 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
763 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
773 static const int one = 1;
774 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
780 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
782 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
788 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
790 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
792 fprintf(stderr, "Could not fully establish control socket connection\n");
802 static int cmd_start(int argc, char *argv[]) {
803 if(connect_tincd(false)) {
805 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
807 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
812 char *slash = strrchr(program_name, '/');
815 if ((c = strrchr(program_name, '\\')) > slash)
820 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
825 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
830 Windows has no real concept of an "argv array". A command line is just one string.
831 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
832 it uses quotes to handle spaces in arguments.
833 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
834 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
835 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
837 xasprintf(&arg0, "\"%s\"", arg0);
839 nargv[nargc++] = arg0;
840 for(int i = 1; i < optind; i++)
841 nargv[nargc++] = orig_argv[i];
842 for(int i = 1; i < argc; i++)
843 nargv[nargc++] = argv[i];
846 int status = spawnvp(_P_WAIT, c, nargv);
848 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
855 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
861 exit(execvp(c, nargv));
865 int status = -1, result;
867 signal(SIGINT, SIG_IGN);
869 result = waitpid(pid, &status, 0);
871 signal(SIGINT, SIG_DFL);
874 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
875 fprintf(stderr, "Error starting %s\n", c);
883 static int cmd_stop(int argc, char *argv[]) {
885 fprintf(stderr, "Too many arguments!\n");
890 if(!connect_tincd(true)) {
892 if(kill(pid, SIGTERM)) {
893 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
897 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
898 waitpid(pid, NULL, 0);
905 sendline(fd, "%d %d", CONTROL, REQ_STOP);
907 while(recvline(fd, line, sizeof line)) {
908 // Wait for tincd to close the connection...
911 if(!remove_service())
921 static int cmd_restart(int argc, char *argv[]) {
923 return cmd_start(argc, argv);
926 static int cmd_reload(int argc, char *argv[]) {
928 fprintf(stderr, "Too many arguments!\n");
932 if(!connect_tincd(true))
935 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
936 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
937 fprintf(stderr, "Could not reload configuration.\n");
945 static int cmd_dump(int argc, char *argv[]) {
946 bool only_reachable = false;
948 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
949 if(strcasecmp(argv[2], "nodes")) {
950 fprintf(stderr, "`reachable' only supported for nodes.\n");
954 only_reachable = true;
960 fprintf(stderr, "Invalid number of arguments.\n");
965 if(!connect_tincd(true))
970 if(!strcasecmp(argv[1], "nodes"))
971 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
972 else if(!strcasecmp(argv[1], "edges"))
973 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
974 else if(!strcasecmp(argv[1], "subnets"))
975 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
976 else if(!strcasecmp(argv[1], "connections"))
977 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
978 else if(!strcasecmp(argv[1], "graph")) {
979 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
980 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
982 } else if(!strcasecmp(argv[1], "digraph")) {
983 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
984 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
987 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
994 else if(do_graph == 2)
995 printf("digraph {\n");
997 while(recvline(fd, line, sizeof line)) {
998 char node1[4096], node2[4096];
999 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1001 if(do_graph && req == REQ_DUMP_NODES)
1019 char local_host[4096];
1020 char local_port[4096];
1023 int cipher, digest, maclength, compression, distance, socket, weight;
1024 short int pmtu, minmtu, maxmtu;
1025 unsigned int options, status_int;
1026 node_status_t status;
1027 long int last_state_change;
1030 case REQ_DUMP_NODES: {
1031 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);
1033 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1037 memcpy(&status, &status_int, sizeof status);
1040 const char *color = "black";
1041 if(!strcmp(host, "MYSELF"))
1043 else if(!status.reachable)
1045 else if(strcmp(via, node))
1047 else if(!status.validkey)
1051 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1053 if(only_reachable && !status.reachable)
1055 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",
1056 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1060 case REQ_DUMP_EDGES: {
1061 int n = sscanf(line, "%*d %*d %s %s %s port %s %s port %s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
1063 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1068 float w = 1 + 65536.0 / weight;
1069 if(do_graph == 1 && strcmp(node1, node2) > 0)
1070 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1071 else if(do_graph == 2)
1072 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1074 printf("%s to %s at %s port %s local %s port %s options %x weight %d\n", from, to, host, port, local_host, local_port, options, weight);
1078 case REQ_DUMP_SUBNETS: {
1079 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1081 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1084 printf("%s owner %s\n", strip_weight(subnet), node);
1087 case REQ_DUMP_CONNECTIONS: {
1088 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1090 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1093 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1097 fprintf(stderr, "Unable to parse dump from tincd.\n");
1102 fprintf(stderr, "Error receiving dump.\n");
1106 static int cmd_purge(int argc, char *argv[]) {
1108 fprintf(stderr, "Too many arguments!\n");
1112 if(!connect_tincd(true))
1115 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1116 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1117 fprintf(stderr, "Could not purge old information.\n");
1124 static int cmd_debug(int argc, char *argv[]) {
1126 fprintf(stderr, "Invalid number of arguments.\n");
1130 if(!connect_tincd(true))
1133 int debuglevel = atoi(argv[1]);
1136 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1137 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1138 fprintf(stderr, "Could not set debug level.\n");
1142 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1146 static int cmd_retry(int argc, char *argv[]) {
1148 fprintf(stderr, "Too many arguments!\n");
1152 if(!connect_tincd(true))
1155 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1156 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1157 fprintf(stderr, "Could not retry outgoing connections.\n");
1164 static int cmd_connect(int argc, char *argv[]) {
1166 fprintf(stderr, "Invalid number of arguments.\n");
1170 if(!check_id(argv[1])) {
1171 fprintf(stderr, "Invalid name for node.\n");
1175 if(!connect_tincd(true))
1178 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1179 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1180 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1187 static int cmd_disconnect(int argc, char *argv[]) {
1189 fprintf(stderr, "Invalid number of arguments.\n");
1193 if(!check_id(argv[1])) {
1194 fprintf(stderr, "Invalid name for node.\n");
1198 if(!connect_tincd(true))
1201 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1202 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1203 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1210 static int cmd_top(int argc, char *argv[]) {
1212 fprintf(stderr, "Too many arguments!\n");
1217 if(!connect_tincd(true))
1223 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1228 static int cmd_pcap(int argc, char *argv[]) {
1230 fprintf(stderr, "Too many arguments!\n");
1234 if(!connect_tincd(true))
1237 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1242 static void sigint_handler(int sig) {
1243 fprintf(stderr, "\n");
1244 shutdown(fd, SHUT_RDWR);
1248 static int cmd_log(int argc, char *argv[]) {
1250 fprintf(stderr, "Too many arguments!\n");
1254 if(!connect_tincd(true))
1258 signal(SIGINT, sigint_handler);
1261 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1264 signal(SIGINT, SIG_DFL);
1272 static int cmd_pid(int argc, char *argv[]) {
1274 fprintf(stderr, "Too many arguments!\n");
1278 if(!connect_tincd(true) && !pid)
1281 printf("%d\n", pid);
1285 int rstrip(char *value) {
1286 int len = strlen(value);
1287 while(len && strchr("\t\r\n ", value[len - 1]))
1292 char *get_my_name(bool verbose) {
1293 FILE *f = fopen(tinc_conf, "r");
1296 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1302 while(fgets(buf, sizeof buf, f)) {
1303 int len = strcspn(buf, "\t =");
1305 value += strspn(value, "\t ");
1308 value += strspn(value, "\t ");
1313 if(strcasecmp(buf, "Name"))
1317 return replace_name(value);
1323 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1327 const var_t variables[] = {
1328 /* Server configuration */
1329 {"AddressFamily", VAR_SERVER},
1330 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1331 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1332 {"BindToInterface", VAR_SERVER},
1333 {"Broadcast", VAR_SERVER | VAR_SAFE},
1334 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1335 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1336 {"DecrementTTL", VAR_SERVER},
1337 {"Device", VAR_SERVER},
1338 {"DeviceStandby", VAR_SERVER},
1339 {"DeviceType", VAR_SERVER},
1340 {"DirectOnly", VAR_SERVER},
1341 {"Ed25519PrivateKeyFile", VAR_SERVER},
1342 {"ExperimentalProtocol", VAR_SERVER},
1343 {"Forwarding", VAR_SERVER},
1344 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1345 {"Hostnames", VAR_SERVER},
1346 {"IffOneQueue", VAR_SERVER},
1347 {"Interface", VAR_SERVER},
1348 {"KeyExpire", VAR_SERVER},
1349 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1350 {"LocalDiscovery", VAR_SERVER},
1351 {"MACExpire", VAR_SERVER},
1352 {"MaxConnectionBurst", VAR_SERVER},
1353 {"MaxOutputBufferSize", VAR_SERVER},
1354 {"MaxTimeout", VAR_SERVER},
1355 {"Mode", VAR_SERVER | VAR_SAFE},
1356 {"Name", VAR_SERVER},
1357 {"PingInterval", VAR_SERVER},
1358 {"PingTimeout", VAR_SERVER},
1359 {"PriorityInheritance", VAR_SERVER},
1360 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1361 {"PrivateKeyFile", VAR_SERVER},
1362 {"ProcessPriority", VAR_SERVER},
1363 {"Proxy", VAR_SERVER},
1364 {"ReplayWindow", VAR_SERVER},
1365 {"ScriptsExtension", VAR_SERVER},
1366 {"ScriptsInterpreter", VAR_SERVER},
1367 {"StrictSubnets", VAR_SERVER},
1368 {"TunnelServer", VAR_SERVER},
1369 {"UDPDiscovery", VAR_SERVER},
1370 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1371 {"UDPDiscoveryInterval", VAR_SERVER},
1372 {"UDPDiscoveryTimeout", VAR_SERVER},
1373 {"UDPRcvBuf", VAR_SERVER},
1374 {"UDPSndBuf", VAR_SERVER},
1375 {"VDEGroup", VAR_SERVER},
1376 {"VDEPort", VAR_SERVER},
1377 /* Host configuration */
1378 {"Address", VAR_HOST | VAR_MULTIPLE},
1379 {"Cipher", VAR_SERVER | VAR_HOST},
1380 {"ClampMSS", VAR_SERVER | VAR_HOST},
1381 {"Compression", VAR_SERVER | VAR_HOST},
1382 {"Digest", VAR_SERVER | VAR_HOST},
1383 {"Ed25519PublicKey", VAR_HOST},
1384 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1385 {"IndirectData", VAR_SERVER | VAR_HOST},
1386 {"MACLength", VAR_SERVER | VAR_HOST},
1387 {"PMTU", VAR_SERVER | VAR_HOST},
1388 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1390 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1391 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1392 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1393 {"TCPOnly", VAR_SERVER | VAR_HOST},
1394 {"Weight", VAR_HOST | VAR_SAFE},
1398 static int cmd_config(int argc, char *argv[]) {
1400 fprintf(stderr, "Invalid number of arguments.\n");
1404 if(strcasecmp(argv[0], "config"))
1408 if(!strcasecmp(argv[1], "get")) {
1410 } else if(!strcasecmp(argv[1], "add")) {
1411 argv++, argc--, action = 1;
1412 } else if(!strcasecmp(argv[1], "del")) {
1413 argv++, argc--, action = -1;
1414 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1415 argv++, argc--, action = 0;
1419 fprintf(stderr, "Invalid number of arguments.\n");
1423 // Concatenate the rest of the command line
1424 strncpy(line, argv[1], sizeof line - 1);
1425 for(int i = 2; i < argc; i++) {
1426 strncat(line, " ", sizeof line - 1 - strlen(line));
1427 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1430 // Liberal parsing into node name, variable name and value.
1436 len = strcspn(line, "\t =");
1438 value += strspn(value, "\t ");
1441 value += strspn(value, "\t ");
1444 variable = strchr(line, '.');
1453 fprintf(stderr, "No variable given.\n");
1457 if(action >= 0 && !*value) {
1458 fprintf(stderr, "No value for variable given.\n");
1462 if(action < -1 && *value)
1465 /* Some simple checks. */
1467 bool warnonremove = false;
1469 for(int i = 0; variables[i].name; i++) {
1470 if(strcasecmp(variables[i].name, variable))
1474 variable = (char *)variables[i].name;
1476 /* Discourage use of obsolete variables. */
1478 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1480 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1482 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1487 /* Don't put server variables in host config files */
1489 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1491 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1493 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1498 /* Should this go into our own host config file? */
1500 if(!node && !(variables[i].type & VAR_SERVER)) {
1501 node = get_my_name(true);
1506 /* Change "add" into "set" for variables that do not allow multiple occurences.
1507 Turn on warnings when it seems variables might be removed unintentionally. */
1509 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1510 warnonremove = true;
1512 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1513 warnonremove = true;
1519 if(node && !check_id(node)) {
1520 fprintf(stderr, "Invalid name for node.\n");
1525 if(force || action < 0) {
1526 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1528 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1533 // Open the right configuration file.
1536 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1538 filename = tinc_conf;
1540 FILE *f = fopen(filename, "r");
1542 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1546 char *tmpfile = NULL;
1550 xasprintf(&tmpfile, "%s.config.tmp", filename);
1551 tf = fopen(tmpfile, "w");
1553 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1559 // Copy the file, making modifications on the fly, unless we are just getting a value.
1563 bool removed = false;
1566 while(fgets(buf1, sizeof buf1, f)) {
1567 buf1[sizeof buf1 - 1] = 0;
1568 strncpy(buf2, buf1, sizeof buf2);
1570 // Parse line in a simple way
1574 len = strcspn(buf2, "\t =");
1575 bvalue = buf2 + len;
1576 bvalue += strspn(bvalue, "\t ");
1577 if(*bvalue == '=') {
1579 bvalue += strspn(bvalue, "\t ");
1585 if(!strcasecmp(buf2, variable)) {
1589 printf("%s\n", bvalue);
1591 } else if(action == -1) {
1592 if(!*value || !strcasecmp(bvalue, value)) {
1597 } else if(action == 0) {
1598 // Warn if "set" was used for variables that can occur multiple times
1599 if(warnonremove && strcasecmp(bvalue, value))
1600 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1602 // Already set? Delete the rest...
1606 // Otherwise, replace.
1607 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1608 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1614 } else if(action > 0) {
1615 // Check if we've already seen this variable with the same value
1616 if(!strcasecmp(bvalue, value))
1622 // Copy original line...
1623 if(fputs(buf1, tf) < 0) {
1624 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1628 // Add newline if it is missing...
1629 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1630 if(fputc('\n', tf) < 0) {
1631 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1638 // Make sure we read everything...
1639 if(ferror(f) || !feof(f)) {
1640 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1645 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1649 // Add new variable if necessary.
1650 if((action > 0 && !found)|| (action == 0 && !set)) {
1651 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1652 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1661 fprintf(stderr, "No matching configuration variables found.\n");
1666 // Make sure we wrote everything...
1668 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1672 // Could we find what we had to remove?
1673 if(action < 0 && !removed) {
1675 fprintf(stderr, "No configuration variables deleted.\n");
1679 // Replace the configuration file with the new one
1681 if(remove(filename)) {
1682 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1686 if(rename(tmpfile, filename)) {
1687 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1691 // Silently try notifying a running tincd of changes.
1692 if(connect_tincd(false))
1693 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1698 static bool try_bind(int port) {
1699 struct addrinfo *ai = NULL;
1700 struct addrinfo hint = {
1701 .ai_flags = AI_PASSIVE,
1702 .ai_family = AF_UNSPEC,
1703 .ai_socktype = SOCK_STREAM,
1704 .ai_protocol = IPPROTO_TCP,
1708 snprintf(portstr, sizeof portstr, "%d", port);
1710 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1714 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1717 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1727 int check_port(char *name) {
1731 fprintf(stderr, "Warning: could not bind to port 655. ");
1733 for(int i = 0; i < 100; i++) {
1734 int port = 0x1000 + (rand() & 0x7fff);
1735 if(try_bind(port)) {
1737 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1738 FILE *f = fopen(filename, "a");
1741 fprintf(stderr, "Please change tinc's Port manually.\n");
1745 fprintf(f, "Port = %d\n", port);
1747 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1752 fprintf(stderr, "Please change tinc's Port manually.\n");
1756 static int cmd_init(int argc, char *argv[]) {
1757 if(!access(tinc_conf, F_OK)) {
1758 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1763 fprintf(stderr, "Too many arguments!\n");
1765 } else if(argc < 2) {
1768 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1769 if(!fgets(buf, sizeof buf, stdin)) {
1770 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1773 int len = rstrip(buf);
1775 fprintf(stderr, "No name given!\n");
1780 fprintf(stderr, "No Name given!\n");
1784 name = strdup(argv[1]);
1786 fprintf(stderr, "No Name given!\n");
1791 if(!check_id(name)) {
1792 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1796 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1797 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1801 if(mkdir(confbase, 0777) && errno != EEXIST) {
1802 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1806 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1807 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1811 FILE *f = fopen(tinc_conf, "w");
1813 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1817 fprintf(f, "Name = %s\n", name);
1820 #ifndef DISABLE_LEGACY
1821 if(!rsa_keygen(2048, false))
1825 if(!ed25519_keygen(false))
1832 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1833 if(access(filename, F_OK)) {
1834 FILE *f = fopenmask(filename, "w", 0777);
1836 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1839 fprintf(f, "#!/bin/sh\n\necho 'Unconfigured tinc-up script, please edit '$0'!'\n\n#ifconfig $INTERFACE <your vpn IP address> netmask <netmask of whole VPN>\n");
1848 static int cmd_generate_keys(int argc, char *argv[]) {
1849 #ifdef DISABLE_LEGACY
1854 fprintf(stderr, "Too many arguments!\n");
1859 name = get_my_name(false);
1861 #ifndef DISABLE_LEGACY
1862 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1866 if(!ed25519_keygen(true))
1872 #ifndef DISABLE_LEGACY
1873 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1875 fprintf(stderr, "Too many arguments!\n");
1880 name = get_my_name(false);
1882 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1886 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1888 fprintf(stderr, "Too many arguments!\n");
1893 name = get_my_name(false);
1895 return !ed25519_keygen(true);
1898 static int cmd_help(int argc, char *argv[]) {
1903 static int cmd_version(int argc, char *argv[]) {
1905 fprintf(stderr, "Too many arguments!\n");
1913 static int cmd_info(int argc, char *argv[]) {
1915 fprintf(stderr, "Invalid number of arguments.\n");
1919 if(!connect_tincd(true))
1922 return info(fd, argv[1]);
1925 static const char *conffiles[] = {
1936 static int cmd_edit(int argc, char *argv[]) {
1938 fprintf(stderr, "Invalid number of arguments.\n");
1942 char *filename = NULL;
1944 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1945 for(int i = 0; conffiles[i]; i++) {
1946 if(!strcmp(argv[1], conffiles[i])) {
1947 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1956 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1957 char *dash = strchr(argv[1], '-');
1960 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1961 fprintf(stderr, "Invalid configuration filename.\n");
1969 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1971 xasprintf(&command, "edit \"%s\"", filename);
1973 int result = system(command);
1977 // Silently try notifying a running tincd of changes.
1978 if(connect_tincd(false))
1979 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1984 static int export(const char *name, FILE *out) {
1986 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1987 FILE *in = fopen(filename, "r");
1989 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1993 fprintf(out, "Name = %s\n", name);
1995 while(fgets(buf, sizeof buf, in)) {
1996 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2001 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2010 static int cmd_export(int argc, char *argv[]) {
2012 fprintf(stderr, "Too many arguments!\n");
2016 char *name = get_my_name(true);
2020 int result = export(name, stdout);
2028 static int cmd_export_all(int argc, char *argv[]) {
2030 fprintf(stderr, "Too many arguments!\n");
2034 DIR *dir = opendir(hosts_dir);
2036 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2044 while((ent = readdir(dir))) {
2045 if(!check_id(ent->d_name))
2051 printf("#---------------------------------------------------------------#\n");
2053 result |= export(ent->d_name, stdout);
2062 static int cmd_import(int argc, char *argv[]) {
2064 fprintf(stderr, "Too many arguments!\n");
2073 char *filename = NULL;
2075 bool firstline = true;
2077 while(fgets(buf, sizeof buf, in)) {
2078 if(sscanf(buf, "Name = %s", name) == 1) {
2081 if(!check_id(name)) {
2082 fprintf(stderr, "Invalid Name in input!\n");
2090 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2092 if(!force && !access(filename, F_OK)) {
2093 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2098 out = fopen(filename, "w");
2100 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2106 } else if(firstline) {
2107 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2112 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2116 if(fputs(buf, out) < 0) {
2117 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2127 fprintf(stderr, "Imported %d host configuration files.\n", count);
2130 fprintf(stderr, "No host configuration files imported.\n");
2135 static int cmd_exchange(int argc, char *argv[]) {
2136 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2139 static int cmd_exchange_all(int argc, char *argv[]) {
2140 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2143 static int switch_network(char *name) {
2155 free(unixsocketname);
2156 unixsocketname = NULL;
2162 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2164 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2165 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2166 xasprintf(&prompt, "%s> ", identname);
2171 static int cmd_network(int argc, char *argv[]) {
2173 fprintf(stderr, "Too many arguments!\n");
2178 return switch_network(argv[1]);
2180 DIR *dir = opendir(confdir);
2182 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2187 while((ent = readdir(dir))) {
2188 if(*ent->d_name == '.')
2191 if(!strcmp(ent->d_name, "tinc.conf")) {
2197 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2198 if(!access(fname, R_OK))
2199 printf("%s\n", ent->d_name);
2208 static int cmd_fsck(int argc, char *argv[]) {
2210 fprintf(stderr, "Too many arguments!\n");
2214 return fsck(orig_argv[0]);
2217 static const struct {
2218 const char *command;
2219 int (*function)(int argc, char *argv[]);
2222 {"start", cmd_start},
2224 {"restart", cmd_restart},
2225 {"reload", cmd_reload},
2227 {"purge", cmd_purge},
2228 {"debug", cmd_debug},
2229 {"retry", cmd_retry},
2230 {"connect", cmd_connect},
2231 {"disconnect", cmd_disconnect},
2236 {"config", cmd_config, true},
2237 {"add", cmd_config},
2238 {"del", cmd_config},
2239 {"get", cmd_config},
2240 {"set", cmd_config},
2242 {"generate-keys", cmd_generate_keys},
2243 #ifndef DISABLE_LEGACY
2244 {"generate-rsa-keys", cmd_generate_rsa_keys},
2246 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2248 {"version", cmd_version},
2251 {"export", cmd_export},
2252 {"export-all", cmd_export_all},
2253 {"import", cmd_import},
2254 {"exchange", cmd_exchange},
2255 {"exchange-all", cmd_exchange_all},
2256 {"invite", cmd_invite},
2258 {"network", cmd_network},
2263 #ifdef HAVE_READLINE
2264 static char *complete_command(const char *text, int state) {
2272 while(commands[i].command) {
2273 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2274 return xstrdup(commands[i].command);
2281 static char *complete_dump(const char *text, int state) {
2282 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2291 if(!strncasecmp(matches[i], text, strlen(text)))
2292 return xstrdup(matches[i]);
2299 static char *complete_config(const char *text, int state) {
2307 while(variables[i].name) {
2308 char *dot = strchr(text, '.');
2310 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2312 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2316 if(!strncasecmp(variables[i].name, text, strlen(text)))
2317 return xstrdup(variables[i].name);
2325 static char *complete_info(const char *text, int state) {
2329 if(!connect_tincd(false))
2331 // Check the list of nodes
2332 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2333 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2336 while(recvline(fd, line, sizeof line)) {
2338 int n = sscanf(line, "%d %d %s", &code, &req, item);
2348 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2352 if(!strncmp(item, text, strlen(text)))
2353 return xstrdup(strip_weight(item));
2359 static char *complete_nothing(const char *text, int state) {
2363 static char **completion (const char *text, int start, int end) {
2364 char **matches = NULL;
2367 matches = rl_completion_matches(text, complete_command);
2368 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2369 matches = rl_completion_matches(text, complete_dump);
2370 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2371 matches = rl_completion_matches(text, complete_config);
2372 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2373 matches = rl_completion_matches(text, complete_config);
2374 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2375 matches = rl_completion_matches(text, complete_config);
2376 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2377 matches = rl_completion_matches(text, complete_config);
2378 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2379 matches = rl_completion_matches(text, complete_info);
2385 static int cmd_shell(int argc, char *argv[]) {
2386 xasprintf(&prompt, "%s> ", identname);
2390 int maxargs = argc + 16;
2391 char **nargv = xmalloc(maxargs * sizeof *nargv);
2393 for(int i = 0; i < argc; i++)
2396 #ifdef HAVE_READLINE
2397 rl_readline_name = "tinc";
2398 rl_completion_entry_function = complete_nothing;
2399 rl_attempted_completion_function = completion;
2400 rl_filename_completion_desired = 0;
2405 #ifdef HAVE_READLINE
2409 rl_basic_word_break_characters = "\t\n ";
2410 line = readline(prompt);
2412 copy = xstrdup(line);
2414 line = fgets(buf, sizeof buf, stdin);
2418 fputs(prompt, stdout);
2420 line = fgets(buf, sizeof buf, stdin);
2426 /* Ignore comments */
2434 char *p = line + strspn(line, " \t\n");
2435 char *next = strtok(p, " \t\n");
2438 if(nargc >= maxargs) {
2439 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2442 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2447 next = strtok(NULL, " \t\n");
2453 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2458 for(int i = 0; commands[i].command; i++) {
2459 if(!strcasecmp(nargv[argc], commands[i].command)) {
2460 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2466 #ifdef HAVE_READLINE
2472 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2485 int main(int argc, char *argv[]) {
2486 program_name = argv[0];
2489 tty = isatty(0) && isatty(1);
2491 if(!parse_options(argc, argv))
2495 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2496 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2509 static struct WSAData wsa_state;
2511 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2512 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2521 return cmd_shell(argc, argv);
2523 for(int i = 0; commands[i].command; i++) {
2524 if(!strcasecmp(argv[optind], commands[i].command))
2525 return commands[i].function(argc - optind, argv + optind);
2528 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);