2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2015 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 BUILD_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 " invitations - outstanding invitations\n"
138 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
139 " purge Purge unreachable nodes\n"
140 " debug N Set debug level\n"
141 " retry Retry all outgoing connections\n"
142 " disconnect NODE Close meta connection with NODE\n"
144 " top Show real-time statistics\n"
146 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
147 " log [level] Dump log output [up to the specified level]\n"
148 " export Export host configuration of local node to standard output\n"
149 " export-all Export all host configuration files to standard output\n"
150 " import Import host configuration file(s) from standard input\n"
151 " exchange Same as export followed by import\n"
152 " exchange-all Same as export-all followed by import\n"
153 " invite NODE [...] Generate an invitation for NODE\n"
154 " join INVITATION Join a VPN using an INVITATION\n"
155 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
156 " fsck Check the configuration files for problems.\n"
158 printf("Report bugs to tinc@tinc-vpn.org.\n");
162 static bool parse_options(int argc, char **argv) {
164 int option_index = 0;
166 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
168 case 0: /* long option */
175 case 'c': /* config file */
176 confbase = xstrdup(optarg);
177 confbasegiven = true;
180 case 'n': /* net name given */
181 netname = xstrdup(optarg);
184 case 1: /* show help */
188 case 2: /* show version */
192 case 3: /* open control socket here */
193 pidfilename = xstrdup(optarg);
200 case '?': /* wrong options */
209 if(!netname && (netname = getenv("NETNAME")))
210 netname = xstrdup(netname);
212 /* netname "." is special: a "top-level name" */
214 if(netname && (!*netname || !strcmp(netname, "."))) {
219 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
220 fprintf(stderr, "Invalid character in netname!\n");
227 /* Open a file with the desired permissions, minus the umask.
228 Also, if we want to create an executable file, we call fchmod()
229 to set the executable bits. */
231 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
232 mode_t mask = umask(0);
235 FILE *f = fopen(filename, mode);
237 if((perms & 0444) && f)
238 fchmod(fileno(f), perms);
244 static void disable_old_keys(const char *filename, const char *what) {
245 char tmpfile[PATH_MAX] = "";
247 bool disabled = false;
252 r = fopen(filename, "r");
256 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
258 struct stat st = {.st_mode = 0600};
259 fstat(fileno(r), &st);
260 w = fopenmask(tmpfile, "w", st.st_mode);
262 while(fgets(buf, sizeof buf, r)) {
263 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
264 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
270 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
276 if(block || ed25519pubkey)
278 if(fputs(buf, w) < 0) {
284 if(block && !strncmp(buf, "-----END ", 9))
291 if(ferror(r) || fclose(r) < 0)
296 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
303 // We cannot atomically replace files on Windows.
304 char bakfile[PATH_MAX] = "";
305 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
306 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
307 rename(bakfile, filename);
309 if(rename(tmpfile, filename)) {
311 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
316 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
323 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
329 /* Check stdin and stdout */
331 /* Ask for a file and/or directory name. */
332 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
334 if(fgets(buf, sizeof buf, stdin) == NULL) {
335 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
339 size_t len = strlen(buf);
348 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
350 if(filename[0] != '/') {
352 /* The directory is a relative path or a filename. */
353 directory = get_current_dir_name();
354 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
358 disable_old_keys(filename, what);
360 /* Open it first to keep the inode busy */
362 r = fopenmask(filename, mode, perms);
365 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
373 Generate a public/private Ed25519 keypair, and ask for a file to store
376 static bool ed25519_keygen(bool ask) {
379 char fname[PATH_MAX];
381 fprintf(stderr, "Generating Ed25519 keypair:\n");
383 if(!(key = ecdsa_generate())) {
384 fprintf(stderr, "Error during key generation!\n");
387 fprintf(stderr, "Done.\n");
389 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
390 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
395 if(!ecdsa_write_pem_private_key(key, f)) {
396 fprintf(stderr, "Error writing private key!\n");
403 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
405 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.pub", confbase);
407 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
412 char *pubkey = ecdsa_get_base64_public_key(key);
413 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
427 #ifndef DISABLE_LEGACY
429 Generate a public/private RSA keypair, and ask for a file to store
432 static bool rsa_keygen(int bits, bool ask) {
435 char fname[PATH_MAX];
437 // Make sure the key size is a multiple of 8 bits.
440 // Force them to be between 1024 and 8192 bits long.
446 fprintf(stderr, "Generating %d bits keys:\n", bits);
448 if(!(key = rsa_generate(bits, 0x10001))) {
449 fprintf(stderr, "Error during key generation!\n");
452 fprintf(stderr, "Done.\n");
454 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.priv", confbase);
455 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
460 if(!rsa_write_pem_private_key(key, f)) {
461 fprintf(stderr, "Error writing private key!\n");
468 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
470 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.pub", confbase);
472 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
477 if(!rsa_write_pem_public_key(key, f)) {
478 fprintf(stderr, "Error writing public key!\n");
498 bool recvline(int fd, char *line, size_t len) {
499 char *newline = NULL;
504 while(!(newline = memchr(buffer, '\n', blen))) {
505 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
506 if(result == -1 && sockerrno == EINTR)
513 if(newline - buffer >= len)
516 len = newline - buffer;
518 memcpy(line, buffer, len);
520 memmove(buffer, newline + 1, blen - len - 1);
526 bool recvdata(int fd, char *data, size_t len) {
531 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
532 if(result == -1 && sockerrno == EINTR)
539 memcpy(data, buffer, len);
540 memmove(buffer, buffer + len, blen - len);
546 bool sendline(int fd, char *format, ...) {
547 static char buffer[4096];
552 va_start(ap, format);
553 blen = vsnprintf(buffer, sizeof buffer, format, ap);
556 if(blen < 1 || blen >= sizeof buffer)
563 int result = send(fd, p, blen, MSG_NOSIGNAL);
564 if(result == -1 && sockerrno == EINTR)
575 static void pcap(int fd, FILE *out, int snaplen) {
576 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
584 uint32_t tz_accuracy;
591 snaplen ?: sizeof data,
604 fwrite(&header, sizeof header, 1, out);
608 while(recvline(fd, line, sizeof line)) {
610 int n = sscanf(line, "%d %d %d", &code, &req, &len);
611 gettimeofday(&tv, NULL);
612 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
614 if(!recvdata(fd, data, len))
616 packet.tv_sec = tv.tv_sec;
617 packet.tv_usec = tv.tv_usec;
619 packet.origlen = len;
620 fwrite(&packet, sizeof packet, 1, out);
621 fwrite(data, len, 1, out);
626 static void logcontrol(int fd, FILE *out, int level) {
627 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
631 while(recvline(fd, line, sizeof line)) {
633 int n = sscanf(line, "%d %d %d", &code, &req, &len);
634 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
636 if(!recvdata(fd, data, len))
638 fwrite(data, len, 1, out);
645 static bool remove_service(void) {
646 SC_HANDLE manager = NULL;
647 SC_HANDLE service = NULL;
648 SERVICE_STATUS status = {0};
650 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
652 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
656 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
659 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
663 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
664 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
666 fprintf(stderr, "%s service stopped\n", identname);
668 if(!DeleteService(service)) {
669 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
673 fprintf(stderr, "%s service removed\n", identname);
679 bool connect_tincd(bool verbose) {
684 struct timeval tv = {0, 0};
685 if(select(fd + 1, &r, NULL, NULL, &tv)) {
686 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
694 FILE *f = fopen(pidfilename, "r");
697 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
704 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
706 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
714 struct sockaddr_un sa;
715 sa.sun_family = AF_UNIX;
716 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
718 fd = socket(AF_UNIX, SOCK_STREAM, 0);
721 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
725 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
727 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
733 struct addrinfo hints = {
734 .ai_family = AF_UNSPEC,
735 .ai_socktype = SOCK_STREAM,
736 .ai_protocol = IPPROTO_TCP,
740 struct addrinfo *res = NULL;
742 if(getaddrinfo(host, port, &hints, &res) || !res) {
744 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
748 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
751 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
756 unsigned long arg = 0;
758 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
760 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
764 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
766 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
776 static const int one = 1;
777 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
783 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
785 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
791 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
793 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
795 fprintf(stderr, "Could not fully establish control socket connection\n");
805 static int cmd_start(int argc, char *argv[]) {
806 if(connect_tincd(false)) {
808 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
810 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
815 char *slash = strrchr(program_name, '/');
818 if ((c = strrchr(program_name, '\\')) > slash)
823 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
828 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
833 Windows has no real concept of an "argv array". A command line is just one string.
834 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
835 it uses quotes to handle spaces in arguments.
836 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
837 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
838 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
840 xasprintf(&arg0, "\"%s\"", arg0);
842 nargv[nargc++] = arg0;
843 for(int i = 1; i < optind; i++)
844 nargv[nargc++] = orig_argv[i];
845 for(int i = 1; i < argc; i++)
846 nargv[nargc++] = argv[i];
849 int status = spawnvp(_P_WAIT, c, nargv);
851 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
858 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
864 exit(execvp(c, nargv));
868 int status = -1, result;
870 signal(SIGINT, SIG_IGN);
872 result = waitpid(pid, &status, 0);
874 signal(SIGINT, SIG_DFL);
877 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
878 fprintf(stderr, "Error starting %s\n", c);
886 static int cmd_stop(int argc, char *argv[]) {
888 fprintf(stderr, "Too many arguments!\n");
893 if(!connect_tincd(true)) {
895 if(kill(pid, SIGTERM)) {
896 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
900 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
901 waitpid(pid, NULL, 0);
908 sendline(fd, "%d %d", CONTROL, REQ_STOP);
910 while(recvline(fd, line, sizeof line)) {
911 // Wait for tincd to close the connection...
914 if(!remove_service())
924 static int cmd_restart(int argc, char *argv[]) {
926 return cmd_start(argc, argv);
929 static int cmd_reload(int argc, char *argv[]) {
931 fprintf(stderr, "Too many arguments!\n");
935 if(!connect_tincd(true))
938 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
939 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
940 fprintf(stderr, "Could not reload configuration.\n");
948 static int dump_invitations(void) {
949 char dname[PATH_MAX];
950 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
951 DIR *dir = opendir(dname);
953 if(errno == ENOENT) {
954 fprintf(stderr, "No outstanding invitations.\n");
958 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
965 while((ent = readdir(dir))) {
966 char buf[MAX_STRING_SIZE];
967 if(b64decode(ent->d_name, buf, 24) != 18)
970 char fname[PATH_MAX];
971 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
972 FILE *f = fopen(fname, "r");
974 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
980 if(!fgets(buf, sizeof buf, f)) {
981 fprintf(stderr, "Invalid invitation file %s", fname);
987 char *eol = buf + strlen(buf);
988 while(strchr("\t \r\n", *--eol))
990 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
991 fprintf(stderr, "Invalid invitation file %s", fname);
996 printf("%s %s\n", ent->d_name, buf + 7);
1002 fprintf(stderr, "No outstanding invitations.\n");
1007 static int cmd_dump(int argc, char *argv[]) {
1008 bool only_reachable = false;
1010 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1011 if(strcasecmp(argv[2], "nodes")) {
1012 fprintf(stderr, "`reachable' only supported for nodes.\n");
1016 only_reachable = true;
1022 fprintf(stderr, "Invalid number of arguments.\n");
1027 if(!strcasecmp(argv[1], "invitations"))
1028 return dump_invitations();
1030 if(!connect_tincd(true))
1035 if(!strcasecmp(argv[1], "nodes"))
1036 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1037 else if(!strcasecmp(argv[1], "edges"))
1038 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1039 else if(!strcasecmp(argv[1], "subnets"))
1040 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1041 else if(!strcasecmp(argv[1], "connections"))
1042 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1043 else if(!strcasecmp(argv[1], "graph")) {
1044 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1045 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1047 } else if(!strcasecmp(argv[1], "digraph")) {
1048 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1049 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1052 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1058 printf("graph {\n");
1059 else if(do_graph == 2)
1060 printf("digraph {\n");
1062 while(recvline(fd, line, sizeof line)) {
1063 char node1[4096], node2[4096];
1064 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1066 if(do_graph && req == REQ_DUMP_NODES)
1084 char local_host[4096];
1085 char local_port[4096];
1088 int cipher, digest, maclength, compression, distance, socket, weight;
1089 short int pmtu, minmtu, maxmtu;
1090 unsigned int options, status_int;
1091 node_status_t status;
1092 long int last_state_change;
1095 case REQ_DUMP_NODES: {
1096 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);
1098 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1102 memcpy(&status, &status_int, sizeof status);
1105 const char *color = "black";
1106 if(!strcmp(host, "MYSELF"))
1108 else if(!status.reachable)
1110 else if(strcmp(via, node))
1112 else if(!status.validkey)
1116 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1118 if(only_reachable && !status.reachable)
1120 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",
1121 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1125 case REQ_DUMP_EDGES: {
1126 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);
1128 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1133 float w = 1 + 65536.0 / weight;
1134 if(do_graph == 1 && strcmp(node1, node2) > 0)
1135 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1136 else if(do_graph == 2)
1137 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1139 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);
1143 case REQ_DUMP_SUBNETS: {
1144 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1146 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1149 printf("%s owner %s\n", strip_weight(subnet), node);
1152 case REQ_DUMP_CONNECTIONS: {
1153 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1155 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1158 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1162 fprintf(stderr, "Unable to parse dump from tincd.\n");
1167 fprintf(stderr, "Error receiving dump.\n");
1171 static int cmd_purge(int argc, char *argv[]) {
1173 fprintf(stderr, "Too many arguments!\n");
1177 if(!connect_tincd(true))
1180 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1181 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1182 fprintf(stderr, "Could not purge old information.\n");
1189 static int cmd_debug(int argc, char *argv[]) {
1191 fprintf(stderr, "Invalid number of arguments.\n");
1195 if(!connect_tincd(true))
1198 int debuglevel = atoi(argv[1]);
1201 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1202 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1203 fprintf(stderr, "Could not set debug level.\n");
1207 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1211 static int cmd_retry(int argc, char *argv[]) {
1213 fprintf(stderr, "Too many arguments!\n");
1217 if(!connect_tincd(true))
1220 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1221 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1222 fprintf(stderr, "Could not retry outgoing connections.\n");
1229 static int cmd_connect(int argc, char *argv[]) {
1231 fprintf(stderr, "Invalid number of arguments.\n");
1235 if(!check_id(argv[1])) {
1236 fprintf(stderr, "Invalid name for node.\n");
1240 if(!connect_tincd(true))
1243 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1244 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1245 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1252 static int cmd_disconnect(int argc, char *argv[]) {
1254 fprintf(stderr, "Invalid number of arguments.\n");
1258 if(!check_id(argv[1])) {
1259 fprintf(stderr, "Invalid name for node.\n");
1263 if(!connect_tincd(true))
1266 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1267 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1268 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1275 static int cmd_top(int argc, char *argv[]) {
1277 fprintf(stderr, "Too many arguments!\n");
1282 if(!connect_tincd(true))
1288 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1293 static int cmd_pcap(int argc, char *argv[]) {
1295 fprintf(stderr, "Too many arguments!\n");
1299 if(!connect_tincd(true))
1302 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1307 static void sigint_handler(int sig) {
1308 fprintf(stderr, "\n");
1309 shutdown(fd, SHUT_RDWR);
1313 static int cmd_log(int argc, char *argv[]) {
1315 fprintf(stderr, "Too many arguments!\n");
1319 if(!connect_tincd(true))
1323 signal(SIGINT, sigint_handler);
1326 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1329 signal(SIGINT, SIG_DFL);
1337 static int cmd_pid(int argc, char *argv[]) {
1339 fprintf(stderr, "Too many arguments!\n");
1343 if(!connect_tincd(true) && !pid)
1346 printf("%d\n", pid);
1350 int rstrip(char *value) {
1351 int len = strlen(value);
1352 while(len && strchr("\t\r\n ", value[len - 1]))
1357 char *get_my_name(bool verbose) {
1358 FILE *f = fopen(tinc_conf, "r");
1361 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1367 while(fgets(buf, sizeof buf, f)) {
1368 int len = strcspn(buf, "\t =");
1370 value += strspn(value, "\t ");
1373 value += strspn(value, "\t ");
1378 if(strcasecmp(buf, "Name"))
1382 return replace_name(value);
1388 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1392 const var_t variables[] = {
1393 /* Server configuration */
1394 {"AddressFamily", VAR_SERVER},
1395 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1396 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1397 {"BindToInterface", VAR_SERVER},
1398 {"Broadcast", VAR_SERVER | VAR_SAFE},
1399 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1400 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1401 {"DecrementTTL", VAR_SERVER},
1402 {"Device", VAR_SERVER},
1403 {"DeviceStandby", VAR_SERVER},
1404 {"DeviceType", VAR_SERVER},
1405 {"DirectOnly", VAR_SERVER},
1406 {"Ed25519PrivateKeyFile", VAR_SERVER},
1407 {"ExperimentalProtocol", VAR_SERVER},
1408 {"Forwarding", VAR_SERVER},
1409 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1410 {"Hostnames", VAR_SERVER},
1411 {"IffOneQueue", VAR_SERVER},
1412 {"Interface", VAR_SERVER},
1413 {"KeyExpire", VAR_SERVER},
1414 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1415 {"LocalDiscovery", VAR_SERVER},
1416 {"MACExpire", VAR_SERVER},
1417 {"MaxConnectionBurst", VAR_SERVER},
1418 {"MaxOutputBufferSize", VAR_SERVER},
1419 {"MaxTimeout", VAR_SERVER},
1420 {"Mode", VAR_SERVER | VAR_SAFE},
1421 {"Name", VAR_SERVER},
1422 {"PingInterval", VAR_SERVER},
1423 {"PingTimeout", VAR_SERVER},
1424 {"PriorityInheritance", VAR_SERVER},
1425 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1426 {"PrivateKeyFile", VAR_SERVER},
1427 {"ProcessPriority", VAR_SERVER},
1428 {"Proxy", VAR_SERVER},
1429 {"ReplayWindow", VAR_SERVER},
1430 {"ScriptsExtension", VAR_SERVER},
1431 {"ScriptsInterpreter", VAR_SERVER},
1432 {"StrictSubnets", VAR_SERVER},
1433 {"TunnelServer", VAR_SERVER},
1434 {"UDPDiscovery", VAR_SERVER},
1435 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1436 {"UDPDiscoveryInterval", VAR_SERVER},
1437 {"UDPDiscoveryTimeout", VAR_SERVER},
1438 {"MTUInfoInterval", VAR_SERVER},
1439 {"UDPInfoInterval", VAR_SERVER},
1440 {"UDPRcvBuf", VAR_SERVER},
1441 {"UDPSndBuf", VAR_SERVER},
1442 {"VDEGroup", VAR_SERVER},
1443 {"VDEPort", VAR_SERVER},
1444 /* Host configuration */
1445 {"Address", VAR_HOST | VAR_MULTIPLE},
1446 {"Cipher", VAR_SERVER | VAR_HOST},
1447 {"ClampMSS", VAR_SERVER | VAR_HOST},
1448 {"Compression", VAR_SERVER | VAR_HOST},
1449 {"Digest", VAR_SERVER | VAR_HOST},
1450 {"Ed25519PublicKey", VAR_HOST},
1451 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1452 {"IndirectData", VAR_SERVER | VAR_HOST},
1453 {"MACLength", VAR_SERVER | VAR_HOST},
1454 {"PMTU", VAR_SERVER | VAR_HOST},
1455 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1457 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1458 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1459 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1460 {"TCPOnly", VAR_SERVER | VAR_HOST},
1461 {"Weight", VAR_HOST | VAR_SAFE},
1465 static int cmd_config(int argc, char *argv[]) {
1467 fprintf(stderr, "Invalid number of arguments.\n");
1471 if(strcasecmp(argv[0], "config"))
1475 if(!strcasecmp(argv[1], "get")) {
1477 } else if(!strcasecmp(argv[1], "add")) {
1478 argv++, argc--, action = 1;
1479 } else if(!strcasecmp(argv[1], "del")) {
1480 argv++, argc--, action = -1;
1481 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1482 argv++, argc--, action = 0;
1486 fprintf(stderr, "Invalid number of arguments.\n");
1490 // Concatenate the rest of the command line
1491 strncpy(line, argv[1], sizeof line - 1);
1492 for(int i = 2; i < argc; i++) {
1493 strncat(line, " ", sizeof line - 1 - strlen(line));
1494 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1497 // Liberal parsing into node name, variable name and value.
1503 len = strcspn(line, "\t =");
1505 value += strspn(value, "\t ");
1508 value += strspn(value, "\t ");
1511 variable = strchr(line, '.');
1520 fprintf(stderr, "No variable given.\n");
1524 if(action >= 0 && !*value) {
1525 fprintf(stderr, "No value for variable given.\n");
1529 if(action < -1 && *value)
1532 /* Some simple checks. */
1534 bool warnonremove = false;
1536 for(int i = 0; variables[i].name; i++) {
1537 if(strcasecmp(variables[i].name, variable))
1541 variable = (char *)variables[i].name;
1543 /* Discourage use of obsolete variables. */
1545 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1547 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1549 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1554 /* Don't put server variables in host config files */
1556 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1558 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1560 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1565 /* Should this go into our own host config file? */
1567 if(!node && !(variables[i].type & VAR_SERVER)) {
1568 node = get_my_name(true);
1573 /* Change "add" into "set" for variables that do not allow multiple occurences.
1574 Turn on warnings when it seems variables might be removed unintentionally. */
1576 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1577 warnonremove = true;
1579 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1580 warnonremove = true;
1586 if(node && !check_id(node)) {
1587 fprintf(stderr, "Invalid name for node.\n");
1592 if(force || action < 0) {
1593 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1595 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1600 // Open the right configuration file.
1601 char filename[PATH_MAX];
1603 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1605 snprintf(filename, sizeof filename, "%s", tinc_conf);
1607 FILE *f = fopen(filename, "r");
1609 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1613 char tmpfile[PATH_MAX];
1617 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1618 tf = fopen(tmpfile, "w");
1620 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1626 // Copy the file, making modifications on the fly, unless we are just getting a value.
1630 bool removed = false;
1633 while(fgets(buf1, sizeof buf1, f)) {
1634 buf1[sizeof buf1 - 1] = 0;
1635 strncpy(buf2, buf1, sizeof buf2);
1637 // Parse line in a simple way
1641 len = strcspn(buf2, "\t =");
1642 bvalue = buf2 + len;
1643 bvalue += strspn(bvalue, "\t ");
1644 if(*bvalue == '=') {
1646 bvalue += strspn(bvalue, "\t ");
1652 if(!strcasecmp(buf2, variable)) {
1656 printf("%s\n", bvalue);
1658 } else if(action == -1) {
1659 if(!*value || !strcasecmp(bvalue, value)) {
1664 } else if(action == 0) {
1665 // Warn if "set" was used for variables that can occur multiple times
1666 if(warnonremove && strcasecmp(bvalue, value))
1667 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1669 // Already set? Delete the rest...
1673 // Otherwise, replace.
1674 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1675 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1681 } else if(action > 0) {
1682 // Check if we've already seen this variable with the same value
1683 if(!strcasecmp(bvalue, value))
1689 // Copy original line...
1690 if(fputs(buf1, tf) < 0) {
1691 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1695 // Add newline if it is missing...
1696 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1697 if(fputc('\n', tf) < 0) {
1698 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1705 // Make sure we read everything...
1706 if(ferror(f) || !feof(f)) {
1707 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1712 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1716 // Add new variable if necessary.
1717 if((action > 0 && !found)|| (action == 0 && !set)) {
1718 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1719 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1728 fprintf(stderr, "No matching configuration variables found.\n");
1733 // Make sure we wrote everything...
1735 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1739 // Could we find what we had to remove?
1740 if(action < 0 && !removed) {
1742 fprintf(stderr, "No configuration variables deleted.\n");
1746 // Replace the configuration file with the new one
1748 if(remove(filename)) {
1749 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1753 if(rename(tmpfile, filename)) {
1754 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1758 // Silently try notifying a running tincd of changes.
1759 if(connect_tincd(false))
1760 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1765 static bool try_bind(int port) {
1766 struct addrinfo *ai = NULL;
1767 struct addrinfo hint = {
1768 .ai_flags = AI_PASSIVE,
1769 .ai_family = AF_UNSPEC,
1770 .ai_socktype = SOCK_STREAM,
1771 .ai_protocol = IPPROTO_TCP,
1775 snprintf(portstr, sizeof portstr, "%d", port);
1777 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1781 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1784 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1794 int check_port(char *name) {
1798 fprintf(stderr, "Warning: could not bind to port 655. ");
1800 for(int i = 0; i < 100; i++) {
1801 int port = 0x1000 + (rand() & 0x7fff);
1802 if(try_bind(port)) {
1803 char filename[PATH_MAX];
1804 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1805 FILE *f = fopen(filename, "a");
1807 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1808 fprintf(stderr, "Please change tinc's Port manually.\n");
1812 fprintf(f, "Port = %d\n", port);
1814 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1819 fprintf(stderr, "Please change tinc's Port manually.\n");
1823 static int cmd_init(int argc, char *argv[]) {
1824 if(!access(tinc_conf, F_OK)) {
1825 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1830 fprintf(stderr, "Too many arguments!\n");
1832 } else if(argc < 2) {
1835 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1836 if(!fgets(buf, sizeof buf, stdin)) {
1837 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1840 int len = rstrip(buf);
1842 fprintf(stderr, "No name given!\n");
1847 fprintf(stderr, "No Name given!\n");
1851 name = strdup(argv[1]);
1853 fprintf(stderr, "No Name given!\n");
1858 if(!check_id(name)) {
1859 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1863 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1864 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1868 if(mkdir(confbase, 0777) && errno != EEXIST) {
1869 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1873 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1874 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1878 FILE *f = fopen(tinc_conf, "w");
1880 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1884 fprintf(f, "Name = %s\n", name);
1887 #ifndef DISABLE_LEGACY
1888 if(!rsa_keygen(2048, false))
1892 if(!ed25519_keygen(false))
1898 char filename[PATH_MAX];
1899 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1900 if(access(filename, F_OK)) {
1901 FILE *f = fopenmask(filename, "w", 0777);
1903 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1906 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");
1915 static int cmd_generate_keys(int argc, char *argv[]) {
1916 #ifdef DISABLE_LEGACY
1921 fprintf(stderr, "Too many arguments!\n");
1926 name = get_my_name(false);
1928 #ifndef DISABLE_LEGACY
1929 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1933 if(!ed25519_keygen(true))
1939 #ifndef DISABLE_LEGACY
1940 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1942 fprintf(stderr, "Too many arguments!\n");
1947 name = get_my_name(false);
1949 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1953 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1955 fprintf(stderr, "Too many arguments!\n");
1960 name = get_my_name(false);
1962 return !ed25519_keygen(true);
1965 static int cmd_help(int argc, char *argv[]) {
1970 static int cmd_version(int argc, char *argv[]) {
1972 fprintf(stderr, "Too many arguments!\n");
1980 static int cmd_info(int argc, char *argv[]) {
1982 fprintf(stderr, "Invalid number of arguments.\n");
1986 if(!connect_tincd(true))
1989 return info(fd, argv[1]);
1992 static const char *conffiles[] = {
2003 static int cmd_edit(int argc, char *argv[]) {
2005 fprintf(stderr, "Invalid number of arguments.\n");
2009 char filename[PATH_MAX] = "";
2011 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2012 for(int i = 0; conffiles[i]; i++) {
2013 if(!strcmp(argv[1], conffiles[i])) {
2014 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2023 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2024 char *dash = strchr(argv[1], '-');
2027 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2028 fprintf(stderr, "Invalid configuration filename.\n");
2036 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2038 xasprintf(&command, "edit \"%s\"", filename);
2040 int result = system(command);
2045 // Silently try notifying a running tincd of changes.
2046 if(connect_tincd(false))
2047 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2052 static int export(const char *name, FILE *out) {
2053 char filename[PATH_MAX];
2054 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2055 FILE *in = fopen(filename, "r");
2057 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2061 fprintf(out, "Name = %s\n", name);
2063 while(fgets(buf, sizeof buf, in)) {
2064 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2069 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2078 static int cmd_export(int argc, char *argv[]) {
2080 fprintf(stderr, "Too many arguments!\n");
2084 char *name = get_my_name(true);
2088 int result = export(name, stdout);
2096 static int cmd_export_all(int argc, char *argv[]) {
2098 fprintf(stderr, "Too many arguments!\n");
2102 DIR *dir = opendir(hosts_dir);
2104 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2112 while((ent = readdir(dir))) {
2113 if(!check_id(ent->d_name))
2119 printf("#---------------------------------------------------------------#\n");
2121 result |= export(ent->d_name, stdout);
2130 static int cmd_import(int argc, char *argv[]) {
2132 fprintf(stderr, "Too many arguments!\n");
2141 char filename[PATH_MAX] = "";
2143 bool firstline = true;
2145 while(fgets(buf, sizeof buf, in)) {
2146 if(sscanf(buf, "Name = %s", name) == 1) {
2149 if(!check_id(name)) {
2150 fprintf(stderr, "Invalid Name in input!\n");
2157 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2159 if(!force && !access(filename, F_OK)) {
2160 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2165 out = fopen(filename, "w");
2167 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2173 } else if(firstline) {
2174 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2179 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2183 if(fputs(buf, out) < 0) {
2184 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2194 fprintf(stderr, "Imported %d host configuration files.\n", count);
2197 fprintf(stderr, "No host configuration files imported.\n");
2202 static int cmd_exchange(int argc, char *argv[]) {
2203 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2206 static int cmd_exchange_all(int argc, char *argv[]) {
2207 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2210 static int switch_network(char *name) {
2222 free(unixsocketname);
2223 unixsocketname = NULL;
2229 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2231 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2232 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2233 xasprintf(&prompt, "%s> ", identname);
2238 static int cmd_network(int argc, char *argv[]) {
2240 fprintf(stderr, "Too many arguments!\n");
2245 return switch_network(argv[1]);
2247 DIR *dir = opendir(confdir);
2249 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2254 while((ent = readdir(dir))) {
2255 if(*ent->d_name == '.')
2258 if(!strcmp(ent->d_name, "tinc.conf")) {
2263 char fname[PATH_MAX];
2264 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2265 if(!access(fname, R_OK))
2266 printf("%s\n", ent->d_name);
2274 static int cmd_fsck(int argc, char *argv[]) {
2276 fprintf(stderr, "Too many arguments!\n");
2280 return fsck(orig_argv[0]);
2283 static const struct {
2284 const char *command;
2285 int (*function)(int argc, char *argv[]);
2288 {"start", cmd_start},
2290 {"restart", cmd_restart},
2291 {"reload", cmd_reload},
2294 {"purge", cmd_purge},
2295 {"debug", cmd_debug},
2296 {"retry", cmd_retry},
2297 {"connect", cmd_connect},
2298 {"disconnect", cmd_disconnect},
2303 {"config", cmd_config, true},
2304 {"add", cmd_config},
2305 {"del", cmd_config},
2306 {"get", cmd_config},
2307 {"set", cmd_config},
2309 {"generate-keys", cmd_generate_keys},
2310 #ifndef DISABLE_LEGACY
2311 {"generate-rsa-keys", cmd_generate_rsa_keys},
2313 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2315 {"version", cmd_version},
2318 {"export", cmd_export},
2319 {"export-all", cmd_export_all},
2320 {"import", cmd_import},
2321 {"exchange", cmd_exchange},
2322 {"exchange-all", cmd_exchange_all},
2323 {"invite", cmd_invite},
2325 {"network", cmd_network},
2330 #ifdef HAVE_READLINE
2331 static char *complete_command(const char *text, int state) {
2339 while(commands[i].command) {
2340 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2341 return xstrdup(commands[i].command);
2348 static char *complete_dump(const char *text, int state) {
2349 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2358 if(!strncasecmp(matches[i], text, strlen(text)))
2359 return xstrdup(matches[i]);
2366 static char *complete_config(const char *text, int state) {
2374 while(variables[i].name) {
2375 char *dot = strchr(text, '.');
2377 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2379 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2383 if(!strncasecmp(variables[i].name, text, strlen(text)))
2384 return xstrdup(variables[i].name);
2392 static char *complete_info(const char *text, int state) {
2396 if(!connect_tincd(false))
2398 // Check the list of nodes
2399 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2400 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2403 while(recvline(fd, line, sizeof line)) {
2405 int n = sscanf(line, "%d %d %s", &code, &req, item);
2415 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2419 if(!strncmp(item, text, strlen(text)))
2420 return xstrdup(strip_weight(item));
2426 static char *complete_nothing(const char *text, int state) {
2430 static char **completion (const char *text, int start, int end) {
2431 char **matches = NULL;
2434 matches = rl_completion_matches(text, complete_command);
2435 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2436 matches = rl_completion_matches(text, complete_dump);
2437 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2438 matches = rl_completion_matches(text, complete_config);
2439 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2440 matches = rl_completion_matches(text, complete_config);
2441 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2442 matches = rl_completion_matches(text, complete_config);
2443 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2444 matches = rl_completion_matches(text, complete_config);
2445 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2446 matches = rl_completion_matches(text, complete_info);
2452 static int cmd_shell(int argc, char *argv[]) {
2453 xasprintf(&prompt, "%s> ", identname);
2457 int maxargs = argc + 16;
2458 char **nargv = xmalloc(maxargs * sizeof *nargv);
2460 for(int i = 0; i < argc; i++)
2463 #ifdef HAVE_READLINE
2464 rl_readline_name = "tinc";
2465 rl_completion_entry_function = complete_nothing;
2466 rl_attempted_completion_function = completion;
2467 rl_filename_completion_desired = 0;
2472 #ifdef HAVE_READLINE
2476 rl_basic_word_break_characters = "\t\n ";
2477 line = readline(prompt);
2479 copy = xstrdup(line);
2481 line = fgets(buf, sizeof buf, stdin);
2485 fputs(prompt, stdout);
2487 line = fgets(buf, sizeof buf, stdin);
2493 /* Ignore comments */
2501 char *p = line + strspn(line, " \t\n");
2502 char *next = strtok(p, " \t\n");
2505 if(nargc >= maxargs) {
2506 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2509 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2514 next = strtok(NULL, " \t\n");
2520 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2525 for(int i = 0; commands[i].command; i++) {
2526 if(!strcasecmp(nargv[argc], commands[i].command)) {
2527 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2533 #ifdef HAVE_READLINE
2539 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2552 int main(int argc, char *argv[]) {
2553 program_name = argv[0];
2556 tty = isatty(0) && isatty(1);
2558 if(!parse_options(argc, argv))
2562 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2563 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2576 static struct WSAData wsa_state;
2578 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2579 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2588 return cmd_shell(argc, argv);
2590 for(int i = 0; commands[i].command; i++) {
2591 if(!strcasecmp(argv[optind], commands[i].command))
2592 return commands[i].function(argc - optind, argv + optind);
2595 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);