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"
35 #include "invitation.h"
44 #define MSG_NOSIGNAL 0
47 static char **orig_argv;
50 /* If nonzero, display usage information and exit. */
51 static bool show_help = false;
53 /* If nonzero, print the version on standard output and exit. */
54 static bool show_version = false;
56 static char *name = NULL;
57 static char controlcookie[1025];
58 char *tinc_conf = NULL;
59 char *hosts_dir = NULL;
62 // Horrible global variables...
69 static bool force = false;
71 bool confbasegiven = false;
72 bool netnamegiven = false;
73 char *scriptinterpreter = NULL;
74 char *scriptextension = "";
77 static struct option const long_options[] = {
78 {"batch", no_argument, NULL, 'b'},
79 {"config", required_argument, NULL, 'c'},
80 {"net", required_argument, NULL, 'n'},
81 {"help", no_argument, NULL, 1},
82 {"version", no_argument, NULL, 2},
83 {"pidfile", required_argument, NULL, 3},
84 {"force", no_argument, NULL, 4},
88 static void version(void) {
89 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
90 VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
91 printf("Copyright (C) 1998-2014 Ivo Timmermans, Guus Sliepen and others.\n"
92 "See the AUTHORS file for a complete list.\n\n"
93 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
94 "and you are welcome to redistribute it under certain conditions;\n"
95 "see the file COPYING for details.\n");
98 static void usage(bool status) {
100 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
102 printf("Usage: %s [options] command\n\n", program_name);
103 printf("Valid options are:\n"
104 " -b, --batch Don't ask for anything (non-interactive mode).\n"
105 " -c, --config=DIR Read configuration options from DIR.\n"
106 " -n, --net=NETNAME Connect to net NETNAME.\n"
107 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
108 " --help Display this help and exit.\n"
109 " --version Output version information and exit.\n"
111 "Valid commands are:\n"
112 " init [name] Create initial configuration files.\n"
113 " get VARIABLE Print current value of VARIABLE\n"
114 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
115 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
116 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
117 " start [tincd options] Start tincd.\n"
118 " stop Stop tincd.\n"
119 " restart [tincd options] Restart tincd.\n"
120 " reload Partially reload configuration of running tincd.\n"
121 " pid Show PID of currently running tincd.\n"
122 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
123 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
124 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
125 " dump Dump a list of one of the following things:\n"
126 " [reachable] nodes - all known nodes in the VPN\n"
127 " edges - all known connections in the VPN\n"
128 " subnets - all known subnets in the VPN\n"
129 " connections - all meta connections with ourself\n"
130 " [di]graph - graph of the VPN in dotty format\n"
131 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
132 " purge Purge unreachable nodes\n"
133 " debug N Set debug level\n"
134 " retry Retry all outgoing connections\n"
135 " disconnect NODE Close meta connection with NODE\n"
137 " top Show real-time statistics\n"
139 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
140 " log [level] Dump log output [up to the specified level]\n"
141 " export Export host configuration of local node to standard output\n"
142 " export-all Export all host configuration files to standard output\n"
143 " import [--force] Import host configuration file(s) from standard input\n"
144 " exchange [--force] Same as export followed by import\n"
145 " exchange-all [--force] Same as export-all followed by import\n"
146 " invite NODE [...] Generate an invitation for NODE\n"
147 " join INVITATION Join a VPN using an INVITIATION\n"
148 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
150 printf("Report bugs to tinc@tinc-vpn.org.\n");
154 static bool parse_options(int argc, char **argv) {
156 int option_index = 0;
158 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
160 case 0: /* long option */
167 case 'c': /* config file */
168 confbase = xstrdup(optarg);
169 confbasegiven = true;
172 case 'n': /* net name given */
173 netname = xstrdup(optarg);
176 case 1: /* show help */
180 case 2: /* show version */
184 case 3: /* open control socket here */
185 pidfilename = xstrdup(optarg);
192 case '?': /* wrong options */
201 if(!netname && (netname = getenv("NETNAME")))
202 netname = xstrdup(netname);
204 /* netname "." is special: a "top-level name" */
206 if(netname && (!*netname || !strcmp(netname, "."))) {
211 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
212 fprintf(stderr, "Invalid character in netname!\n");
219 /* Open a file with the desired permissions, minus the umask.
220 Also, if we want to create an executable file, we call fchmod()
221 to set the executable bits. */
223 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
224 mode_t mask = umask(0);
227 FILE *f = fopen(filename, mode);
229 if((perms & 0444) && f)
230 fchmod(fileno(f), perms);
236 static void disable_old_keys(const char *filename, const char *what) {
237 char tmpfile[PATH_MAX] = "";
239 bool disabled = false;
244 r = fopen(filename, "r");
248 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
250 struct stat st = {.st_mode = 0600};
251 fstat(fileno(r), &st);
252 w = fopenmask(tmpfile, "w", st.st_mode);
254 while(fgets(buf, sizeof buf, r)) {
255 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
256 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
262 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
268 if(block || ed25519pubkey)
270 if(fputs(buf, w) < 0) {
276 if(block && !strncmp(buf, "-----END ", 9))
283 if(ferror(r) || fclose(r) < 0)
288 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
295 // We cannot atomically replace files on Windows.
296 char bakfile[PATH_MAX] = "";
297 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
298 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
299 rename(bakfile, filename);
301 if(rename(tmpfile, filename)) {
303 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
308 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
315 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
321 /* Check stdin and stdout */
323 /* Ask for a file and/or directory name. */
324 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
326 if(fgets(buf, sizeof buf, stdin) == NULL) {
327 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
331 size_t len = strlen(buf);
340 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
342 if(filename[0] != '/') {
344 /* The directory is a relative path or a filename. */
345 directory = get_current_dir_name();
346 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
350 disable_old_keys(filename, what);
352 /* Open it first to keep the inode busy */
354 r = fopenmask(filename, mode, perms);
357 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
365 Generate a public/private Ed25519 keypair, and ask for a file to store
368 static bool ed25519_keygen(bool ask) {
371 char *pubname, *privname;
373 fprintf(stderr, "Generating Ed25519 keypair:\n");
375 if(!(key = ecdsa_generate())) {
376 fprintf(stderr, "Error during key generation!\n");
379 fprintf(stderr, "Done.\n");
381 xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
382 f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
388 if(!ecdsa_write_pem_private_key(key, f)) {
389 fprintf(stderr, "Error writing private key!\n");
398 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
400 xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
402 f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
408 char *pubkey = ecdsa_get_base64_public_key(key);
409 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
419 Generate a public/private RSA keypair, and ask for a file to store
422 static bool rsa_keygen(int bits, bool ask) {
425 char *pubname, *privname;
427 // Make sure the key size is a multiple of 8 bits.
430 // Force them to be between 1024 and 8192 bits long.
436 fprintf(stderr, "Generating %d bits keys:\n", bits);
438 if(!(key = rsa_generate(bits, 0x10001))) {
439 fprintf(stderr, "Error during key generation!\n");
442 fprintf(stderr, "Done.\n");
444 xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
445 f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
451 if(!rsa_write_pem_private_key(key, f)) {
452 fprintf(stderr, "Error writing private key!\n");
461 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
463 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
465 f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
471 if(!rsa_write_pem_public_key(key, f)) {
472 fprintf(stderr, "Error writing public key!\n");
487 bool recvline(int fd, char *line, size_t len) {
488 char *newline = NULL;
493 while(!(newline = memchr(buffer, '\n', blen))) {
494 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
495 if(result == -1 && sockerrno == EINTR)
502 if(newline - buffer >= len)
505 len = newline - buffer;
507 memcpy(line, buffer, len);
509 memmove(buffer, newline + 1, blen - len - 1);
515 bool recvdata(int fd, char *data, size_t len) {
520 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
521 if(result == -1 && sockerrno == EINTR)
528 memcpy(data, buffer, len);
529 memmove(buffer, buffer + len, blen - len);
535 bool sendline(int fd, char *format, ...) {
536 static char buffer[4096];
541 va_start(ap, format);
542 blen = vsnprintf(buffer, sizeof buffer, format, ap);
545 if(blen < 1 || blen >= sizeof buffer)
552 int result = send(fd, p, blen, MSG_NOSIGNAL);
553 if(result == -1 && sockerrno == EINTR)
564 static void pcap(int fd, FILE *out, int snaplen) {
565 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
573 uint32_t tz_accuracy;
580 snaplen ?: sizeof data,
593 fwrite(&header, sizeof header, 1, out);
597 while(recvline(fd, line, sizeof line)) {
599 int n = sscanf(line, "%d %d %d", &code, &req, &len);
600 gettimeofday(&tv, NULL);
601 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
603 if(!recvdata(fd, data, len))
605 packet.tv_sec = tv.tv_sec;
606 packet.tv_usec = tv.tv_usec;
608 packet.origlen = len;
609 fwrite(&packet, sizeof packet, 1, out);
610 fwrite(data, len, 1, out);
615 static void logcontrol(int fd, FILE *out, int level) {
616 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
620 while(recvline(fd, line, sizeof line)) {
622 int n = sscanf(line, "%d %d %d", &code, &req, &len);
623 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
625 if(!recvdata(fd, data, len))
627 fwrite(data, len, 1, out);
634 static bool remove_service(void) {
635 SC_HANDLE manager = NULL;
636 SC_HANDLE service = NULL;
637 SERVICE_STATUS status = {0};
639 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
641 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
645 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
648 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
652 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
653 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
655 fprintf(stderr, "%s service stopped\n", identname);
657 if(!DeleteService(service)) {
658 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
662 fprintf(stderr, "%s service removed\n", identname);
668 bool connect_tincd(bool verbose) {
673 struct timeval tv = {0, 0};
674 if(select(fd + 1, &r, NULL, NULL, &tv)) {
675 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
683 FILE *f = fopen(pidfilename, "r");
686 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
693 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
695 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
703 struct sockaddr_un sa;
704 sa.sun_family = AF_UNIX;
705 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
707 fd = socket(AF_UNIX, SOCK_STREAM, 0);
710 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
714 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
716 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
722 struct addrinfo hints = {
723 .ai_family = AF_UNSPEC,
724 .ai_socktype = SOCK_STREAM,
725 .ai_protocol = IPPROTO_TCP,
729 struct addrinfo *res = NULL;
731 if(getaddrinfo(host, port, &hints, &res) || !res) {
733 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
737 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
740 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
745 unsigned long arg = 0;
747 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
749 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
753 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
755 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
765 static const int one = 1;
766 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
772 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
774 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
780 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
782 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
784 fprintf(stderr, "Could not fully establish control socket connection\n");
794 static int cmd_start(int argc, char *argv[]) {
795 if(connect_tincd(false)) {
797 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
799 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
804 char *slash = strrchr(program_name, '/');
807 if ((c = strrchr(program_name, '\\')) > slash)
812 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
817 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
822 Windows has no real concept of an "argv array". A command line is just one string.
823 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
824 it uses quotes to handle spaces in arguments.
825 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
826 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
827 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
829 xasprintf(&arg0, "\"%s\"", arg0);
831 nargv[nargc++] = arg0;
832 for(int i = 1; i < optind; i++)
833 nargv[nargc++] = orig_argv[i];
834 for(int i = 1; i < argc; i++)
835 nargv[nargc++] = argv[i];
838 int status = spawnvp(_P_WAIT, c, nargv);
840 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
847 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
853 exit(execvp(c, nargv));
857 int status = -1, result;
859 signal(SIGINT, SIG_IGN);
861 result = waitpid(pid, &status, 0);
863 signal(SIGINT, SIG_DFL);
866 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
867 fprintf(stderr, "Error starting %s\n", c);
875 static int cmd_stop(int argc, char *argv[]) {
877 fprintf(stderr, "Too many arguments!\n");
882 if(!connect_tincd(true)) {
884 if(kill(pid, SIGTERM)) {
885 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
889 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
890 waitpid(pid, NULL, 0);
897 sendline(fd, "%d %d", CONTROL, REQ_STOP);
899 while(recvline(fd, line, sizeof line)) {
900 // Wait for tincd to close the connection...
903 if(!remove_service())
913 static int cmd_restart(int argc, char *argv[]) {
915 return cmd_start(argc, argv);
918 static int cmd_reload(int argc, char *argv[]) {
920 fprintf(stderr, "Too many arguments!\n");
924 if(!connect_tincd(true))
927 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
928 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
929 fprintf(stderr, "Could not reload configuration.\n");
937 static int cmd_dump(int argc, char *argv[]) {
938 bool only_reachable = false;
940 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
941 if(strcasecmp(argv[2], "nodes")) {
942 fprintf(stderr, "`reachable' only supported for nodes.\n");
946 only_reachable = true;
952 fprintf(stderr, "Invalid number of arguments.\n");
957 if(!connect_tincd(true))
962 if(!strcasecmp(argv[1], "nodes"))
963 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
964 else if(!strcasecmp(argv[1], "edges"))
965 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
966 else if(!strcasecmp(argv[1], "subnets"))
967 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
968 else if(!strcasecmp(argv[1], "connections"))
969 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
970 else if(!strcasecmp(argv[1], "graph")) {
971 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
972 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
974 } else if(!strcasecmp(argv[1], "digraph")) {
975 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
976 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
979 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
986 else if(do_graph == 2)
987 printf("digraph {\n");
989 while(recvline(fd, line, sizeof line)) {
990 char node1[4096], node2[4096];
991 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
993 if(do_graph && req == REQ_DUMP_NODES)
1011 char local_host[4096];
1012 char local_port[4096];
1015 int cipher, digest, maclength, compression, distance, socket, weight;
1016 short int pmtu, minmtu, maxmtu;
1017 unsigned int options, status_int;
1018 node_status_t status;
1019 long int last_state_change;
1022 case REQ_DUMP_NODES: {
1023 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);
1025 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1029 memcpy(&status, &status_int, sizeof status);
1032 const char *color = "black";
1033 if(!strcmp(host, "MYSELF"))
1035 else if(!status.reachable)
1037 else if(strcmp(via, node))
1039 else if(!status.validkey)
1043 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1045 if(only_reachable && !status.reachable)
1047 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",
1048 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1052 case REQ_DUMP_EDGES: {
1053 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);
1055 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1060 float w = 1 + 65536.0 / weight;
1061 if(do_graph == 1 && strcmp(node1, node2) > 0)
1062 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1063 else if(do_graph == 2)
1064 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1066 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);
1070 case REQ_DUMP_SUBNETS: {
1071 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1073 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1076 printf("%s owner %s\n", strip_weight(subnet), node);
1079 case REQ_DUMP_CONNECTIONS: {
1080 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1082 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1085 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1089 fprintf(stderr, "Unable to parse dump from tincd.\n");
1094 fprintf(stderr, "Error receiving dump.\n");
1098 static int cmd_purge(int argc, char *argv[]) {
1100 fprintf(stderr, "Too many arguments!\n");
1104 if(!connect_tincd(true))
1107 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1108 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1109 fprintf(stderr, "Could not purge old information.\n");
1116 static int cmd_debug(int argc, char *argv[]) {
1118 fprintf(stderr, "Invalid number of arguments.\n");
1122 if(!connect_tincd(true))
1125 int debuglevel = atoi(argv[1]);
1128 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1129 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1130 fprintf(stderr, "Could not set debug level.\n");
1134 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1138 static int cmd_retry(int argc, char *argv[]) {
1140 fprintf(stderr, "Too many arguments!\n");
1144 if(!connect_tincd(true))
1147 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1148 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1149 fprintf(stderr, "Could not retry outgoing connections.\n");
1156 static int cmd_connect(int argc, char *argv[]) {
1158 fprintf(stderr, "Invalid number of arguments.\n");
1162 if(!check_id(argv[1])) {
1163 fprintf(stderr, "Invalid name for node.\n");
1167 if(!connect_tincd(true))
1170 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1171 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1172 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1179 static int cmd_disconnect(int argc, char *argv[]) {
1181 fprintf(stderr, "Invalid number of arguments.\n");
1185 if(!check_id(argv[1])) {
1186 fprintf(stderr, "Invalid name for node.\n");
1190 if(!connect_tincd(true))
1193 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1194 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1195 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1202 static int cmd_top(int argc, char *argv[]) {
1204 fprintf(stderr, "Too many arguments!\n");
1209 if(!connect_tincd(true))
1215 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1220 static int cmd_pcap(int argc, char *argv[]) {
1222 fprintf(stderr, "Too many arguments!\n");
1226 if(!connect_tincd(true))
1229 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1234 static void sigint_handler(int sig) {
1235 fprintf(stderr, "\n");
1236 shutdown(fd, SHUT_RDWR);
1240 static int cmd_log(int argc, char *argv[]) {
1242 fprintf(stderr, "Too many arguments!\n");
1246 if(!connect_tincd(true))
1250 signal(SIGINT, sigint_handler);
1253 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1256 signal(SIGINT, SIG_DFL);
1264 static int cmd_pid(int argc, char *argv[]) {
1266 fprintf(stderr, "Too many arguments!\n");
1270 if(!connect_tincd(true) && !pid)
1273 printf("%d\n", pid);
1277 int rstrip(char *value) {
1278 int len = strlen(value);
1279 while(len && strchr("\t\r\n ", value[len - 1]))
1284 char *get_my_name(bool verbose) {
1285 FILE *f = fopen(tinc_conf, "r");
1288 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1294 while(fgets(buf, sizeof buf, f)) {
1295 int len = strcspn(buf, "\t =");
1297 value += strspn(value, "\t ");
1300 value += strspn(value, "\t ");
1305 if(strcasecmp(buf, "Name"))
1309 return replace_name(value);
1315 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1319 const var_t variables[] = {
1320 /* Server configuration */
1321 {"AddressFamily", VAR_SERVER},
1322 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1323 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1324 {"BindToInterface", VAR_SERVER},
1325 {"Broadcast", VAR_SERVER | VAR_SAFE},
1326 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1327 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1328 {"DecrementTTL", VAR_SERVER},
1329 {"Device", VAR_SERVER},
1330 {"DeviceStandby", VAR_SERVER},
1331 {"DeviceType", VAR_SERVER},
1332 {"DirectOnly", VAR_SERVER},
1333 {"Ed25519PrivateKeyFile", VAR_SERVER},
1334 {"ExperimentalProtocol", VAR_SERVER},
1335 {"Forwarding", VAR_SERVER},
1336 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1337 {"Hostnames", VAR_SERVER},
1338 {"IffOneQueue", VAR_SERVER},
1339 {"Interface", VAR_SERVER},
1340 {"KeyExpire", VAR_SERVER},
1341 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1342 {"LocalDiscovery", VAR_SERVER},
1343 {"MACExpire", VAR_SERVER},
1344 {"MaxConnectionBurst", VAR_SERVER},
1345 {"MaxOutputBufferSize", VAR_SERVER},
1346 {"MaxTimeout", VAR_SERVER},
1347 {"Mode", VAR_SERVER | VAR_SAFE},
1348 {"Name", VAR_SERVER},
1349 {"PingInterval", VAR_SERVER},
1350 {"PingTimeout", VAR_SERVER},
1351 {"PriorityInheritance", VAR_SERVER},
1352 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1353 {"PrivateKeyFile", VAR_SERVER},
1354 {"ProcessPriority", VAR_SERVER},
1355 {"Proxy", VAR_SERVER},
1356 {"ReplayWindow", VAR_SERVER},
1357 {"ScriptsExtension", VAR_SERVER},
1358 {"ScriptsInterpreter", VAR_SERVER},
1359 {"StrictSubnets", VAR_SERVER},
1360 {"TunnelServer", VAR_SERVER},
1361 {"UDPRcvBuf", VAR_SERVER},
1362 {"UDPSndBuf", VAR_SERVER},
1363 {"VDEGroup", VAR_SERVER},
1364 {"VDEPort", VAR_SERVER},
1365 /* Host configuration */
1366 {"Address", VAR_HOST | VAR_MULTIPLE},
1367 {"Cipher", VAR_SERVER | VAR_HOST},
1368 {"ClampMSS", VAR_SERVER | VAR_HOST},
1369 {"Compression", VAR_SERVER | VAR_HOST},
1370 {"Digest", VAR_SERVER | VAR_HOST},
1371 {"Ed25519PublicKey", VAR_HOST},
1372 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1373 {"IndirectData", VAR_SERVER | VAR_HOST},
1374 {"MACLength", VAR_SERVER | VAR_HOST},
1375 {"PMTU", VAR_SERVER | VAR_HOST},
1376 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1378 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1379 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1380 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1381 {"TCPOnly", VAR_SERVER | VAR_HOST},
1382 {"Weight", VAR_HOST | VAR_SAFE},
1386 static int cmd_config(int argc, char *argv[]) {
1388 fprintf(stderr, "Invalid number of arguments.\n");
1392 if(strcasecmp(argv[0], "config"))
1396 if(!strcasecmp(argv[1], "get")) {
1398 } else if(!strcasecmp(argv[1], "add")) {
1399 argv++, argc--, action = 1;
1400 } else if(!strcasecmp(argv[1], "del")) {
1401 argv++, argc--, action = -1;
1402 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1403 argv++, argc--, action = 0;
1407 fprintf(stderr, "Invalid number of arguments.\n");
1411 // Concatenate the rest of the command line
1412 strncpy(line, argv[1], sizeof line - 1);
1413 for(int i = 2; i < argc; i++) {
1414 strncat(line, " ", sizeof line - 1 - strlen(line));
1415 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1418 // Liberal parsing into node name, variable name and value.
1424 len = strcspn(line, "\t =");
1426 value += strspn(value, "\t ");
1429 value += strspn(value, "\t ");
1432 variable = strchr(line, '.');
1441 fprintf(stderr, "No variable given.\n");
1445 if(action >= 0 && !*value) {
1446 fprintf(stderr, "No value for variable given.\n");
1450 if(action < -1 && *value)
1453 /* Some simple checks. */
1455 bool warnonremove = false;
1457 for(int i = 0; variables[i].name; i++) {
1458 if(strcasecmp(variables[i].name, variable))
1462 variable = (char *)variables[i].name;
1464 /* Discourage use of obsolete variables. */
1466 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1468 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1470 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1475 /* Don't put server variables in host config files */
1477 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1479 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1481 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1486 /* Should this go into our own host config file? */
1488 if(!node && !(variables[i].type & VAR_SERVER)) {
1489 node = get_my_name(true);
1494 /* Change "add" into "set" for variables that do not allow multiple occurences.
1495 Turn on warnings when it seems variables might be removed unintentionally. */
1497 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1498 warnonremove = true;
1500 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1501 warnonremove = true;
1507 if(node && !check_id(node)) {
1508 fprintf(stderr, "Invalid name for node.\n");
1513 if(force || action < 0) {
1514 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1516 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1521 // Open the right configuration file.
1524 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1526 filename = tinc_conf;
1528 FILE *f = fopen(filename, "r");
1530 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1534 char *tmpfile = NULL;
1538 xasprintf(&tmpfile, "%s.config.tmp", filename);
1539 tf = fopen(tmpfile, "w");
1541 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1547 // Copy the file, making modifications on the fly, unless we are just getting a value.
1551 bool removed = false;
1554 while(fgets(buf1, sizeof buf1, f)) {
1555 buf1[sizeof buf1 - 1] = 0;
1556 strncpy(buf2, buf1, sizeof buf2);
1558 // Parse line in a simple way
1562 len = strcspn(buf2, "\t =");
1563 bvalue = buf2 + len;
1564 bvalue += strspn(bvalue, "\t ");
1565 if(*bvalue == '=') {
1567 bvalue += strspn(bvalue, "\t ");
1573 if(!strcasecmp(buf2, variable)) {
1577 printf("%s\n", bvalue);
1579 } else if(action == -1) {
1580 if(!*value || !strcasecmp(bvalue, value)) {
1585 } else if(action == 0) {
1586 // Warn if "set" was used for variables that can occur multiple times
1587 if(warnonremove && strcasecmp(bvalue, value))
1588 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1590 // Already set? Delete the rest...
1594 // Otherwise, replace.
1595 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1596 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1605 // Copy original line...
1606 if(fputs(buf1, tf) < 0) {
1607 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1611 // Add newline if it is missing...
1612 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1613 if(fputc('\n', tf) < 0) {
1614 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1621 // Make sure we read everything...
1622 if(ferror(f) || !feof(f)) {
1623 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1628 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1632 // Add new variable if necessary.
1633 if(action > 0 || (action == 0 && !set)) {
1634 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1635 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1644 fprintf(stderr, "No matching configuration variables found.\n");
1649 // Make sure we wrote everything...
1651 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1655 // Could we find what we had to remove?
1656 if(action < 0 && !removed) {
1658 fprintf(stderr, "No configuration variables deleted.\n");
1662 // Replace the configuration file with the new one
1664 if(remove(filename)) {
1665 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1669 if(rename(tmpfile, filename)) {
1670 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1674 // Silently try notifying a running tincd of changes.
1675 if(connect_tincd(false))
1676 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1681 static bool try_bind(int port) {
1682 struct addrinfo *ai = NULL;
1683 struct addrinfo hint = {
1684 .ai_flags = AI_PASSIVE,
1685 .ai_family = AF_UNSPEC,
1686 .ai_socktype = SOCK_STREAM,
1687 .ai_protocol = IPPROTO_TCP,
1691 snprintf(portstr, sizeof portstr, "%d", port);
1693 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1697 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1700 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1710 int check_port(char *name) {
1714 fprintf(stderr, "Warning: could not bind to port 655. ");
1716 for(int i = 0; i < 100; i++) {
1717 int port = 0x1000 + (rand() & 0x7fff);
1718 if(try_bind(port)) {
1720 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1721 FILE *f = fopen(filename, "a");
1724 fprintf(stderr, "Please change tinc's Port manually.\n");
1728 fprintf(f, "Port = %d\n", port);
1730 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1735 fprintf(stderr, "Please change tinc's Port manually.\n");
1739 static int cmd_init(int argc, char *argv[]) {
1740 if(!access(tinc_conf, F_OK)) {
1741 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1746 fprintf(stderr, "Too many arguments!\n");
1748 } else if(argc < 2) {
1751 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1752 if(!fgets(buf, sizeof buf, stdin)) {
1753 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1756 int len = rstrip(buf);
1758 fprintf(stderr, "No name given!\n");
1763 fprintf(stderr, "No Name given!\n");
1767 name = strdup(argv[1]);
1769 fprintf(stderr, "No Name given!\n");
1774 if(!check_id(name)) {
1775 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1779 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1780 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1784 if(mkdir(confbase, 0777) && errno != EEXIST) {
1785 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1789 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1790 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1794 FILE *f = fopen(tinc_conf, "w");
1796 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1800 fprintf(f, "Name = %s\n", name);
1803 if(!rsa_keygen(2048, false) || !ed25519_keygen(false))
1810 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1811 if(access(filename, F_OK)) {
1812 FILE *f = fopenmask(filename, "w", 0777);
1814 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1817 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");
1826 static int cmd_generate_keys(int argc, char *argv[]) {
1828 fprintf(stderr, "Too many arguments!\n");
1833 name = get_my_name(false);
1835 return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ed25519_keygen(true));
1838 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1840 fprintf(stderr, "Too many arguments!\n");
1845 name = get_my_name(false);
1847 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1850 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1852 fprintf(stderr, "Too many arguments!\n");
1857 name = get_my_name(false);
1859 return !ed25519_keygen(true);
1862 static int cmd_help(int argc, char *argv[]) {
1867 static int cmd_version(int argc, char *argv[]) {
1869 fprintf(stderr, "Too many arguments!\n");
1877 static int cmd_info(int argc, char *argv[]) {
1879 fprintf(stderr, "Invalid number of arguments.\n");
1883 if(!connect_tincd(true))
1886 return info(fd, argv[1]);
1889 static const char *conffiles[] = {
1900 static int cmd_edit(int argc, char *argv[]) {
1902 fprintf(stderr, "Invalid number of arguments.\n");
1906 char *filename = NULL;
1908 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1909 for(int i = 0; conffiles[i]; i++) {
1910 if(!strcmp(argv[1], conffiles[i])) {
1911 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1920 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1921 char *dash = strchr(argv[1], '-');
1924 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1925 fprintf(stderr, "Invalid configuration filename.\n");
1933 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1935 xasprintf(&command, "edit \"%s\"", filename);
1937 int result = system(command);
1941 // Silently try notifying a running tincd of changes.
1942 if(connect_tincd(false))
1943 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1948 static int export(const char *name, FILE *out) {
1950 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1951 FILE *in = fopen(filename, "r");
1953 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1957 fprintf(out, "Name = %s\n", name);
1959 while(fgets(buf, sizeof buf, in)) {
1960 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
1965 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
1974 static int cmd_export(int argc, char *argv[]) {
1976 fprintf(stderr, "Too many arguments!\n");
1980 char *name = get_my_name(true);
1984 int result = export(name, stdout);
1992 static int cmd_export_all(int argc, char *argv[]) {
1994 fprintf(stderr, "Too many arguments!\n");
1998 DIR *dir = opendir(hosts_dir);
2000 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2008 while((ent = readdir(dir))) {
2009 if(!check_id(ent->d_name))
2015 printf("#---------------------------------------------------------------#\n");
2017 result |= export(ent->d_name, stdout);
2026 static int cmd_import(int argc, char *argv[]) {
2028 fprintf(stderr, "Too many arguments!\n");
2037 char *filename = NULL;
2039 bool firstline = true;
2041 while(fgets(buf, sizeof buf, in)) {
2042 if(sscanf(buf, "Name = %s", name) == 1) {
2045 if(!check_id(name)) {
2046 fprintf(stderr, "Invalid Name in input!\n");
2054 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2056 if(!force && !access(filename, F_OK)) {
2057 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2062 out = fopen(filename, "w");
2064 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2070 } else if(firstline) {
2071 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2076 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2080 if(fputs(buf, out) < 0) {
2081 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2091 fprintf(stderr, "Imported %d host configuration files.\n", count);
2094 fprintf(stderr, "No host configuration files imported.\n");
2099 static int cmd_exchange(int argc, char *argv[]) {
2100 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2103 static int cmd_exchange_all(int argc, char *argv[]) {
2104 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2107 static int switch_network(char *name) {
2119 free(unixsocketname);
2120 unixsocketname = NULL;
2126 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2129 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2130 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2131 xasprintf(&prompt, "%s> ", identname);
2136 static int cmd_network(int argc, char *argv[]) {
2138 fprintf(stderr, "Too many arguments!\n");
2143 return switch_network(argv[1]);
2145 DIR *dir = opendir(confdir);
2147 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2152 while((ent = readdir(dir))) {
2153 if(*ent->d_name == '.')
2156 if(!strcmp(ent->d_name, "tinc.conf")) {
2162 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2163 if(!access(fname, R_OK))
2164 printf("%s\n", ent->d_name);
2173 static const struct {
2174 const char *command;
2175 int (*function)(int argc, char *argv[]);
2178 {"start", cmd_start},
2180 {"restart", cmd_restart},
2181 {"reload", cmd_reload},
2183 {"purge", cmd_purge},
2184 {"debug", cmd_debug},
2185 {"retry", cmd_retry},
2186 {"connect", cmd_connect},
2187 {"disconnect", cmd_disconnect},
2192 {"config", cmd_config, true},
2193 {"add", cmd_config},
2194 {"del", cmd_config},
2195 {"get", cmd_config},
2196 {"set", cmd_config},
2198 {"generate-keys", cmd_generate_keys},
2199 {"generate-rsa-keys", cmd_generate_rsa_keys},
2200 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2202 {"version", cmd_version},
2205 {"export", cmd_export},
2206 {"export-all", cmd_export_all},
2207 {"import", cmd_import},
2208 {"exchange", cmd_exchange},
2209 {"exchange-all", cmd_exchange_all},
2210 {"invite", cmd_invite},
2212 {"network", cmd_network},
2216 #ifdef HAVE_READLINE
2217 static char *complete_command(const char *text, int state) {
2225 while(commands[i].command) {
2226 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2227 return xstrdup(commands[i].command);
2234 static char *complete_dump(const char *text, int state) {
2235 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2244 if(!strncasecmp(matches[i], text, strlen(text)))
2245 return xstrdup(matches[i]);
2252 static char *complete_config(const char *text, int state) {
2260 while(variables[i].name) {
2261 char *dot = strchr(text, '.');
2263 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2265 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2269 if(!strncasecmp(variables[i].name, text, strlen(text)))
2270 return xstrdup(variables[i].name);
2278 static char *complete_info(const char *text, int state) {
2282 if(!connect_tincd(false))
2284 // Check the list of nodes
2285 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2286 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2289 while(recvline(fd, line, sizeof line)) {
2291 int n = sscanf(line, "%d %d %s", &code, &req, item);
2301 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2305 if(!strncmp(item, text, strlen(text)))
2306 return xstrdup(strip_weight(item));
2312 static char *complete_nothing(const char *text, int state) {
2316 static char **completion (const char *text, int start, int end) {
2317 char **matches = NULL;
2320 matches = rl_completion_matches(text, complete_command);
2321 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2322 matches = rl_completion_matches(text, complete_dump);
2323 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2324 matches = rl_completion_matches(text, complete_config);
2325 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2326 matches = rl_completion_matches(text, complete_config);
2327 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2328 matches = rl_completion_matches(text, complete_config);
2329 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2330 matches = rl_completion_matches(text, complete_config);
2331 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2332 matches = rl_completion_matches(text, complete_info);
2338 static int cmd_shell(int argc, char *argv[]) {
2339 xasprintf(&prompt, "%s> ", identname);
2343 int maxargs = argc + 16;
2344 char **nargv = xmalloc(maxargs * sizeof *nargv);
2346 for(int i = 0; i < argc; i++)
2349 #ifdef HAVE_READLINE
2350 rl_readline_name = "tinc";
2351 rl_completion_entry_function = complete_nothing;
2352 rl_attempted_completion_function = completion;
2353 rl_filename_completion_desired = 0;
2358 #ifdef HAVE_READLINE
2362 rl_basic_word_break_characters = "\t\n ";
2363 line = readline(prompt);
2365 copy = xstrdup(line);
2367 line = fgets(buf, sizeof buf, stdin);
2371 fputs(prompt, stdout);
2373 line = fgets(buf, sizeof buf, stdin);
2379 /* Ignore comments */
2387 char *p = line + strspn(line, " \t\n");
2388 char *next = strtok(p, " \t\n");
2391 if(nargc >= maxargs) {
2392 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2395 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2400 next = strtok(NULL, " \t\n");
2406 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2411 for(int i = 0; commands[i].command; i++) {
2412 if(!strcasecmp(nargv[argc], commands[i].command)) {
2413 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2419 #ifdef HAVE_READLINE
2425 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2438 int main(int argc, char *argv[]) {
2439 program_name = argv[0];
2442 tty = isatty(0) && isatty(1);
2444 if(!parse_options(argc, argv))
2448 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2449 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2462 static struct WSAData wsa_state;
2464 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2465 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2474 return cmd_shell(argc, argv);
2476 for(int i = 0; commands[i].command; i++) {
2477 if(!strcasecmp(argv[optind], commands[i].command))
2478 return commands[i].function(argc - optind, argv + optind);
2481 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);