2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2014 Guus Sliepen <guus@tinc-vpn.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "readline/readline.h"
26 #include "readline/history.h"
31 #include "control_common.h"
36 #include "invitation.h"
45 #define MSG_NOSIGNAL 0
48 static char **orig_argv;
51 /* If nonzero, display usage information and exit. */
52 static bool show_help = false;
54 /* If nonzero, print the version on standard output and exit. */
55 static bool show_version = false;
57 static char *name = NULL;
58 static char controlcookie[1025];
59 char *tinc_conf = NULL;
60 char *hosts_dir = NULL;
63 // Horrible global variables...
72 bool confbasegiven = false;
73 bool netnamegiven = false;
74 char *scriptinterpreter = NULL;
75 char *scriptextension = "";
78 static struct option const long_options[] = {
79 {"batch", no_argument, NULL, 'b'},
80 {"config", required_argument, NULL, 'c'},
81 {"net", required_argument, NULL, 'n'},
82 {"help", no_argument, NULL, 1},
83 {"version", no_argument, NULL, 2},
84 {"pidfile", required_argument, NULL, 3},
85 {"force", no_argument, NULL, 4},
89 static void version(void) {
90 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
91 VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
92 printf("Copyright (C) 1998-2014 Ivo Timmermans, Guus Sliepen and others.\n"
93 "See the AUTHORS file for a complete list.\n\n"
94 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
95 "and you are welcome to redistribute it under certain conditions;\n"
96 "see the file COPYING for details.\n");
99 static void usage(bool status) {
101 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
103 printf("Usage: %s [options] command\n\n", program_name);
104 printf("Valid options are:\n"
105 " -b, --batch Don't ask for anything (non-interactive mode).\n"
106 " -c, --config=DIR Read configuration options from DIR.\n"
107 " -n, --net=NETNAME Connect to net NETNAME.\n"
108 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
109 " --force Force some commands to work despite warnings.\n"
110 " --help Display this help and exit.\n"
111 " --version Output version information and exit.\n"
113 "Valid commands are:\n"
114 " init [name] Create initial configuration files.\n"
115 " get VARIABLE Print current value of VARIABLE\n"
116 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
117 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
118 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
119 " start [tincd options] Start tincd.\n"
120 " stop Stop tincd.\n"
121 " restart [tincd options] Restart tincd.\n"
122 " reload Partially reload configuration of running tincd.\n"
123 " pid Show PID of currently running tincd.\n"
124 #ifdef DISABLE_LEGACY
125 " generate-keys Generate a new Ed25519 public/private keypair.\n"
127 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
128 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
130 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
131 " dump Dump a list of one of the following things:\n"
132 " [reachable] nodes - all known nodes in the VPN\n"
133 " edges - all known connections in the VPN\n"
134 " subnets - all known subnets in the VPN\n"
135 " connections - all meta connections with ourself\n"
136 " [di]graph - graph of the VPN in dotty format\n"
137 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
138 " purge Purge unreachable nodes\n"
139 " debug N Set debug level\n"
140 " retry Retry all outgoing connections\n"
141 " disconnect NODE Close meta connection with NODE\n"
143 " top Show real-time statistics\n"
145 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
146 " log [level] Dump log output [up to the specified level]\n"
147 " export Export host configuration of local node to standard output\n"
148 " export-all Export all host configuration files to standard output\n"
149 " import Import host configuration file(s) from standard input\n"
150 " exchange Same as export followed by import\n"
151 " exchange-all Same as export-all followed by import\n"
152 " invite NODE [...] Generate an invitation for NODE\n"
153 " join INVITATION Join a VPN using an INVITIATION\n"
154 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
155 " fsck Check the configuration files for problems.\n"
157 printf("Report bugs to tinc@tinc-vpn.org.\n");
161 static bool parse_options(int argc, char **argv) {
163 int option_index = 0;
165 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
167 case 0: /* long option */
174 case 'c': /* config file */
175 confbase = xstrdup(optarg);
176 confbasegiven = true;
179 case 'n': /* net name given */
180 netname = xstrdup(optarg);
183 case 1: /* show help */
187 case 2: /* show version */
191 case 3: /* open control socket here */
192 pidfilename = xstrdup(optarg);
199 case '?': /* wrong options */
208 if(!netname && (netname = getenv("NETNAME")))
209 netname = xstrdup(netname);
211 /* netname "." is special: a "top-level name" */
213 if(netname && (!*netname || !strcmp(netname, "."))) {
218 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
219 fprintf(stderr, "Invalid character in netname!\n");
226 /* Open a file with the desired permissions, minus the umask.
227 Also, if we want to create an executable file, we call fchmod()
228 to set the executable bits. */
230 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
231 mode_t mask = umask(0);
234 FILE *f = fopen(filename, mode);
236 if((perms & 0444) && f)
237 fchmod(fileno(f), perms);
243 static void disable_old_keys(const char *filename, const char *what) {
244 char tmpfile[PATH_MAX] = "";
246 bool disabled = false;
251 r = fopen(filename, "r");
255 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
257 struct stat st = {.st_mode = 0600};
258 fstat(fileno(r), &st);
259 w = fopenmask(tmpfile, "w", st.st_mode);
261 while(fgets(buf, sizeof buf, r)) {
262 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
263 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
269 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
275 if(block || ed25519pubkey)
277 if(fputs(buf, w) < 0) {
283 if(block && !strncmp(buf, "-----END ", 9))
290 if(ferror(r) || fclose(r) < 0)
295 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
302 // We cannot atomically replace files on Windows.
303 char bakfile[PATH_MAX] = "";
304 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
305 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
306 rename(bakfile, filename);
308 if(rename(tmpfile, filename)) {
310 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
315 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
322 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
328 /* Check stdin and stdout */
330 /* Ask for a file and/or directory name. */
331 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
333 if(fgets(buf, sizeof buf, stdin) == NULL) {
334 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
338 size_t len = strlen(buf);
347 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
349 if(filename[0] != '/') {
351 /* The directory is a relative path or a filename. */
352 directory = get_current_dir_name();
353 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
357 disable_old_keys(filename, what);
359 /* Open it first to keep the inode busy */
361 r = fopenmask(filename, mode, perms);
364 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
372 Generate a public/private Ed25519 keypair, and ask for a file to store
375 static bool ed25519_keygen(bool ask) {
378 char *pubname, *privname;
380 fprintf(stderr, "Generating Ed25519 keypair:\n");
382 if(!(key = ecdsa_generate())) {
383 fprintf(stderr, "Error during key generation!\n");
386 fprintf(stderr, "Done.\n");
388 xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
389 f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
395 if(!ecdsa_write_pem_private_key(key, f)) {
396 fprintf(stderr, "Error writing private key!\n");
405 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
407 xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
409 f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
415 char *pubkey = ecdsa_get_base64_public_key(key);
416 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
425 #ifndef DISABLE_LEGACY
427 Generate a public/private RSA keypair, and ask for a file to store
430 static bool rsa_keygen(int bits, bool ask) {
433 char *pubname, *privname;
435 // Make sure the key size is a multiple of 8 bits.
438 // Force them to be between 1024 and 8192 bits long.
444 fprintf(stderr, "Generating %d bits keys:\n", bits);
446 if(!(key = rsa_generate(bits, 0x10001))) {
447 fprintf(stderr, "Error during key generation!\n");
450 fprintf(stderr, "Done.\n");
452 xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
453 f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
459 if(!rsa_write_pem_private_key(key, f)) {
460 fprintf(stderr, "Error writing private key!\n");
469 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
471 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
473 f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
479 if(!rsa_write_pem_public_key(key, f)) {
480 fprintf(stderr, "Error writing public key!\n");
496 bool recvline(int fd, char *line, size_t len) {
497 char *newline = NULL;
502 while(!(newline = memchr(buffer, '\n', blen))) {
503 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
504 if(result == -1 && sockerrno == EINTR)
511 if(newline - buffer >= len)
514 len = newline - buffer;
516 memcpy(line, buffer, len);
518 memmove(buffer, newline + 1, blen - len - 1);
524 bool recvdata(int fd, char *data, size_t len) {
529 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
530 if(result == -1 && sockerrno == EINTR)
537 memcpy(data, buffer, len);
538 memmove(buffer, buffer + len, blen - len);
544 bool sendline(int fd, char *format, ...) {
545 static char buffer[4096];
550 va_start(ap, format);
551 blen = vsnprintf(buffer, sizeof buffer, format, ap);
554 if(blen < 1 || blen >= sizeof buffer)
561 int result = send(fd, p, blen, MSG_NOSIGNAL);
562 if(result == -1 && sockerrno == EINTR)
573 static void pcap(int fd, FILE *out, int snaplen) {
574 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
582 uint32_t tz_accuracy;
589 snaplen ?: sizeof data,
602 fwrite(&header, sizeof header, 1, out);
606 while(recvline(fd, line, sizeof line)) {
608 int n = sscanf(line, "%d %d %d", &code, &req, &len);
609 gettimeofday(&tv, NULL);
610 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
612 if(!recvdata(fd, data, len))
614 packet.tv_sec = tv.tv_sec;
615 packet.tv_usec = tv.tv_usec;
617 packet.origlen = len;
618 fwrite(&packet, sizeof packet, 1, out);
619 fwrite(data, len, 1, out);
624 static void logcontrol(int fd, FILE *out, int level) {
625 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
629 while(recvline(fd, line, sizeof line)) {
631 int n = sscanf(line, "%d %d %d", &code, &req, &len);
632 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
634 if(!recvdata(fd, data, len))
636 fwrite(data, len, 1, out);
643 static bool remove_service(void) {
644 SC_HANDLE manager = NULL;
645 SC_HANDLE service = NULL;
646 SERVICE_STATUS status = {0};
648 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
650 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
654 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
657 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
661 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
662 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
664 fprintf(stderr, "%s service stopped\n", identname);
666 if(!DeleteService(service)) {
667 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
671 fprintf(stderr, "%s service removed\n", identname);
677 bool connect_tincd(bool verbose) {
682 struct timeval tv = {0, 0};
683 if(select(fd + 1, &r, NULL, NULL, &tv)) {
684 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
692 FILE *f = fopen(pidfilename, "r");
695 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
702 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
704 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
712 struct sockaddr_un sa;
713 sa.sun_family = AF_UNIX;
714 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
716 fd = socket(AF_UNIX, SOCK_STREAM, 0);
719 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
723 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
725 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
731 struct addrinfo hints = {
732 .ai_family = AF_UNSPEC,
733 .ai_socktype = SOCK_STREAM,
734 .ai_protocol = IPPROTO_TCP,
738 struct addrinfo *res = NULL;
740 if(getaddrinfo(host, port, &hints, &res) || !res) {
742 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
746 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
749 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
754 unsigned long arg = 0;
756 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
758 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
762 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
764 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
774 static const int one = 1;
775 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
781 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
783 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
789 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
791 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
793 fprintf(stderr, "Could not fully establish control socket connection\n");
803 static int cmd_start(int argc, char *argv[]) {
804 if(connect_tincd(false)) {
806 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
808 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
813 char *slash = strrchr(program_name, '/');
816 if ((c = strrchr(program_name, '\\')) > slash)
821 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
826 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
831 Windows has no real concept of an "argv array". A command line is just one string.
832 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
833 it uses quotes to handle spaces in arguments.
834 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
835 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
836 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
838 xasprintf(&arg0, "\"%s\"", arg0);
840 nargv[nargc++] = arg0;
841 for(int i = 1; i < optind; i++)
842 nargv[nargc++] = orig_argv[i];
843 for(int i = 1; i < argc; i++)
844 nargv[nargc++] = argv[i];
847 int status = spawnvp(_P_WAIT, c, nargv);
849 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
856 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
862 exit(execvp(c, nargv));
866 int status = -1, result;
868 signal(SIGINT, SIG_IGN);
870 result = waitpid(pid, &status, 0);
872 signal(SIGINT, SIG_DFL);
875 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
876 fprintf(stderr, "Error starting %s\n", c);
884 static int cmd_stop(int argc, char *argv[]) {
886 fprintf(stderr, "Too many arguments!\n");
891 if(!connect_tincd(true)) {
893 if(kill(pid, SIGTERM)) {
894 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
898 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
899 waitpid(pid, NULL, 0);
906 sendline(fd, "%d %d", CONTROL, REQ_STOP);
908 while(recvline(fd, line, sizeof line)) {
909 // Wait for tincd to close the connection...
912 if(!remove_service())
922 static int cmd_restart(int argc, char *argv[]) {
924 return cmd_start(argc, argv);
927 static int cmd_reload(int argc, char *argv[]) {
929 fprintf(stderr, "Too many arguments!\n");
933 if(!connect_tincd(true))
936 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
937 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
938 fprintf(stderr, "Could not reload configuration.\n");
946 static int cmd_dump(int argc, char *argv[]) {
947 bool only_reachable = false;
949 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
950 if(strcasecmp(argv[2], "nodes")) {
951 fprintf(stderr, "`reachable' only supported for nodes.\n");
955 only_reachable = true;
961 fprintf(stderr, "Invalid number of arguments.\n");
966 if(!connect_tincd(true))
971 if(!strcasecmp(argv[1], "nodes"))
972 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
973 else if(!strcasecmp(argv[1], "edges"))
974 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
975 else if(!strcasecmp(argv[1], "subnets"))
976 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
977 else if(!strcasecmp(argv[1], "connections"))
978 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
979 else if(!strcasecmp(argv[1], "graph")) {
980 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
981 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
983 } else if(!strcasecmp(argv[1], "digraph")) {
984 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
985 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
988 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
995 else if(do_graph == 2)
996 printf("digraph {\n");
998 while(recvline(fd, line, sizeof line)) {
999 char node1[4096], node2[4096];
1000 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1002 if(do_graph && req == REQ_DUMP_NODES)
1020 char local_host[4096];
1021 char local_port[4096];
1024 int cipher, digest, maclength, compression, distance, socket, weight;
1025 short int pmtu, minmtu, maxmtu;
1026 unsigned int options, status_int;
1027 node_status_t status;
1028 long int last_state_change;
1031 case REQ_DUMP_NODES: {
1032 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);
1034 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1038 memcpy(&status, &status_int, sizeof status);
1041 const char *color = "black";
1042 if(!strcmp(host, "MYSELF"))
1044 else if(!status.reachable)
1046 else if(strcmp(via, node))
1048 else if(!status.validkey)
1052 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1054 if(only_reachable && !status.reachable)
1056 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",
1057 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1061 case REQ_DUMP_EDGES: {
1062 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);
1064 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1069 float w = 1 + 65536.0 / weight;
1070 if(do_graph == 1 && strcmp(node1, node2) > 0)
1071 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1072 else if(do_graph == 2)
1073 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1075 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);
1079 case REQ_DUMP_SUBNETS: {
1080 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1082 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1085 printf("%s owner %s\n", strip_weight(subnet), node);
1088 case REQ_DUMP_CONNECTIONS: {
1089 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1091 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1094 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1098 fprintf(stderr, "Unable to parse dump from tincd.\n");
1103 fprintf(stderr, "Error receiving dump.\n");
1107 static int cmd_purge(int argc, char *argv[]) {
1109 fprintf(stderr, "Too many arguments!\n");
1113 if(!connect_tincd(true))
1116 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1117 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1118 fprintf(stderr, "Could not purge old information.\n");
1125 static int cmd_debug(int argc, char *argv[]) {
1127 fprintf(stderr, "Invalid number of arguments.\n");
1131 if(!connect_tincd(true))
1134 int debuglevel = atoi(argv[1]);
1137 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1138 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1139 fprintf(stderr, "Could not set debug level.\n");
1143 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1147 static int cmd_retry(int argc, char *argv[]) {
1149 fprintf(stderr, "Too many arguments!\n");
1153 if(!connect_tincd(true))
1156 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1157 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1158 fprintf(stderr, "Could not retry outgoing connections.\n");
1165 static int cmd_connect(int argc, char *argv[]) {
1167 fprintf(stderr, "Invalid number of arguments.\n");
1171 if(!check_id(argv[1])) {
1172 fprintf(stderr, "Invalid name for node.\n");
1176 if(!connect_tincd(true))
1179 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1180 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1181 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1188 static int cmd_disconnect(int argc, char *argv[]) {
1190 fprintf(stderr, "Invalid number of arguments.\n");
1194 if(!check_id(argv[1])) {
1195 fprintf(stderr, "Invalid name for node.\n");
1199 if(!connect_tincd(true))
1202 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1203 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1204 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1211 static int cmd_top(int argc, char *argv[]) {
1213 fprintf(stderr, "Too many arguments!\n");
1218 if(!connect_tincd(true))
1224 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1229 static int cmd_pcap(int argc, char *argv[]) {
1231 fprintf(stderr, "Too many arguments!\n");
1235 if(!connect_tincd(true))
1238 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1243 static void sigint_handler(int sig) {
1244 fprintf(stderr, "\n");
1245 shutdown(fd, SHUT_RDWR);
1249 static int cmd_log(int argc, char *argv[]) {
1251 fprintf(stderr, "Too many arguments!\n");
1255 if(!connect_tincd(true))
1259 signal(SIGINT, sigint_handler);
1262 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1265 signal(SIGINT, SIG_DFL);
1273 static int cmd_pid(int argc, char *argv[]) {
1275 fprintf(stderr, "Too many arguments!\n");
1279 if(!connect_tincd(true) && !pid)
1282 printf("%d\n", pid);
1286 int rstrip(char *value) {
1287 int len = strlen(value);
1288 while(len && strchr("\t\r\n ", value[len - 1]))
1293 char *get_my_name(bool verbose) {
1294 FILE *f = fopen(tinc_conf, "r");
1297 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1303 while(fgets(buf, sizeof buf, f)) {
1304 int len = strcspn(buf, "\t =");
1306 value += strspn(value, "\t ");
1309 value += strspn(value, "\t ");
1314 if(strcasecmp(buf, "Name"))
1318 return replace_name(value);
1324 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1328 const var_t variables[] = {
1329 /* Server configuration */
1330 {"AddressFamily", VAR_SERVER},
1331 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1332 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1333 {"BindToInterface", VAR_SERVER},
1334 {"Broadcast", VAR_SERVER | VAR_SAFE},
1335 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1336 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1337 {"DecrementTTL", VAR_SERVER},
1338 {"Device", VAR_SERVER},
1339 {"DeviceStandby", VAR_SERVER},
1340 {"DeviceType", VAR_SERVER},
1341 {"DirectOnly", VAR_SERVER},
1342 {"Ed25519PrivateKeyFile", VAR_SERVER},
1343 {"ExperimentalProtocol", VAR_SERVER},
1344 {"Forwarding", VAR_SERVER},
1345 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1346 {"Hostnames", VAR_SERVER},
1347 {"IffOneQueue", VAR_SERVER},
1348 {"Interface", VAR_SERVER},
1349 {"KeyExpire", VAR_SERVER},
1350 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1351 {"LocalDiscovery", VAR_SERVER},
1352 {"MACExpire", VAR_SERVER},
1353 {"MaxConnectionBurst", VAR_SERVER},
1354 {"MaxOutputBufferSize", VAR_SERVER},
1355 {"MaxTimeout", VAR_SERVER},
1356 {"Mode", VAR_SERVER | VAR_SAFE},
1357 {"Name", VAR_SERVER},
1358 {"PingInterval", VAR_SERVER},
1359 {"PingTimeout", VAR_SERVER},
1360 {"PriorityInheritance", VAR_SERVER},
1361 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1362 {"PrivateKeyFile", VAR_SERVER},
1363 {"ProcessPriority", VAR_SERVER},
1364 {"Proxy", VAR_SERVER},
1365 {"ReplayWindow", VAR_SERVER},
1366 {"ScriptsExtension", VAR_SERVER},
1367 {"ScriptsInterpreter", VAR_SERVER},
1368 {"StrictSubnets", VAR_SERVER},
1369 {"TunnelServer", VAR_SERVER},
1370 {"UDPDiscovery", VAR_SERVER},
1371 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1372 {"UDPDiscoveryInterval", VAR_SERVER},
1373 {"UDPDiscoveryTimeout", VAR_SERVER},
1374 {"MTUInfoInterval", VAR_SERVER},
1375 {"UDPInfoInterval", VAR_SERVER},
1376 {"UDPRcvBuf", VAR_SERVER},
1377 {"UDPSndBuf", VAR_SERVER},
1378 {"VDEGroup", VAR_SERVER},
1379 {"VDEPort", VAR_SERVER},
1380 /* Host configuration */
1381 {"Address", VAR_HOST | VAR_MULTIPLE},
1382 {"Cipher", VAR_SERVER | VAR_HOST},
1383 {"ClampMSS", VAR_SERVER | VAR_HOST},
1384 {"Compression", VAR_SERVER | VAR_HOST},
1385 {"Digest", VAR_SERVER | VAR_HOST},
1386 {"Ed25519PublicKey", VAR_HOST},
1387 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1388 {"IndirectData", VAR_SERVER | VAR_HOST},
1389 {"MACLength", VAR_SERVER | VAR_HOST},
1390 {"PMTU", VAR_SERVER | VAR_HOST},
1391 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1393 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1394 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1395 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1396 {"TCPOnly", VAR_SERVER | VAR_HOST},
1397 {"Weight", VAR_HOST | VAR_SAFE},
1401 static int cmd_config(int argc, char *argv[]) {
1403 fprintf(stderr, "Invalid number of arguments.\n");
1407 if(strcasecmp(argv[0], "config"))
1411 if(!strcasecmp(argv[1], "get")) {
1413 } else if(!strcasecmp(argv[1], "add")) {
1414 argv++, argc--, action = 1;
1415 } else if(!strcasecmp(argv[1], "del")) {
1416 argv++, argc--, action = -1;
1417 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1418 argv++, argc--, action = 0;
1422 fprintf(stderr, "Invalid number of arguments.\n");
1426 // Concatenate the rest of the command line
1427 strncpy(line, argv[1], sizeof line - 1);
1428 for(int i = 2; i < argc; i++) {
1429 strncat(line, " ", sizeof line - 1 - strlen(line));
1430 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1433 // Liberal parsing into node name, variable name and value.
1439 len = strcspn(line, "\t =");
1441 value += strspn(value, "\t ");
1444 value += strspn(value, "\t ");
1447 variable = strchr(line, '.');
1456 fprintf(stderr, "No variable given.\n");
1460 if(action >= 0 && !*value) {
1461 fprintf(stderr, "No value for variable given.\n");
1465 if(action < -1 && *value)
1468 /* Some simple checks. */
1470 bool warnonremove = false;
1472 for(int i = 0; variables[i].name; i++) {
1473 if(strcasecmp(variables[i].name, variable))
1477 variable = (char *)variables[i].name;
1479 /* Discourage use of obsolete variables. */
1481 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1483 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1485 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1490 /* Don't put server variables in host config files */
1492 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1494 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1496 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1501 /* Should this go into our own host config file? */
1503 if(!node && !(variables[i].type & VAR_SERVER)) {
1504 node = get_my_name(true);
1509 /* Change "add" into "set" for variables that do not allow multiple occurences.
1510 Turn on warnings when it seems variables might be removed unintentionally. */
1512 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1513 warnonremove = true;
1515 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1516 warnonremove = true;
1522 if(node && !check_id(node)) {
1523 fprintf(stderr, "Invalid name for node.\n");
1528 if(force || action < 0) {
1529 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1531 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1536 // Open the right configuration file.
1539 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1541 filename = tinc_conf;
1543 FILE *f = fopen(filename, "r");
1545 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1549 char *tmpfile = NULL;
1553 xasprintf(&tmpfile, "%s.config.tmp", filename);
1554 tf = fopen(tmpfile, "w");
1556 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1562 // Copy the file, making modifications on the fly, unless we are just getting a value.
1566 bool removed = false;
1569 while(fgets(buf1, sizeof buf1, f)) {
1570 buf1[sizeof buf1 - 1] = 0;
1571 strncpy(buf2, buf1, sizeof buf2);
1573 // Parse line in a simple way
1577 len = strcspn(buf2, "\t =");
1578 bvalue = buf2 + len;
1579 bvalue += strspn(bvalue, "\t ");
1580 if(*bvalue == '=') {
1582 bvalue += strspn(bvalue, "\t ");
1588 if(!strcasecmp(buf2, variable)) {
1592 printf("%s\n", bvalue);
1594 } else if(action == -1) {
1595 if(!*value || !strcasecmp(bvalue, value)) {
1600 } else if(action == 0) {
1601 // Warn if "set" was used for variables that can occur multiple times
1602 if(warnonremove && strcasecmp(bvalue, value))
1603 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1605 // Already set? Delete the rest...
1609 // Otherwise, replace.
1610 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1611 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1617 } else if(action > 0) {
1618 // Check if we've already seen this variable with the same value
1619 if(!strcasecmp(bvalue, value))
1625 // Copy original line...
1626 if(fputs(buf1, tf) < 0) {
1627 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1631 // Add newline if it is missing...
1632 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1633 if(fputc('\n', tf) < 0) {
1634 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1641 // Make sure we read everything...
1642 if(ferror(f) || !feof(f)) {
1643 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1648 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1652 // Add new variable if necessary.
1653 if((action > 0 && !found)|| (action == 0 && !set)) {
1654 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1655 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1664 fprintf(stderr, "No matching configuration variables found.\n");
1669 // Make sure we wrote everything...
1671 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1675 // Could we find what we had to remove?
1676 if(action < 0 && !removed) {
1678 fprintf(stderr, "No configuration variables deleted.\n");
1682 // Replace the configuration file with the new one
1684 if(remove(filename)) {
1685 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1689 if(rename(tmpfile, filename)) {
1690 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1694 // Silently try notifying a running tincd of changes.
1695 if(connect_tincd(false))
1696 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1701 static bool try_bind(int port) {
1702 struct addrinfo *ai = NULL;
1703 struct addrinfo hint = {
1704 .ai_flags = AI_PASSIVE,
1705 .ai_family = AF_UNSPEC,
1706 .ai_socktype = SOCK_STREAM,
1707 .ai_protocol = IPPROTO_TCP,
1711 snprintf(portstr, sizeof portstr, "%d", port);
1713 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1717 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1720 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1730 int check_port(char *name) {
1734 fprintf(stderr, "Warning: could not bind to port 655. ");
1736 for(int i = 0; i < 100; i++) {
1737 int port = 0x1000 + (rand() & 0x7fff);
1738 if(try_bind(port)) {
1740 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1741 FILE *f = fopen(filename, "a");
1744 fprintf(stderr, "Please change tinc's Port manually.\n");
1748 fprintf(f, "Port = %d\n", port);
1750 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1755 fprintf(stderr, "Please change tinc's Port manually.\n");
1759 static int cmd_init(int argc, char *argv[]) {
1760 if(!access(tinc_conf, F_OK)) {
1761 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1766 fprintf(stderr, "Too many arguments!\n");
1768 } else if(argc < 2) {
1771 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1772 if(!fgets(buf, sizeof buf, stdin)) {
1773 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1776 int len = rstrip(buf);
1778 fprintf(stderr, "No name given!\n");
1783 fprintf(stderr, "No Name given!\n");
1787 name = strdup(argv[1]);
1789 fprintf(stderr, "No Name given!\n");
1794 if(!check_id(name)) {
1795 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1799 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1800 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1804 if(mkdir(confbase, 0777) && errno != EEXIST) {
1805 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1809 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1810 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1814 FILE *f = fopen(tinc_conf, "w");
1816 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1820 fprintf(f, "Name = %s\n", name);
1823 #ifndef DISABLE_LEGACY
1824 if(!rsa_keygen(2048, false))
1828 if(!ed25519_keygen(false))
1835 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1836 if(access(filename, F_OK)) {
1837 FILE *f = fopenmask(filename, "w", 0777);
1839 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1842 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");
1851 static int cmd_generate_keys(int argc, char *argv[]) {
1852 #ifdef DISABLE_LEGACY
1857 fprintf(stderr, "Too many arguments!\n");
1862 name = get_my_name(false);
1864 #ifndef DISABLE_LEGACY
1865 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1869 if(!ed25519_keygen(true))
1875 #ifndef DISABLE_LEGACY
1876 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1878 fprintf(stderr, "Too many arguments!\n");
1883 name = get_my_name(false);
1885 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1889 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1891 fprintf(stderr, "Too many arguments!\n");
1896 name = get_my_name(false);
1898 return !ed25519_keygen(true);
1901 static int cmd_help(int argc, char *argv[]) {
1906 static int cmd_version(int argc, char *argv[]) {
1908 fprintf(stderr, "Too many arguments!\n");
1916 static int cmd_info(int argc, char *argv[]) {
1918 fprintf(stderr, "Invalid number of arguments.\n");
1922 if(!connect_tincd(true))
1925 return info(fd, argv[1]);
1928 static const char *conffiles[] = {
1939 static int cmd_edit(int argc, char *argv[]) {
1941 fprintf(stderr, "Invalid number of arguments.\n");
1945 char *filename = NULL;
1947 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1948 for(int i = 0; conffiles[i]; i++) {
1949 if(!strcmp(argv[1], conffiles[i])) {
1950 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1959 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1960 char *dash = strchr(argv[1], '-');
1963 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1964 fprintf(stderr, "Invalid configuration filename.\n");
1972 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1974 xasprintf(&command, "edit \"%s\"", filename);
1976 int result = system(command);
1980 // Silently try notifying a running tincd of changes.
1981 if(connect_tincd(false))
1982 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1987 static int export(const char *name, FILE *out) {
1989 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1990 FILE *in = fopen(filename, "r");
1992 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1996 fprintf(out, "Name = %s\n", name);
1998 while(fgets(buf, sizeof buf, in)) {
1999 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2004 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2013 static int cmd_export(int argc, char *argv[]) {
2015 fprintf(stderr, "Too many arguments!\n");
2019 char *name = get_my_name(true);
2023 int result = export(name, stdout);
2031 static int cmd_export_all(int argc, char *argv[]) {
2033 fprintf(stderr, "Too many arguments!\n");
2037 DIR *dir = opendir(hosts_dir);
2039 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2047 while((ent = readdir(dir))) {
2048 if(!check_id(ent->d_name))
2054 printf("#---------------------------------------------------------------#\n");
2056 result |= export(ent->d_name, stdout);
2065 static int cmd_import(int argc, char *argv[]) {
2067 fprintf(stderr, "Too many arguments!\n");
2076 char *filename = NULL;
2078 bool firstline = true;
2080 while(fgets(buf, sizeof buf, in)) {
2081 if(sscanf(buf, "Name = %s", name) == 1) {
2084 if(!check_id(name)) {
2085 fprintf(stderr, "Invalid Name in input!\n");
2093 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2095 if(!force && !access(filename, F_OK)) {
2096 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2101 out = fopen(filename, "w");
2103 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2109 } else if(firstline) {
2110 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2115 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2119 if(fputs(buf, out) < 0) {
2120 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2130 fprintf(stderr, "Imported %d host configuration files.\n", count);
2133 fprintf(stderr, "No host configuration files imported.\n");
2138 static int cmd_exchange(int argc, char *argv[]) {
2139 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2142 static int cmd_exchange_all(int argc, char *argv[]) {
2143 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2146 static int switch_network(char *name) {
2158 free(unixsocketname);
2159 unixsocketname = NULL;
2165 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2167 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2168 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2169 xasprintf(&prompt, "%s> ", identname);
2174 static int cmd_network(int argc, char *argv[]) {
2176 fprintf(stderr, "Too many arguments!\n");
2181 return switch_network(argv[1]);
2183 DIR *dir = opendir(confdir);
2185 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2190 while((ent = readdir(dir))) {
2191 if(*ent->d_name == '.')
2194 if(!strcmp(ent->d_name, "tinc.conf")) {
2200 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2201 if(!access(fname, R_OK))
2202 printf("%s\n", ent->d_name);
2211 static int cmd_fsck(int argc, char *argv[]) {
2213 fprintf(stderr, "Too many arguments!\n");
2217 return fsck(orig_argv[0]);
2220 static const struct {
2221 const char *command;
2222 int (*function)(int argc, char *argv[]);
2225 {"start", cmd_start},
2227 {"restart", cmd_restart},
2228 {"reload", cmd_reload},
2230 {"purge", cmd_purge},
2231 {"debug", cmd_debug},
2232 {"retry", cmd_retry},
2233 {"connect", cmd_connect},
2234 {"disconnect", cmd_disconnect},
2239 {"config", cmd_config, true},
2240 {"add", cmd_config},
2241 {"del", cmd_config},
2242 {"get", cmd_config},
2243 {"set", cmd_config},
2245 {"generate-keys", cmd_generate_keys},
2246 #ifndef DISABLE_LEGACY
2247 {"generate-rsa-keys", cmd_generate_rsa_keys},
2249 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2251 {"version", cmd_version},
2254 {"export", cmd_export},
2255 {"export-all", cmd_export_all},
2256 {"import", cmd_import},
2257 {"exchange", cmd_exchange},
2258 {"exchange-all", cmd_exchange_all},
2259 {"invite", cmd_invite},
2261 {"network", cmd_network},
2266 #ifdef HAVE_READLINE
2267 static char *complete_command(const char *text, int state) {
2275 while(commands[i].command) {
2276 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2277 return xstrdup(commands[i].command);
2284 static char *complete_dump(const char *text, int state) {
2285 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2294 if(!strncasecmp(matches[i], text, strlen(text)))
2295 return xstrdup(matches[i]);
2302 static char *complete_config(const char *text, int state) {
2310 while(variables[i].name) {
2311 char *dot = strchr(text, '.');
2313 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2315 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2319 if(!strncasecmp(variables[i].name, text, strlen(text)))
2320 return xstrdup(variables[i].name);
2328 static char *complete_info(const char *text, int state) {
2332 if(!connect_tincd(false))
2334 // Check the list of nodes
2335 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2336 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2339 while(recvline(fd, line, sizeof line)) {
2341 int n = sscanf(line, "%d %d %s", &code, &req, item);
2351 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2355 if(!strncmp(item, text, strlen(text)))
2356 return xstrdup(strip_weight(item));
2362 static char *complete_nothing(const char *text, int state) {
2366 static char **completion (const char *text, int start, int end) {
2367 char **matches = NULL;
2370 matches = rl_completion_matches(text, complete_command);
2371 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2372 matches = rl_completion_matches(text, complete_dump);
2373 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2374 matches = rl_completion_matches(text, complete_config);
2375 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2376 matches = rl_completion_matches(text, complete_config);
2377 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2378 matches = rl_completion_matches(text, complete_config);
2379 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2380 matches = rl_completion_matches(text, complete_config);
2381 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2382 matches = rl_completion_matches(text, complete_info);
2388 static int cmd_shell(int argc, char *argv[]) {
2389 xasprintf(&prompt, "%s> ", identname);
2393 int maxargs = argc + 16;
2394 char **nargv = xmalloc(maxargs * sizeof *nargv);
2396 for(int i = 0; i < argc; i++)
2399 #ifdef HAVE_READLINE
2400 rl_readline_name = "tinc";
2401 rl_completion_entry_function = complete_nothing;
2402 rl_attempted_completion_function = completion;
2403 rl_filename_completion_desired = 0;
2408 #ifdef HAVE_READLINE
2412 rl_basic_word_break_characters = "\t\n ";
2413 line = readline(prompt);
2415 copy = xstrdup(line);
2417 line = fgets(buf, sizeof buf, stdin);
2421 fputs(prompt, stdout);
2423 line = fgets(buf, sizeof buf, stdin);
2429 /* Ignore comments */
2437 char *p = line + strspn(line, " \t\n");
2438 char *next = strtok(p, " \t\n");
2441 if(nargc >= maxargs) {
2442 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2445 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2450 next = strtok(NULL, " \t\n");
2456 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2461 for(int i = 0; commands[i].command; i++) {
2462 if(!strcasecmp(nargv[argc], commands[i].command)) {
2463 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2469 #ifdef HAVE_READLINE
2475 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2488 int main(int argc, char *argv[]) {
2489 program_name = argv[0];
2492 tty = isatty(0) && isatty(1);
2494 if(!parse_options(argc, argv))
2498 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2499 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2512 static struct WSAData wsa_state;
2514 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2515 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2524 return cmd_shell(argc, argv);
2526 for(int i = 0; commands[i].command; i++) {
2527 if(!strcasecmp(argv[optind], commands[i].command))
2528 return commands[i].function(argc - optind, argv + optind);
2531 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);