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-2012 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)
1010 char local_host[4096];
1011 char local_port[4096];
1014 int cipher, digest, maclength, compression, distance, socket, weight;
1015 short int pmtu, minmtu, maxmtu;
1016 unsigned int options, status_int;
1017 node_status_t status;
1018 long int last_state_change;
1021 case REQ_DUMP_NODES: {
1022 int n = sscanf(line, "%*d %*d %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
1024 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1028 memcpy(&status, &status_int, sizeof status);
1031 const char *color = "black";
1032 if(!strcmp(host, "MYSELF"))
1034 else if(!status.reachable)
1036 else if(strcmp(via, node))
1038 else if(!status.validkey)
1042 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1044 if(only_reachable && !status.reachable)
1046 printf("%s at %s port %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s distance %d pmtu %hd (min %hd max %hd)\n",
1047 node, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1051 case REQ_DUMP_EDGES: {
1052 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);
1054 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1059 float w = 1 + 65536.0 / weight;
1060 if(do_graph == 1 && strcmp(node1, node2) > 0)
1061 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1062 else if(do_graph == 2)
1063 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1065 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);
1069 case REQ_DUMP_SUBNETS: {
1070 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1072 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1075 printf("%s owner %s\n", strip_weight(subnet), node);
1078 case REQ_DUMP_CONNECTIONS: {
1079 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1081 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1084 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1088 fprintf(stderr, "Unable to parse dump from tincd.\n");
1093 fprintf(stderr, "Error receiving dump.\n");
1097 static int cmd_purge(int argc, char *argv[]) {
1099 fprintf(stderr, "Too many arguments!\n");
1103 if(!connect_tincd(true))
1106 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1107 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1108 fprintf(stderr, "Could not purge old information.\n");
1115 static int cmd_debug(int argc, char *argv[]) {
1117 fprintf(stderr, "Invalid number of arguments.\n");
1121 if(!connect_tincd(true))
1124 int debuglevel = atoi(argv[1]);
1127 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1128 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1129 fprintf(stderr, "Could not set debug level.\n");
1133 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1137 static int cmd_retry(int argc, char *argv[]) {
1139 fprintf(stderr, "Too many arguments!\n");
1143 if(!connect_tincd(true))
1146 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1147 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1148 fprintf(stderr, "Could not retry outgoing connections.\n");
1155 static int cmd_connect(int argc, char *argv[]) {
1157 fprintf(stderr, "Invalid number of arguments.\n");
1161 if(!check_id(argv[1])) {
1162 fprintf(stderr, "Invalid name for node.\n");
1166 if(!connect_tincd(true))
1169 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1170 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1171 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1178 static int cmd_disconnect(int argc, char *argv[]) {
1180 fprintf(stderr, "Invalid number of arguments.\n");
1184 if(!check_id(argv[1])) {
1185 fprintf(stderr, "Invalid name for node.\n");
1189 if(!connect_tincd(true))
1192 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1193 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1194 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1201 static int cmd_top(int argc, char *argv[]) {
1203 fprintf(stderr, "Too many arguments!\n");
1208 if(!connect_tincd(true))
1214 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1219 static int cmd_pcap(int argc, char *argv[]) {
1221 fprintf(stderr, "Too many arguments!\n");
1225 if(!connect_tincd(true))
1228 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1233 static void sigint_handler(int sig) {
1234 fprintf(stderr, "\n");
1235 shutdown(fd, SHUT_RDWR);
1239 static int cmd_log(int argc, char *argv[]) {
1241 fprintf(stderr, "Too many arguments!\n");
1245 if(!connect_tincd(true))
1249 signal(SIGINT, sigint_handler);
1252 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1255 signal(SIGINT, SIG_DFL);
1263 static int cmd_pid(int argc, char *argv[]) {
1265 fprintf(stderr, "Too many arguments!\n");
1269 if(!connect_tincd(true) && !pid)
1272 printf("%d\n", pid);
1276 int rstrip(char *value) {
1277 int len = strlen(value);
1278 while(len && strchr("\t\r\n ", value[len - 1]))
1283 char *get_my_name(bool verbose) {
1284 FILE *f = fopen(tinc_conf, "r");
1287 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1293 while(fgets(buf, sizeof buf, f)) {
1294 int len = strcspn(buf, "\t =");
1296 value += strspn(value, "\t ");
1299 value += strspn(value, "\t ");
1304 if(strcasecmp(buf, "Name"))
1308 return replace_name(value);
1314 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1318 const var_t variables[] = {
1319 /* Server configuration */
1320 {"AddressFamily", VAR_SERVER},
1321 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1322 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1323 {"BindToInterface", VAR_SERVER},
1324 {"Broadcast", VAR_SERVER | VAR_SAFE},
1325 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1326 {"DecrementTTL", VAR_SERVER},
1327 {"Device", VAR_SERVER},
1328 {"DeviceType", VAR_SERVER},
1329 {"DirectOnly", VAR_SERVER},
1330 {"Ed25519PrivateKeyFile", VAR_SERVER},
1331 {"ExperimentalProtocol", VAR_SERVER},
1332 {"Forwarding", VAR_SERVER},
1333 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1334 {"Hostnames", VAR_SERVER},
1335 {"IffOneQueue", VAR_SERVER},
1336 {"Interface", VAR_SERVER},
1337 {"KeyExpire", VAR_SERVER},
1338 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1339 {"LocalDiscovery", VAR_SERVER},
1340 {"MACExpire", VAR_SERVER},
1341 {"MaxConnectionBurst", VAR_SERVER},
1342 {"MaxOutputBufferSize", VAR_SERVER},
1343 {"MaxTimeout", VAR_SERVER},
1344 {"Mode", VAR_SERVER | VAR_SAFE},
1345 {"Name", VAR_SERVER},
1346 {"PingInterval", VAR_SERVER},
1347 {"PingTimeout", VAR_SERVER},
1348 {"PriorityInheritance", VAR_SERVER},
1349 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1350 {"PrivateKeyFile", VAR_SERVER},
1351 {"ProcessPriority", VAR_SERVER},
1352 {"Proxy", VAR_SERVER},
1353 {"ReplayWindow", VAR_SERVER},
1354 {"ScriptsExtension", VAR_SERVER},
1355 {"ScriptsInterpreter", VAR_SERVER},
1356 {"StrictSubnets", VAR_SERVER},
1357 {"TunnelServer", VAR_SERVER},
1358 {"UDPRcvBuf", VAR_SERVER},
1359 {"UDPSndBuf", VAR_SERVER},
1360 {"VDEGroup", VAR_SERVER},
1361 {"VDEPort", VAR_SERVER},
1362 /* Host configuration */
1363 {"Address", VAR_HOST | VAR_MULTIPLE},
1364 {"Cipher", VAR_SERVER | VAR_HOST},
1365 {"ClampMSS", VAR_SERVER | VAR_HOST},
1366 {"Compression", VAR_SERVER | VAR_HOST},
1367 {"Digest", VAR_SERVER | VAR_HOST},
1368 {"Ed25519PublicKey", VAR_HOST},
1369 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1370 {"IndirectData", VAR_SERVER | VAR_HOST},
1371 {"MACLength", VAR_SERVER | VAR_HOST},
1372 {"PMTU", VAR_SERVER | VAR_HOST},
1373 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1375 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1376 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1377 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1378 {"TCPOnly", VAR_SERVER | VAR_HOST},
1379 {"Weight", VAR_HOST | VAR_SAFE},
1383 static int cmd_config(int argc, char *argv[]) {
1385 fprintf(stderr, "Invalid number of arguments.\n");
1389 if(strcasecmp(argv[0], "config"))
1393 if(!strcasecmp(argv[1], "get")) {
1395 } else if(!strcasecmp(argv[1], "add")) {
1396 argv++, argc--, action = 1;
1397 } else if(!strcasecmp(argv[1], "del")) {
1398 argv++, argc--, action = -1;
1399 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1400 argv++, argc--, action = 0;
1404 fprintf(stderr, "Invalid number of arguments.\n");
1408 // Concatenate the rest of the command line
1409 strncpy(line, argv[1], sizeof line - 1);
1410 for(int i = 2; i < argc; i++) {
1411 strncat(line, " ", sizeof line - 1 - strlen(line));
1412 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1415 // Liberal parsing into node name, variable name and value.
1421 len = strcspn(line, "\t =");
1423 value += strspn(value, "\t ");
1426 value += strspn(value, "\t ");
1429 variable = strchr(line, '.');
1438 fprintf(stderr, "No variable given.\n");
1442 if(action >= 0 && !*value) {
1443 fprintf(stderr, "No value for variable given.\n");
1447 if(action < -1 && *value)
1450 /* Some simple checks. */
1452 bool warnonremove = false;
1454 for(int i = 0; variables[i].name; i++) {
1455 if(strcasecmp(variables[i].name, variable))
1459 variable = (char *)variables[i].name;
1461 /* Discourage use of obsolete variables. */
1463 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1465 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1467 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1472 /* Don't put server variables in host config files */
1474 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1476 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1478 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1483 /* Should this go into our own host config file? */
1485 if(!node && !(variables[i].type & VAR_SERVER)) {
1486 node = get_my_name(true);
1491 /* Change "add" into "set" for variables that do not allow multiple occurences.
1492 Turn on warnings when it seems variables might be removed unintentionally. */
1494 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1495 warnonremove = true;
1497 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1498 warnonremove = true;
1504 if(node && !check_id(node)) {
1505 fprintf(stderr, "Invalid name for node.\n");
1510 if(force || action < 0) {
1511 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1513 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1518 // Open the right configuration file.
1521 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1523 filename = tinc_conf;
1525 FILE *f = fopen(filename, "r");
1527 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1531 char *tmpfile = NULL;
1535 xasprintf(&tmpfile, "%s.config.tmp", filename);
1536 tf = fopen(tmpfile, "w");
1538 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1544 // Copy the file, making modifications on the fly, unless we are just getting a value.
1548 bool removed = false;
1551 while(fgets(buf1, sizeof buf1, f)) {
1552 buf1[sizeof buf1 - 1] = 0;
1553 strncpy(buf2, buf1, sizeof buf2);
1555 // Parse line in a simple way
1559 len = strcspn(buf2, "\t =");
1560 bvalue = buf2 + len;
1561 bvalue += strspn(bvalue, "\t ");
1562 if(*bvalue == '=') {
1564 bvalue += strspn(bvalue, "\t ");
1570 if(!strcasecmp(buf2, variable)) {
1574 printf("%s\n", bvalue);
1576 } else if(action == -1) {
1577 if(!*value || !strcasecmp(bvalue, value)) {
1582 } else if(action == 0) {
1583 // Warn if "set" was used for variables that can occur multiple times
1584 if(warnonremove && strcasecmp(bvalue, value))
1585 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1587 // Already set? Delete the rest...
1591 // Otherwise, replace.
1592 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1593 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1602 // Copy original line...
1603 if(fputs(buf1, tf) < 0) {
1604 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1608 // Add newline if it is missing...
1609 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1610 if(fputc('\n', tf) < 0) {
1611 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1618 // Make sure we read everything...
1619 if(ferror(f) || !feof(f)) {
1620 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1625 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1629 // Add new variable if necessary.
1630 if(action > 0 || (action == 0 && !set)) {
1631 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1632 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1641 fprintf(stderr, "No matching configuration variables found.\n");
1646 // Make sure we wrote everything...
1648 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1652 // Could we find what we had to remove?
1653 if(action < 0 && !removed) {
1655 fprintf(stderr, "No configuration variables deleted.\n");
1659 // Replace the configuration file with the new one
1661 if(remove(filename)) {
1662 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1666 if(rename(tmpfile, filename)) {
1667 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1671 // Silently try notifying a running tincd of changes.
1672 if(connect_tincd(false))
1673 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1678 static bool try_bind(int port) {
1679 struct addrinfo *ai = NULL;
1680 struct addrinfo hint = {
1681 .ai_flags = AI_PASSIVE,
1682 .ai_family = AF_UNSPEC,
1683 .ai_socktype = SOCK_STREAM,
1684 .ai_protocol = IPPROTO_TCP,
1688 snprintf(portstr, sizeof portstr, "%d", port);
1690 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1694 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1697 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1707 int check_port(char *name) {
1711 fprintf(stderr, "Warning: could not bind to port 655. ");
1713 for(int i = 0; i < 100; i++) {
1714 int port = 0x1000 + (rand() & 0x7fff);
1715 if(try_bind(port)) {
1717 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1718 FILE *f = fopen(filename, "a");
1721 fprintf(stderr, "Please change tinc's Port manually.\n");
1725 fprintf(f, "Port = %d\n", port);
1727 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1732 fprintf(stderr, "Please change tinc's Port manually.\n");
1736 static int cmd_init(int argc, char *argv[]) {
1737 if(!access(tinc_conf, F_OK)) {
1738 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1743 fprintf(stderr, "Too many arguments!\n");
1745 } else if(argc < 2) {
1748 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1749 if(!fgets(buf, sizeof buf, stdin)) {
1750 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1753 int len = rstrip(buf);
1755 fprintf(stderr, "No name given!\n");
1760 fprintf(stderr, "No Name given!\n");
1764 name = strdup(argv[1]);
1766 fprintf(stderr, "No Name given!\n");
1771 if(!check_id(name)) {
1772 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1776 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1777 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1781 if(mkdir(confbase, 0777) && errno != EEXIST) {
1782 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1786 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1787 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1791 FILE *f = fopen(tinc_conf, "w");
1793 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1797 fprintf(f, "Name = %s\n", name);
1800 if(!rsa_keygen(2048, false) || !ed25519_keygen(false))
1807 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1808 if(access(filename, F_OK)) {
1809 FILE *f = fopenmask(filename, "w", 0777);
1811 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1814 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");
1823 static int cmd_generate_keys(int argc, char *argv[]) {
1825 fprintf(stderr, "Too many arguments!\n");
1830 name = get_my_name(false);
1832 return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ed25519_keygen(true));
1835 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1837 fprintf(stderr, "Too many arguments!\n");
1842 name = get_my_name(false);
1844 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1847 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1849 fprintf(stderr, "Too many arguments!\n");
1854 name = get_my_name(false);
1856 return !ed25519_keygen(true);
1859 static int cmd_help(int argc, char *argv[]) {
1864 static int cmd_version(int argc, char *argv[]) {
1866 fprintf(stderr, "Too many arguments!\n");
1874 static int cmd_info(int argc, char *argv[]) {
1876 fprintf(stderr, "Invalid number of arguments.\n");
1880 if(!connect_tincd(true))
1883 return info(fd, argv[1]);
1886 static const char *conffiles[] = {
1897 static int cmd_edit(int argc, char *argv[]) {
1899 fprintf(stderr, "Invalid number of arguments.\n");
1903 char *filename = NULL;
1905 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1906 for(int i = 0; conffiles[i]; i++) {
1907 if(!strcmp(argv[1], conffiles[i])) {
1908 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1917 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1918 char *dash = strchr(argv[1], '-');
1921 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1922 fprintf(stderr, "Invalid configuration filename.\n");
1930 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1932 xasprintf(&command, "edit \"%s\"", filename);
1934 int result = system(command);
1938 // Silently try notifying a running tincd of changes.
1939 if(connect_tincd(false))
1940 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1945 static int export(const char *name, FILE *out) {
1947 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1948 FILE *in = fopen(filename, "r");
1950 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1954 fprintf(out, "Name = %s\n", name);
1956 while(fgets(buf, sizeof buf, in)) {
1957 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
1962 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
1971 static int cmd_export(int argc, char *argv[]) {
1973 fprintf(stderr, "Too many arguments!\n");
1977 char *name = get_my_name(true);
1981 int result = export(name, stdout);
1989 static int cmd_export_all(int argc, char *argv[]) {
1991 fprintf(stderr, "Too many arguments!\n");
1995 DIR *dir = opendir(hosts_dir);
1997 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2005 while((ent = readdir(dir))) {
2006 if(!check_id(ent->d_name))
2012 printf("#---------------------------------------------------------------#\n");
2014 result |= export(ent->d_name, stdout);
2023 static int cmd_import(int argc, char *argv[]) {
2025 fprintf(stderr, "Too many arguments!\n");
2034 char *filename = NULL;
2036 bool firstline = true;
2038 while(fgets(buf, sizeof buf, in)) {
2039 if(sscanf(buf, "Name = %s", name) == 1) {
2042 if(!check_id(name)) {
2043 fprintf(stderr, "Invalid Name in input!\n");
2051 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2053 if(!force && !access(filename, F_OK)) {
2054 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2059 out = fopen(filename, "w");
2061 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2067 } else if(firstline) {
2068 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2073 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2077 if(fputs(buf, out) < 0) {
2078 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2088 fprintf(stderr, "Imported %d host configuration files.\n", count);
2091 fprintf(stderr, "No host configuration files imported.\n");
2096 static int cmd_exchange(int argc, char *argv[]) {
2097 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2100 static int cmd_exchange_all(int argc, char *argv[]) {
2101 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2104 static int switch_network(char *name) {
2116 free(unixsocketname);
2117 unixsocketname = NULL;
2123 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2126 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2127 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2128 xasprintf(&prompt, "%s> ", identname);
2133 static int cmd_network(int argc, char *argv[]) {
2135 fprintf(stderr, "Too many arguments!\n");
2140 return switch_network(argv[1]);
2142 DIR *dir = opendir(confdir);
2144 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2149 while((ent = readdir(dir))) {
2150 if(*ent->d_name == '.')
2153 if(!strcmp(ent->d_name, "tinc.conf")) {
2159 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2160 if(!access(fname, R_OK))
2161 printf("%s\n", ent->d_name);
2170 static const struct {
2171 const char *command;
2172 int (*function)(int argc, char *argv[]);
2175 {"start", cmd_start},
2177 {"restart", cmd_restart},
2178 {"reload", cmd_reload},
2180 {"purge", cmd_purge},
2181 {"debug", cmd_debug},
2182 {"retry", cmd_retry},
2183 {"connect", cmd_connect},
2184 {"disconnect", cmd_disconnect},
2189 {"config", cmd_config, true},
2190 {"add", cmd_config},
2191 {"del", cmd_config},
2192 {"get", cmd_config},
2193 {"set", cmd_config},
2195 {"generate-keys", cmd_generate_keys},
2196 {"generate-rsa-keys", cmd_generate_rsa_keys},
2197 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2199 {"version", cmd_version},
2202 {"export", cmd_export},
2203 {"export-all", cmd_export_all},
2204 {"import", cmd_import},
2205 {"exchange", cmd_exchange},
2206 {"exchange-all", cmd_exchange_all},
2207 {"invite", cmd_invite},
2209 {"network", cmd_network},
2213 #ifdef HAVE_READLINE
2214 static char *complete_command(const char *text, int state) {
2222 while(commands[i].command) {
2223 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2224 return xstrdup(commands[i].command);
2231 static char *complete_dump(const char *text, int state) {
2232 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2241 if(!strncasecmp(matches[i], text, strlen(text)))
2242 return xstrdup(matches[i]);
2249 static char *complete_config(const char *text, int state) {
2257 while(variables[i].name) {
2258 char *dot = strchr(text, '.');
2260 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2262 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2266 if(!strncasecmp(variables[i].name, text, strlen(text)))
2267 return xstrdup(variables[i].name);
2275 static char *complete_info(const char *text, int state) {
2279 if(!connect_tincd(false))
2281 // Check the list of nodes
2282 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2283 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2286 while(recvline(fd, line, sizeof line)) {
2288 int n = sscanf(line, "%d %d %s", &code, &req, item);
2298 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2302 if(!strncmp(item, text, strlen(text)))
2303 return xstrdup(strip_weight(item));
2309 static char *complete_nothing(const char *text, int state) {
2313 static char **completion (const char *text, int start, int end) {
2314 char **matches = NULL;
2317 matches = rl_completion_matches(text, complete_command);
2318 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2319 matches = rl_completion_matches(text, complete_dump);
2320 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2321 matches = rl_completion_matches(text, complete_config);
2322 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2323 matches = rl_completion_matches(text, complete_config);
2324 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2325 matches = rl_completion_matches(text, complete_config);
2326 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2327 matches = rl_completion_matches(text, complete_config);
2328 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2329 matches = rl_completion_matches(text, complete_info);
2335 static int cmd_shell(int argc, char *argv[]) {
2336 xasprintf(&prompt, "%s> ", identname);
2340 int maxargs = argc + 16;
2341 char **nargv = xmalloc(maxargs * sizeof *nargv);
2343 for(int i = 0; i < argc; i++)
2346 #ifdef HAVE_READLINE
2347 rl_readline_name = "tinc";
2348 rl_completion_entry_function = complete_nothing;
2349 rl_attempted_completion_function = completion;
2350 rl_filename_completion_desired = 0;
2355 #ifdef HAVE_READLINE
2359 rl_basic_word_break_characters = "\t\n ";
2360 line = readline(prompt);
2362 copy = xstrdup(line);
2364 line = fgets(buf, sizeof buf, stdin);
2368 fputs(prompt, stdout);
2370 line = fgets(buf, sizeof buf, stdin);
2376 /* Ignore comments */
2384 char *p = line + strspn(line, " \t\n");
2385 char *next = strtok(p, " \t\n");
2388 if(nargc >= maxargs) {
2389 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2392 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2397 next = strtok(NULL, " \t\n");
2403 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2408 for(int i = 0; commands[i].command; i++) {
2409 if(!strcasecmp(nargv[argc], commands[i].command)) {
2410 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2416 #ifdef HAVE_READLINE
2422 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2435 int main(int argc, char *argv[]) {
2436 program_name = argv[0];
2439 tty = isatty(0) && isatty(1);
2441 if(!parse_options(argc, argv))
2445 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2446 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2459 static struct WSAData wsa_state;
2461 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2462 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2471 return cmd_shell(argc, argv);
2473 for(int i = 0; commands[i].command; i++) {
2474 if(!strcasecmp(argv[optind], commands[i].command))
2475 return commands[i].function(argc - optind, argv + optind);
2478 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);