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 {"UDPInfoInterval", VAR_SERVER},
1375 {"UDPRcvBuf", VAR_SERVER},
1376 {"UDPSndBuf", VAR_SERVER},
1377 {"VDEGroup", VAR_SERVER},
1378 {"VDEPort", VAR_SERVER},
1379 /* Host configuration */
1380 {"Address", VAR_HOST | VAR_MULTIPLE},
1381 {"Cipher", VAR_SERVER | VAR_HOST},
1382 {"ClampMSS", VAR_SERVER | VAR_HOST},
1383 {"Compression", VAR_SERVER | VAR_HOST},
1384 {"Digest", VAR_SERVER | VAR_HOST},
1385 {"Ed25519PublicKey", VAR_HOST},
1386 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1387 {"IndirectData", VAR_SERVER | VAR_HOST},
1388 {"MACLength", VAR_SERVER | VAR_HOST},
1389 {"PMTU", VAR_SERVER | VAR_HOST},
1390 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1392 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1393 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1394 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1395 {"TCPOnly", VAR_SERVER | VAR_HOST},
1396 {"Weight", VAR_HOST | VAR_SAFE},
1400 static int cmd_config(int argc, char *argv[]) {
1402 fprintf(stderr, "Invalid number of arguments.\n");
1406 if(strcasecmp(argv[0], "config"))
1410 if(!strcasecmp(argv[1], "get")) {
1412 } else if(!strcasecmp(argv[1], "add")) {
1413 argv++, argc--, action = 1;
1414 } else if(!strcasecmp(argv[1], "del")) {
1415 argv++, argc--, action = -1;
1416 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1417 argv++, argc--, action = 0;
1421 fprintf(stderr, "Invalid number of arguments.\n");
1425 // Concatenate the rest of the command line
1426 strncpy(line, argv[1], sizeof line - 1);
1427 for(int i = 2; i < argc; i++) {
1428 strncat(line, " ", sizeof line - 1 - strlen(line));
1429 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1432 // Liberal parsing into node name, variable name and value.
1438 len = strcspn(line, "\t =");
1440 value += strspn(value, "\t ");
1443 value += strspn(value, "\t ");
1446 variable = strchr(line, '.');
1455 fprintf(stderr, "No variable given.\n");
1459 if(action >= 0 && !*value) {
1460 fprintf(stderr, "No value for variable given.\n");
1464 if(action < -1 && *value)
1467 /* Some simple checks. */
1469 bool warnonremove = false;
1471 for(int i = 0; variables[i].name; i++) {
1472 if(strcasecmp(variables[i].name, variable))
1476 variable = (char *)variables[i].name;
1478 /* Discourage use of obsolete variables. */
1480 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1482 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1484 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1489 /* Don't put server variables in host config files */
1491 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1493 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1495 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1500 /* Should this go into our own host config file? */
1502 if(!node && !(variables[i].type & VAR_SERVER)) {
1503 node = get_my_name(true);
1508 /* Change "add" into "set" for variables that do not allow multiple occurences.
1509 Turn on warnings when it seems variables might be removed unintentionally. */
1511 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1512 warnonremove = true;
1514 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1515 warnonremove = true;
1521 if(node && !check_id(node)) {
1522 fprintf(stderr, "Invalid name for node.\n");
1527 if(force || action < 0) {
1528 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1530 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1535 // Open the right configuration file.
1538 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1540 filename = tinc_conf;
1542 FILE *f = fopen(filename, "r");
1544 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1548 char *tmpfile = NULL;
1552 xasprintf(&tmpfile, "%s.config.tmp", filename);
1553 tf = fopen(tmpfile, "w");
1555 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1561 // Copy the file, making modifications on the fly, unless we are just getting a value.
1565 bool removed = false;
1568 while(fgets(buf1, sizeof buf1, f)) {
1569 buf1[sizeof buf1 - 1] = 0;
1570 strncpy(buf2, buf1, sizeof buf2);
1572 // Parse line in a simple way
1576 len = strcspn(buf2, "\t =");
1577 bvalue = buf2 + len;
1578 bvalue += strspn(bvalue, "\t ");
1579 if(*bvalue == '=') {
1581 bvalue += strspn(bvalue, "\t ");
1587 if(!strcasecmp(buf2, variable)) {
1591 printf("%s\n", bvalue);
1593 } else if(action == -1) {
1594 if(!*value || !strcasecmp(bvalue, value)) {
1599 } else if(action == 0) {
1600 // Warn if "set" was used for variables that can occur multiple times
1601 if(warnonremove && strcasecmp(bvalue, value))
1602 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1604 // Already set? Delete the rest...
1608 // Otherwise, replace.
1609 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1610 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1616 } else if(action > 0) {
1617 // Check if we've already seen this variable with the same value
1618 if(!strcasecmp(bvalue, value))
1624 // Copy original line...
1625 if(fputs(buf1, tf) < 0) {
1626 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1630 // Add newline if it is missing...
1631 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1632 if(fputc('\n', tf) < 0) {
1633 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1640 // Make sure we read everything...
1641 if(ferror(f) || !feof(f)) {
1642 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1647 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1651 // Add new variable if necessary.
1652 if((action > 0 && !found)|| (action == 0 && !set)) {
1653 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1654 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1663 fprintf(stderr, "No matching configuration variables found.\n");
1668 // Make sure we wrote everything...
1670 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1674 // Could we find what we had to remove?
1675 if(action < 0 && !removed) {
1677 fprintf(stderr, "No configuration variables deleted.\n");
1681 // Replace the configuration file with the new one
1683 if(remove(filename)) {
1684 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1688 if(rename(tmpfile, filename)) {
1689 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1693 // Silently try notifying a running tincd of changes.
1694 if(connect_tincd(false))
1695 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1700 static bool try_bind(int port) {
1701 struct addrinfo *ai = NULL;
1702 struct addrinfo hint = {
1703 .ai_flags = AI_PASSIVE,
1704 .ai_family = AF_UNSPEC,
1705 .ai_socktype = SOCK_STREAM,
1706 .ai_protocol = IPPROTO_TCP,
1710 snprintf(portstr, sizeof portstr, "%d", port);
1712 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1716 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1719 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1729 int check_port(char *name) {
1733 fprintf(stderr, "Warning: could not bind to port 655. ");
1735 for(int i = 0; i < 100; i++) {
1736 int port = 0x1000 + (rand() & 0x7fff);
1737 if(try_bind(port)) {
1739 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1740 FILE *f = fopen(filename, "a");
1743 fprintf(stderr, "Please change tinc's Port manually.\n");
1747 fprintf(f, "Port = %d\n", port);
1749 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1754 fprintf(stderr, "Please change tinc's Port manually.\n");
1758 static int cmd_init(int argc, char *argv[]) {
1759 if(!access(tinc_conf, F_OK)) {
1760 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1765 fprintf(stderr, "Too many arguments!\n");
1767 } else if(argc < 2) {
1770 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1771 if(!fgets(buf, sizeof buf, stdin)) {
1772 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1775 int len = rstrip(buf);
1777 fprintf(stderr, "No name given!\n");
1782 fprintf(stderr, "No Name given!\n");
1786 name = strdup(argv[1]);
1788 fprintf(stderr, "No Name given!\n");
1793 if(!check_id(name)) {
1794 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1798 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1799 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1803 if(mkdir(confbase, 0777) && errno != EEXIST) {
1804 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1808 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1809 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1813 FILE *f = fopen(tinc_conf, "w");
1815 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1819 fprintf(f, "Name = %s\n", name);
1822 #ifndef DISABLE_LEGACY
1823 if(!rsa_keygen(2048, false))
1827 if(!ed25519_keygen(false))
1834 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1835 if(access(filename, F_OK)) {
1836 FILE *f = fopenmask(filename, "w", 0777);
1838 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1841 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");
1850 static int cmd_generate_keys(int argc, char *argv[]) {
1851 #ifdef DISABLE_LEGACY
1856 fprintf(stderr, "Too many arguments!\n");
1861 name = get_my_name(false);
1863 #ifndef DISABLE_LEGACY
1864 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1868 if(!ed25519_keygen(true))
1874 #ifndef DISABLE_LEGACY
1875 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1877 fprintf(stderr, "Too many arguments!\n");
1882 name = get_my_name(false);
1884 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1888 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1890 fprintf(stderr, "Too many arguments!\n");
1895 name = get_my_name(false);
1897 return !ed25519_keygen(true);
1900 static int cmd_help(int argc, char *argv[]) {
1905 static int cmd_version(int argc, char *argv[]) {
1907 fprintf(stderr, "Too many arguments!\n");
1915 static int cmd_info(int argc, char *argv[]) {
1917 fprintf(stderr, "Invalid number of arguments.\n");
1921 if(!connect_tincd(true))
1924 return info(fd, argv[1]);
1927 static const char *conffiles[] = {
1938 static int cmd_edit(int argc, char *argv[]) {
1940 fprintf(stderr, "Invalid number of arguments.\n");
1944 char *filename = NULL;
1946 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1947 for(int i = 0; conffiles[i]; i++) {
1948 if(!strcmp(argv[1], conffiles[i])) {
1949 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1958 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1959 char *dash = strchr(argv[1], '-');
1962 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1963 fprintf(stderr, "Invalid configuration filename.\n");
1971 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1973 xasprintf(&command, "edit \"%s\"", filename);
1975 int result = system(command);
1979 // Silently try notifying a running tincd of changes.
1980 if(connect_tincd(false))
1981 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1986 static int export(const char *name, FILE *out) {
1988 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1989 FILE *in = fopen(filename, "r");
1991 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1995 fprintf(out, "Name = %s\n", name);
1997 while(fgets(buf, sizeof buf, in)) {
1998 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2003 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2012 static int cmd_export(int argc, char *argv[]) {
2014 fprintf(stderr, "Too many arguments!\n");
2018 char *name = get_my_name(true);
2022 int result = export(name, stdout);
2030 static int cmd_export_all(int argc, char *argv[]) {
2032 fprintf(stderr, "Too many arguments!\n");
2036 DIR *dir = opendir(hosts_dir);
2038 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2046 while((ent = readdir(dir))) {
2047 if(!check_id(ent->d_name))
2053 printf("#---------------------------------------------------------------#\n");
2055 result |= export(ent->d_name, stdout);
2064 static int cmd_import(int argc, char *argv[]) {
2066 fprintf(stderr, "Too many arguments!\n");
2075 char *filename = NULL;
2077 bool firstline = true;
2079 while(fgets(buf, sizeof buf, in)) {
2080 if(sscanf(buf, "Name = %s", name) == 1) {
2083 if(!check_id(name)) {
2084 fprintf(stderr, "Invalid Name in input!\n");
2092 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2094 if(!force && !access(filename, F_OK)) {
2095 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2100 out = fopen(filename, "w");
2102 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2108 } else if(firstline) {
2109 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2114 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2118 if(fputs(buf, out) < 0) {
2119 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2129 fprintf(stderr, "Imported %d host configuration files.\n", count);
2132 fprintf(stderr, "No host configuration files imported.\n");
2137 static int cmd_exchange(int argc, char *argv[]) {
2138 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2141 static int cmd_exchange_all(int argc, char *argv[]) {
2142 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2145 static int switch_network(char *name) {
2157 free(unixsocketname);
2158 unixsocketname = NULL;
2164 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2166 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2167 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2168 xasprintf(&prompt, "%s> ", identname);
2173 static int cmd_network(int argc, char *argv[]) {
2175 fprintf(stderr, "Too many arguments!\n");
2180 return switch_network(argv[1]);
2182 DIR *dir = opendir(confdir);
2184 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2189 while((ent = readdir(dir))) {
2190 if(*ent->d_name == '.')
2193 if(!strcmp(ent->d_name, "tinc.conf")) {
2199 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2200 if(!access(fname, R_OK))
2201 printf("%s\n", ent->d_name);
2210 static int cmd_fsck(int argc, char *argv[]) {
2212 fprintf(stderr, "Too many arguments!\n");
2216 return fsck(orig_argv[0]);
2219 static const struct {
2220 const char *command;
2221 int (*function)(int argc, char *argv[]);
2224 {"start", cmd_start},
2226 {"restart", cmd_restart},
2227 {"reload", cmd_reload},
2229 {"purge", cmd_purge},
2230 {"debug", cmd_debug},
2231 {"retry", cmd_retry},
2232 {"connect", cmd_connect},
2233 {"disconnect", cmd_disconnect},
2238 {"config", cmd_config, true},
2239 {"add", cmd_config},
2240 {"del", cmd_config},
2241 {"get", cmd_config},
2242 {"set", cmd_config},
2244 {"generate-keys", cmd_generate_keys},
2245 #ifndef DISABLE_LEGACY
2246 {"generate-rsa-keys", cmd_generate_rsa_keys},
2248 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2250 {"version", cmd_version},
2253 {"export", cmd_export},
2254 {"export-all", cmd_export_all},
2255 {"import", cmd_import},
2256 {"exchange", cmd_exchange},
2257 {"exchange-all", cmd_exchange_all},
2258 {"invite", cmd_invite},
2260 {"network", cmd_network},
2265 #ifdef HAVE_READLINE
2266 static char *complete_command(const char *text, int state) {
2274 while(commands[i].command) {
2275 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2276 return xstrdup(commands[i].command);
2283 static char *complete_dump(const char *text, int state) {
2284 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2293 if(!strncasecmp(matches[i], text, strlen(text)))
2294 return xstrdup(matches[i]);
2301 static char *complete_config(const char *text, int state) {
2309 while(variables[i].name) {
2310 char *dot = strchr(text, '.');
2312 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2314 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2318 if(!strncasecmp(variables[i].name, text, strlen(text)))
2319 return xstrdup(variables[i].name);
2327 static char *complete_info(const char *text, int state) {
2331 if(!connect_tincd(false))
2333 // Check the list of nodes
2334 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2335 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2338 while(recvline(fd, line, sizeof line)) {
2340 int n = sscanf(line, "%d %d %s", &code, &req, item);
2350 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2354 if(!strncmp(item, text, strlen(text)))
2355 return xstrdup(strip_weight(item));
2361 static char *complete_nothing(const char *text, int state) {
2365 static char **completion (const char *text, int start, int end) {
2366 char **matches = NULL;
2369 matches = rl_completion_matches(text, complete_command);
2370 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2371 matches = rl_completion_matches(text, complete_dump);
2372 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2373 matches = rl_completion_matches(text, complete_config);
2374 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2375 matches = rl_completion_matches(text, complete_config);
2376 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2377 matches = rl_completion_matches(text, complete_config);
2378 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2379 matches = rl_completion_matches(text, complete_config);
2380 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2381 matches = rl_completion_matches(text, complete_info);
2387 static int cmd_shell(int argc, char *argv[]) {
2388 xasprintf(&prompt, "%s> ", identname);
2392 int maxargs = argc + 16;
2393 char **nargv = xmalloc(maxargs * sizeof *nargv);
2395 for(int i = 0; i < argc; i++)
2398 #ifdef HAVE_READLINE
2399 rl_readline_name = "tinc";
2400 rl_completion_entry_function = complete_nothing;
2401 rl_attempted_completion_function = completion;
2402 rl_filename_completion_desired = 0;
2407 #ifdef HAVE_READLINE
2411 rl_basic_word_break_characters = "\t\n ";
2412 line = readline(prompt);
2414 copy = xstrdup(line);
2416 line = fgets(buf, sizeof buf, stdin);
2420 fputs(prompt, stdout);
2422 line = fgets(buf, sizeof buf, stdin);
2428 /* Ignore comments */
2436 char *p = line + strspn(line, " \t\n");
2437 char *next = strtok(p, " \t\n");
2440 if(nargc >= maxargs) {
2441 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2444 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2449 next = strtok(NULL, " \t\n");
2455 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2460 for(int i = 0; commands[i].command; i++) {
2461 if(!strcasecmp(nargv[argc], commands[i].command)) {
2462 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2468 #ifdef HAVE_READLINE
2474 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2487 int main(int argc, char *argv[]) {
2488 program_name = argv[0];
2491 tty = isatty(0) && isatty(1);
2493 if(!parse_options(argc, argv))
2497 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2498 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2511 static struct WSAData wsa_state;
2513 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2514 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2523 return cmd_shell(argc, argv);
2525 for(int i = 0; commands[i].command; i++) {
2526 if(!strcasecmp(argv[optind], commands[i].command))
2527 return commands[i].function(argc - optind, argv + optind);
2530 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);