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 {"UDPRcvBuf", VAR_SERVER},
1375 {"UDPSndBuf", VAR_SERVER},
1376 {"VDEGroup", VAR_SERVER},
1377 {"VDEPort", VAR_SERVER},
1378 /* Host configuration */
1379 {"Address", VAR_HOST | VAR_MULTIPLE},
1380 {"Cipher", VAR_SERVER | VAR_HOST},
1381 {"ClampMSS", VAR_SERVER | VAR_HOST},
1382 {"Compression", VAR_SERVER | VAR_HOST},
1383 {"Digest", VAR_SERVER | VAR_HOST},
1384 {"Ed25519PublicKey", VAR_HOST},
1385 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1386 {"IndirectData", VAR_SERVER | VAR_HOST},
1387 {"MACLength", VAR_SERVER | VAR_HOST},
1388 {"PMTU", VAR_SERVER | VAR_HOST},
1389 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1391 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1392 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1393 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1394 {"TCPOnly", VAR_SERVER | VAR_HOST},
1395 {"Weight", VAR_HOST | VAR_SAFE},
1399 static int cmd_config(int argc, char *argv[]) {
1401 fprintf(stderr, "Invalid number of arguments.\n");
1405 if(strcasecmp(argv[0], "config"))
1409 if(!strcasecmp(argv[1], "get")) {
1411 } else if(!strcasecmp(argv[1], "add")) {
1412 argv++, argc--, action = 1;
1413 } else if(!strcasecmp(argv[1], "del")) {
1414 argv++, argc--, action = -1;
1415 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1416 argv++, argc--, action = 0;
1420 fprintf(stderr, "Invalid number of arguments.\n");
1424 // Concatenate the rest of the command line
1425 strncpy(line, argv[1], sizeof line - 1);
1426 for(int i = 2; i < argc; i++) {
1427 strncat(line, " ", sizeof line - 1 - strlen(line));
1428 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1431 // Liberal parsing into node name, variable name and value.
1437 len = strcspn(line, "\t =");
1439 value += strspn(value, "\t ");
1442 value += strspn(value, "\t ");
1445 variable = strchr(line, '.');
1454 fprintf(stderr, "No variable given.\n");
1458 if(action >= 0 && !*value) {
1459 fprintf(stderr, "No value for variable given.\n");
1463 if(action < -1 && *value)
1466 /* Some simple checks. */
1468 bool warnonremove = false;
1470 for(int i = 0; variables[i].name; i++) {
1471 if(strcasecmp(variables[i].name, variable))
1475 variable = (char *)variables[i].name;
1477 /* Discourage use of obsolete variables. */
1479 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1481 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1483 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1488 /* Don't put server variables in host config files */
1490 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1492 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1494 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1499 /* Should this go into our own host config file? */
1501 if(!node && !(variables[i].type & VAR_SERVER)) {
1502 node = get_my_name(true);
1507 /* Change "add" into "set" for variables that do not allow multiple occurences.
1508 Turn on warnings when it seems variables might be removed unintentionally. */
1510 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1511 warnonremove = true;
1513 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1514 warnonremove = true;
1520 if(node && !check_id(node)) {
1521 fprintf(stderr, "Invalid name for node.\n");
1526 if(force || action < 0) {
1527 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1529 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1534 // Open the right configuration file.
1537 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1539 filename = tinc_conf;
1541 FILE *f = fopen(filename, "r");
1543 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1547 char *tmpfile = NULL;
1551 xasprintf(&tmpfile, "%s.config.tmp", filename);
1552 tf = fopen(tmpfile, "w");
1554 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1560 // Copy the file, making modifications on the fly, unless we are just getting a value.
1564 bool removed = false;
1567 while(fgets(buf1, sizeof buf1, f)) {
1568 buf1[sizeof buf1 - 1] = 0;
1569 strncpy(buf2, buf1, sizeof buf2);
1571 // Parse line in a simple way
1575 len = strcspn(buf2, "\t =");
1576 bvalue = buf2 + len;
1577 bvalue += strspn(bvalue, "\t ");
1578 if(*bvalue == '=') {
1580 bvalue += strspn(bvalue, "\t ");
1586 if(!strcasecmp(buf2, variable)) {
1590 printf("%s\n", bvalue);
1592 } else if(action == -1) {
1593 if(!*value || !strcasecmp(bvalue, value)) {
1598 } else if(action == 0) {
1599 // Warn if "set" was used for variables that can occur multiple times
1600 if(warnonremove && strcasecmp(bvalue, value))
1601 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1603 // Already set? Delete the rest...
1607 // Otherwise, replace.
1608 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1609 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1615 } else if(action > 0) {
1616 // Check if we've already seen this variable with the same value
1617 if(!strcasecmp(bvalue, value))
1623 // Copy original line...
1624 if(fputs(buf1, tf) < 0) {
1625 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1629 // Add newline if it is missing...
1630 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1631 if(fputc('\n', tf) < 0) {
1632 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1639 // Make sure we read everything...
1640 if(ferror(f) || !feof(f)) {
1641 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1646 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1650 // Add new variable if necessary.
1651 if((action > 0 && !found)|| (action == 0 && !set)) {
1652 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1653 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1662 fprintf(stderr, "No matching configuration variables found.\n");
1667 // Make sure we wrote everything...
1669 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1673 // Could we find what we had to remove?
1674 if(action < 0 && !removed) {
1676 fprintf(stderr, "No configuration variables deleted.\n");
1680 // Replace the configuration file with the new one
1682 if(remove(filename)) {
1683 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1687 if(rename(tmpfile, filename)) {
1688 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1692 // Silently try notifying a running tincd of changes.
1693 if(connect_tincd(false))
1694 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1699 static bool try_bind(int port) {
1700 struct addrinfo *ai = NULL;
1701 struct addrinfo hint = {
1702 .ai_flags = AI_PASSIVE,
1703 .ai_family = AF_UNSPEC,
1704 .ai_socktype = SOCK_STREAM,
1705 .ai_protocol = IPPROTO_TCP,
1709 snprintf(portstr, sizeof portstr, "%d", port);
1711 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1715 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1718 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1728 int check_port(char *name) {
1732 fprintf(stderr, "Warning: could not bind to port 655. ");
1734 for(int i = 0; i < 100; i++) {
1735 int port = 0x1000 + (rand() & 0x7fff);
1736 if(try_bind(port)) {
1738 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1739 FILE *f = fopen(filename, "a");
1742 fprintf(stderr, "Please change tinc's Port manually.\n");
1746 fprintf(f, "Port = %d\n", port);
1748 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1753 fprintf(stderr, "Please change tinc's Port manually.\n");
1757 static int cmd_init(int argc, char *argv[]) {
1758 if(!access(tinc_conf, F_OK)) {
1759 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1764 fprintf(stderr, "Too many arguments!\n");
1766 } else if(argc < 2) {
1769 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1770 if(!fgets(buf, sizeof buf, stdin)) {
1771 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1774 int len = rstrip(buf);
1776 fprintf(stderr, "No name given!\n");
1781 fprintf(stderr, "No Name given!\n");
1785 name = strdup(argv[1]);
1787 fprintf(stderr, "No Name given!\n");
1792 if(!check_id(name)) {
1793 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1797 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1798 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1802 if(mkdir(confbase, 0777) && errno != EEXIST) {
1803 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1807 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1808 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1812 FILE *f = fopen(tinc_conf, "w");
1814 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1818 fprintf(f, "Name = %s\n", name);
1821 #ifndef DISABLE_LEGACY
1822 if(!rsa_keygen(2048, false))
1826 if(!ed25519_keygen(false))
1833 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1834 if(access(filename, F_OK)) {
1835 FILE *f = fopenmask(filename, "w", 0777);
1837 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1840 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");
1849 static int cmd_generate_keys(int argc, char *argv[]) {
1850 #ifdef DISABLE_LEGACY
1855 fprintf(stderr, "Too many arguments!\n");
1860 name = get_my_name(false);
1862 #ifndef DISABLE_LEGACY
1863 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1867 if(!ed25519_keygen(true))
1873 #ifndef DISABLE_LEGACY
1874 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1876 fprintf(stderr, "Too many arguments!\n");
1881 name = get_my_name(false);
1883 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1887 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1889 fprintf(stderr, "Too many arguments!\n");
1894 name = get_my_name(false);
1896 return !ed25519_keygen(true);
1899 static int cmd_help(int argc, char *argv[]) {
1904 static int cmd_version(int argc, char *argv[]) {
1906 fprintf(stderr, "Too many arguments!\n");
1914 static int cmd_info(int argc, char *argv[]) {
1916 fprintf(stderr, "Invalid number of arguments.\n");
1920 if(!connect_tincd(true))
1923 return info(fd, argv[1]);
1926 static const char *conffiles[] = {
1937 static int cmd_edit(int argc, char *argv[]) {
1939 fprintf(stderr, "Invalid number of arguments.\n");
1943 char *filename = NULL;
1945 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1946 for(int i = 0; conffiles[i]; i++) {
1947 if(!strcmp(argv[1], conffiles[i])) {
1948 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1957 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1958 char *dash = strchr(argv[1], '-');
1961 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1962 fprintf(stderr, "Invalid configuration filename.\n");
1970 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1972 xasprintf(&command, "edit \"%s\"", filename);
1974 int result = system(command);
1978 // Silently try notifying a running tincd of changes.
1979 if(connect_tincd(false))
1980 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1985 static int export(const char *name, FILE *out) {
1987 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1988 FILE *in = fopen(filename, "r");
1990 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1994 fprintf(out, "Name = %s\n", name);
1996 while(fgets(buf, sizeof buf, in)) {
1997 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2002 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2011 static int cmd_export(int argc, char *argv[]) {
2013 fprintf(stderr, "Too many arguments!\n");
2017 char *name = get_my_name(true);
2021 int result = export(name, stdout);
2029 static int cmd_export_all(int argc, char *argv[]) {
2031 fprintf(stderr, "Too many arguments!\n");
2035 DIR *dir = opendir(hosts_dir);
2037 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2045 while((ent = readdir(dir))) {
2046 if(!check_id(ent->d_name))
2052 printf("#---------------------------------------------------------------#\n");
2054 result |= export(ent->d_name, stdout);
2063 static int cmd_import(int argc, char *argv[]) {
2065 fprintf(stderr, "Too many arguments!\n");
2074 char *filename = NULL;
2076 bool firstline = true;
2078 while(fgets(buf, sizeof buf, in)) {
2079 if(sscanf(buf, "Name = %s", name) == 1) {
2082 if(!check_id(name)) {
2083 fprintf(stderr, "Invalid Name in input!\n");
2091 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2093 if(!force && !access(filename, F_OK)) {
2094 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2099 out = fopen(filename, "w");
2101 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2107 } else if(firstline) {
2108 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2113 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2117 if(fputs(buf, out) < 0) {
2118 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2128 fprintf(stderr, "Imported %d host configuration files.\n", count);
2131 fprintf(stderr, "No host configuration files imported.\n");
2136 static int cmd_exchange(int argc, char *argv[]) {
2137 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2140 static int cmd_exchange_all(int argc, char *argv[]) {
2141 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2144 static int switch_network(char *name) {
2156 free(unixsocketname);
2157 unixsocketname = NULL;
2163 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2165 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2166 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2167 xasprintf(&prompt, "%s> ", identname);
2172 static int cmd_network(int argc, char *argv[]) {
2174 fprintf(stderr, "Too many arguments!\n");
2179 return switch_network(argv[1]);
2181 DIR *dir = opendir(confdir);
2183 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2188 while((ent = readdir(dir))) {
2189 if(*ent->d_name == '.')
2192 if(!strcmp(ent->d_name, "tinc.conf")) {
2198 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2199 if(!access(fname, R_OK))
2200 printf("%s\n", ent->d_name);
2209 static int cmd_fsck(int argc, char *argv[]) {
2211 fprintf(stderr, "Too many arguments!\n");
2215 return fsck(orig_argv[0]);
2218 static const struct {
2219 const char *command;
2220 int (*function)(int argc, char *argv[]);
2223 {"start", cmd_start},
2225 {"restart", cmd_restart},
2226 {"reload", cmd_reload},
2228 {"purge", cmd_purge},
2229 {"debug", cmd_debug},
2230 {"retry", cmd_retry},
2231 {"connect", cmd_connect},
2232 {"disconnect", cmd_disconnect},
2237 {"config", cmd_config, true},
2238 {"add", cmd_config},
2239 {"del", cmd_config},
2240 {"get", cmd_config},
2241 {"set", cmd_config},
2243 {"generate-keys", cmd_generate_keys},
2244 #ifndef DISABLE_LEGACY
2245 {"generate-rsa-keys", cmd_generate_rsa_keys},
2247 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2249 {"version", cmd_version},
2252 {"export", cmd_export},
2253 {"export-all", cmd_export_all},
2254 {"import", cmd_import},
2255 {"exchange", cmd_exchange},
2256 {"exchange-all", cmd_exchange_all},
2257 {"invite", cmd_invite},
2259 {"network", cmd_network},
2264 #ifdef HAVE_READLINE
2265 static char *complete_command(const char *text, int state) {
2273 while(commands[i].command) {
2274 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2275 return xstrdup(commands[i].command);
2282 static char *complete_dump(const char *text, int state) {
2283 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2292 if(!strncasecmp(matches[i], text, strlen(text)))
2293 return xstrdup(matches[i]);
2300 static char *complete_config(const char *text, int state) {
2308 while(variables[i].name) {
2309 char *dot = strchr(text, '.');
2311 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2313 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2317 if(!strncasecmp(variables[i].name, text, strlen(text)))
2318 return xstrdup(variables[i].name);
2326 static char *complete_info(const char *text, int state) {
2330 if(!connect_tincd(false))
2332 // Check the list of nodes
2333 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2334 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2337 while(recvline(fd, line, sizeof line)) {
2339 int n = sscanf(line, "%d %d %s", &code, &req, item);
2349 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2353 if(!strncmp(item, text, strlen(text)))
2354 return xstrdup(strip_weight(item));
2360 static char *complete_nothing(const char *text, int state) {
2364 static char **completion (const char *text, int start, int end) {
2365 char **matches = NULL;
2368 matches = rl_completion_matches(text, complete_command);
2369 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2370 matches = rl_completion_matches(text, complete_dump);
2371 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2372 matches = rl_completion_matches(text, complete_config);
2373 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2374 matches = rl_completion_matches(text, complete_config);
2375 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2376 matches = rl_completion_matches(text, complete_config);
2377 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2378 matches = rl_completion_matches(text, complete_config);
2379 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2380 matches = rl_completion_matches(text, complete_info);
2386 static int cmd_shell(int argc, char *argv[]) {
2387 xasprintf(&prompt, "%s> ", identname);
2391 int maxargs = argc + 16;
2392 char **nargv = xmalloc(maxargs * sizeof *nargv);
2394 for(int i = 0; i < argc; i++)
2397 #ifdef HAVE_READLINE
2398 rl_readline_name = "tinc";
2399 rl_completion_entry_function = complete_nothing;
2400 rl_attempted_completion_function = completion;
2401 rl_filename_completion_desired = 0;
2406 #ifdef HAVE_READLINE
2410 rl_basic_word_break_characters = "\t\n ";
2411 line = readline(prompt);
2413 copy = xstrdup(line);
2415 line = fgets(buf, sizeof buf, stdin);
2419 fputs(prompt, stdout);
2421 line = fgets(buf, sizeof buf, stdin);
2427 /* Ignore comments */
2435 char *p = line + strspn(line, " \t\n");
2436 char *next = strtok(p, " \t\n");
2439 if(nargc >= maxargs) {
2440 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2443 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2448 next = strtok(NULL, " \t\n");
2454 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2459 for(int i = 0; commands[i].command; i++) {
2460 if(!strcasecmp(nargv[argc], commands[i].command)) {
2461 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2467 #ifdef HAVE_READLINE
2473 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2486 int main(int argc, char *argv[]) {
2487 program_name = argv[0];
2490 tty = isatty(0) && isatty(1);
2492 if(!parse_options(argc, argv))
2496 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2497 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2510 static struct WSAData wsa_state;
2512 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2513 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2522 return cmd_shell(argc, argv);
2524 for(int i = 0; commands[i].command; i++) {
2525 if(!strcasecmp(argv[optind], commands[i].command))
2526 return commands[i].function(argc - optind, argv + optind);
2529 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);