2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2017 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-2016 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"
157 " sign [FILE] Generate a signed version of a file.\n"
158 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
160 printf("Report bugs to tinc@tinc-vpn.org.\n");
164 static bool parse_options(int argc, char **argv) {
166 int option_index = 0;
168 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
170 case 0: /* long option */
177 case 'c': /* config file */
178 confbase = xstrdup(optarg);
179 confbasegiven = true;
182 case 'n': /* net name given */
183 netname = xstrdup(optarg);
186 case 1: /* show help */
190 case 2: /* show version */
194 case 3: /* open control socket here */
195 pidfilename = xstrdup(optarg);
202 case '?': /* wrong options */
211 if(!netname && (netname = getenv("NETNAME")))
212 netname = xstrdup(netname);
214 /* netname "." is special: a "top-level name" */
216 if(netname && (!*netname || !strcmp(netname, "."))) {
221 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
222 fprintf(stderr, "Invalid character in netname!\n");
229 /* Open a file with the desired permissions, minus the umask.
230 Also, if we want to create an executable file, we call fchmod()
231 to set the executable bits. */
233 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
234 mode_t mask = umask(0);
237 FILE *f = fopen(filename, mode);
240 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
245 if((perms & 0444) && f)
246 fchmod(fileno(f), perms);
252 static void disable_old_keys(const char *filename, const char *what) {
253 char tmpfile[PATH_MAX] = "";
255 bool disabled = false;
260 r = fopen(filename, "r");
264 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
266 struct stat st = {.st_mode = 0600};
267 fstat(fileno(r), &st);
268 w = fopenmask(tmpfile, "w", st.st_mode);
270 while(fgets(buf, sizeof buf, r)) {
271 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
272 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
278 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
284 if(block || ed25519pubkey)
286 if(fputs(buf, w) < 0) {
292 if(block && !strncmp(buf, "-----END ", 9))
299 if(ferror(r) || fclose(r) < 0)
304 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
311 // We cannot atomically replace files on Windows.
312 char bakfile[PATH_MAX] = "";
313 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
314 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
315 rename(bakfile, filename);
317 if(rename(tmpfile, filename)) {
319 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
324 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
331 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
333 char directory[PATH_MAX] = ".";
337 /* Check stdin and stdout */
339 /* Ask for a file and/or directory name. */
340 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
342 if(fgets(buf, sizeof buf, stdin) == NULL) {
343 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
347 size_t len = strlen(buf);
356 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
358 if(filename[0] != '/') {
360 /* The directory is a relative path or a filename. */
361 getcwd(directory, sizeof directory);
362 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
366 disable_old_keys(filename, what);
368 /* Open it first to keep the inode busy */
370 r = fopenmask(filename, mode, perms);
373 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
381 Generate a public/private Ed25519 keypair, and ask for a file to store
384 static bool ed25519_keygen(bool ask) {
387 char fname[PATH_MAX];
389 fprintf(stderr, "Generating Ed25519 keypair:\n");
391 if(!(key = ecdsa_generate())) {
392 fprintf(stderr, "Error during key generation!\n");
395 fprintf(stderr, "Done.\n");
397 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
398 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
403 if(!ecdsa_write_pem_private_key(key, f)) {
404 fprintf(stderr, "Error writing private key!\n");
411 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
413 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.pub", confbase);
415 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
420 char *pubkey = ecdsa_get_base64_public_key(key);
421 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
436 #ifndef DISABLE_LEGACY
438 Generate a public/private RSA keypair, and ask for a file to store
441 static bool rsa_keygen(int bits, bool ask) {
444 char fname[PATH_MAX];
446 // Make sure the key size is a multiple of 8 bits.
449 // Make sure that a valid key size is used.
450 if(bits < 1024 || bits > 8192) {
451 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
453 } else if(bits < 2048) {
454 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
457 fprintf(stderr, "Generating %d bits keys:\n", bits);
459 if(!(key = rsa_generate(bits, 0x10001))) {
460 fprintf(stderr, "Error during key generation!\n");
463 fprintf(stderr, "Done.\n");
465 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.priv", confbase);
466 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
471 if(!rsa_write_pem_private_key(key, f)) {
472 fprintf(stderr, "Error writing private key!\n");
479 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
481 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.pub", confbase);
483 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
488 if(!rsa_write_pem_public_key(key, f)) {
489 fprintf(stderr, "Error writing public key!\n");
509 bool recvline(int fd, char *line, size_t len) {
510 char *newline = NULL;
515 while(!(newline = memchr(buffer, '\n', blen))) {
516 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
517 if(result == -1 && sockerrno == EINTR)
524 if(newline - buffer >= len)
527 len = newline - buffer;
529 memcpy(line, buffer, len);
531 memmove(buffer, newline + 1, blen - len - 1);
537 bool recvdata(int fd, char *data, size_t len) {
542 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
543 if(result == -1 && sockerrno == EINTR)
550 memcpy(data, buffer, len);
551 memmove(buffer, buffer + len, blen - len);
557 bool sendline(int fd, char *format, ...) {
558 static char buffer[4096];
563 va_start(ap, format);
564 blen = vsnprintf(buffer, sizeof buffer, format, ap);
565 buffer[sizeof buffer - 1] = 0;
568 if(blen < 1 || blen >= sizeof buffer)
575 int result = send(fd, p, blen, MSG_NOSIGNAL);
576 if(result == -1 && sockerrno == EINTR)
587 static void pcap(int fd, FILE *out, int snaplen) {
588 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
596 uint32_t tz_accuracy;
603 snaplen ?: sizeof data,
616 fwrite(&header, sizeof header, 1, out);
620 while(recvline(fd, line, sizeof line)) {
622 int n = sscanf(line, "%d %d %d", &code, &req, &len);
623 gettimeofday(&tv, NULL);
624 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
626 if(!recvdata(fd, data, len))
628 packet.tv_sec = tv.tv_sec;
629 packet.tv_usec = tv.tv_usec;
631 packet.origlen = len;
632 fwrite(&packet, sizeof packet, 1, out);
633 fwrite(data, len, 1, out);
638 static void logcontrol(int fd, FILE *out, int level) {
639 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
643 while(recvline(fd, line, sizeof line)) {
645 int n = sscanf(line, "%d %d %d", &code, &req, &len);
646 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
648 if(!recvdata(fd, data, len))
650 fwrite(data, len, 1, out);
657 static bool remove_service(void) {
658 SC_HANDLE manager = NULL;
659 SC_HANDLE service = NULL;
660 SERVICE_STATUS status = {0};
662 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
664 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
668 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
671 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
675 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
676 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
678 fprintf(stderr, "%s service stopped\n", identname);
680 if(!DeleteService(service)) {
681 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
685 fprintf(stderr, "%s service removed\n", identname);
691 bool connect_tincd(bool verbose) {
696 struct timeval tv = {0, 0};
697 if(select(fd + 1, &r, NULL, NULL, &tv)) {
698 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
706 FILE *f = fopen(pidfilename, "r");
709 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
716 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
718 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
726 if ((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
727 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
728 /* clean up the stale socket and pid file */
730 unlink(unixsocketname);
734 struct sockaddr_un sa;
735 sa.sun_family = AF_UNIX;
736 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
738 fd = socket(AF_UNIX, SOCK_STREAM, 0);
741 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
745 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
747 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
753 struct addrinfo hints = {
754 .ai_family = AF_UNSPEC,
755 .ai_socktype = SOCK_STREAM,
756 .ai_protocol = IPPROTO_TCP,
760 struct addrinfo *res = NULL;
762 if(getaddrinfo(host, port, &hints, &res) || !res) {
764 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
768 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
771 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
776 unsigned long arg = 0;
778 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
780 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
784 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
786 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
796 static const int one = 1;
797 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
803 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
805 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
811 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
813 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
815 fprintf(stderr, "Could not fully establish control socket connection\n");
825 static int cmd_start(int argc, char *argv[]) {
826 if(connect_tincd(false)) {
828 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
830 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
835 char *slash = strrchr(program_name, '/');
838 if ((c = strrchr(program_name, '\\')) > slash)
843 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
848 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
853 Windows has no real concept of an "argv array". A command line is just one string.
854 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
855 it uses quotes to handle spaces in arguments.
856 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
857 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
858 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
860 xasprintf(&arg0, "\"%s\"", arg0);
862 nargv[nargc++] = arg0;
863 for(int i = 1; i < optind; i++)
864 nargv[nargc++] = orig_argv[i];
865 for(int i = 1; i < argc; i++)
866 nargv[nargc++] = argv[i];
869 int status = spawnvp(_P_WAIT, c, nargv);
871 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
876 int pfd[2] = {-1, -1};
877 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
878 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
885 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
893 snprintf(buf, sizeof buf, "%d", pfd[1]);
894 setenv("TINC_UMBILICAL", buf, true);
895 exit(execvp(c, nargv));
902 int status = -1, result;
904 signal(SIGINT, SIG_IGN);
907 // Pass all log messages from the umbilical to stderr.
908 // A nul-byte right before closure means tincd started succesfully.
913 while((len = read(pfd[0], buf, sizeof buf)) > 0) {
914 failure = buf[len - 1];
925 // Make sure the child process is really gone.
926 result = waitpid(pid, &status, 0);
929 signal(SIGINT, SIG_DFL);
932 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
933 fprintf(stderr, "Error starting %s\n", c);
941 static int cmd_stop(int argc, char *argv[]) {
943 fprintf(stderr, "Too many arguments!\n");
948 if(!connect_tincd(true)) {
950 if(kill(pid, SIGTERM)) {
951 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
955 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
956 waitpid(pid, NULL, 0);
963 sendline(fd, "%d %d", CONTROL, REQ_STOP);
965 while(recvline(fd, line, sizeof line)) {
966 // Wait for tincd to close the connection...
969 if(!remove_service())
979 static int cmd_restart(int argc, char *argv[]) {
981 return cmd_start(argc, argv);
984 static int cmd_reload(int argc, char *argv[]) {
986 fprintf(stderr, "Too many arguments!\n");
990 if(!connect_tincd(true))
993 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
994 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
995 fprintf(stderr, "Could not reload configuration.\n");
1003 static int dump_invitations(void) {
1004 char dname[PATH_MAX];
1005 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
1006 DIR *dir = opendir(dname);
1008 if(errno == ENOENT) {
1009 fprintf(stderr, "No outstanding invitations.\n");
1013 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1020 while((ent = readdir(dir))) {
1021 char buf[MAX_STRING_SIZE];
1022 if(b64decode(ent->d_name, buf, 24) != 18)
1025 char fname[PATH_MAX];
1026 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
1027 FILE *f = fopen(fname, "r");
1029 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1035 if(!fgets(buf, sizeof buf, f)) {
1036 fprintf(stderr, "Invalid invitation file %s", fname);
1042 char *eol = buf + strlen(buf);
1043 while(strchr("\t \r\n", *--eol))
1045 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1046 fprintf(stderr, "Invalid invitation file %s", fname);
1051 printf("%s %s\n", ent->d_name, buf + 7);
1057 fprintf(stderr, "No outstanding invitations.\n");
1062 static int cmd_dump(int argc, char *argv[]) {
1063 bool only_reachable = false;
1065 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1066 if(strcasecmp(argv[2], "nodes")) {
1067 fprintf(stderr, "`reachable' only supported for nodes.\n");
1071 only_reachable = true;
1077 fprintf(stderr, "Invalid number of arguments.\n");
1082 if(!strcasecmp(argv[1], "invitations"))
1083 return dump_invitations();
1085 if(!connect_tincd(true))
1090 if(!strcasecmp(argv[1], "nodes"))
1091 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1092 else if(!strcasecmp(argv[1], "edges"))
1093 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1094 else if(!strcasecmp(argv[1], "subnets"))
1095 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1096 else if(!strcasecmp(argv[1], "connections"))
1097 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1098 else if(!strcasecmp(argv[1], "graph")) {
1099 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1100 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1102 } else if(!strcasecmp(argv[1], "digraph")) {
1103 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1104 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1107 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1113 printf("graph {\n");
1114 else if(do_graph == 2)
1115 printf("digraph {\n");
1117 while(recvline(fd, line, sizeof line)) {
1118 char node1[4096], node2[4096];
1119 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1121 if(do_graph && req == REQ_DUMP_NODES)
1139 char local_host[4096];
1140 char local_port[4096];
1143 int cipher, digest, maclength, compression, distance, socket, weight;
1144 short int pmtu, minmtu, maxmtu;
1145 unsigned int options, status_int;
1146 node_status_t status;
1147 long int last_state_change;
1150 case REQ_DUMP_NODES: {
1151 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);
1153 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1157 memcpy(&status, &status_int, sizeof status);
1160 const char *color = "black";
1161 if(!strcmp(host, "MYSELF"))
1163 else if(!status.reachable)
1165 else if(strcmp(via, node))
1167 else if(!status.validkey)
1171 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1173 if(only_reachable && !status.reachable)
1175 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",
1176 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1180 case REQ_DUMP_EDGES: {
1181 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);
1183 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1188 float w = 1 + 65536.0 / weight;
1189 if(do_graph == 1 && strcmp(node1, node2) > 0)
1190 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1191 else if(do_graph == 2)
1192 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1194 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);
1198 case REQ_DUMP_SUBNETS: {
1199 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1201 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1204 printf("%s owner %s\n", strip_weight(subnet), node);
1207 case REQ_DUMP_CONNECTIONS: {
1208 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1210 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1213 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1217 fprintf(stderr, "Unable to parse dump from tincd.\n");
1222 fprintf(stderr, "Error receiving dump.\n");
1226 static int cmd_purge(int argc, char *argv[]) {
1228 fprintf(stderr, "Too many arguments!\n");
1232 if(!connect_tincd(true))
1235 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1236 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1237 fprintf(stderr, "Could not purge old information.\n");
1244 static int cmd_debug(int argc, char *argv[]) {
1246 fprintf(stderr, "Invalid number of arguments.\n");
1250 if(!connect_tincd(true))
1253 int debuglevel = atoi(argv[1]);
1256 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1257 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1258 fprintf(stderr, "Could not set debug level.\n");
1262 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1266 static int cmd_retry(int argc, char *argv[]) {
1268 fprintf(stderr, "Too many arguments!\n");
1272 if(!connect_tincd(true))
1275 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1276 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1277 fprintf(stderr, "Could not retry outgoing connections.\n");
1284 static int cmd_connect(int argc, char *argv[]) {
1286 fprintf(stderr, "Invalid number of arguments.\n");
1290 if(!check_id(argv[1])) {
1291 fprintf(stderr, "Invalid name for node.\n");
1295 if(!connect_tincd(true))
1298 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1299 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1300 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1307 static int cmd_disconnect(int argc, char *argv[]) {
1309 fprintf(stderr, "Invalid number of arguments.\n");
1313 if(!check_id(argv[1])) {
1314 fprintf(stderr, "Invalid name for node.\n");
1318 if(!connect_tincd(true))
1321 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1322 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1323 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1330 static int cmd_top(int argc, char *argv[]) {
1332 fprintf(stderr, "Too many arguments!\n");
1337 if(!connect_tincd(true))
1343 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1348 static int cmd_pcap(int argc, char *argv[]) {
1350 fprintf(stderr, "Too many arguments!\n");
1354 if(!connect_tincd(true))
1357 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1362 static void sigint_handler(int sig) {
1363 fprintf(stderr, "\n");
1364 shutdown(fd, SHUT_RDWR);
1368 static int cmd_log(int argc, char *argv[]) {
1370 fprintf(stderr, "Too many arguments!\n");
1374 if(!connect_tincd(true))
1378 signal(SIGINT, sigint_handler);
1381 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1384 signal(SIGINT, SIG_DFL);
1392 static int cmd_pid(int argc, char *argv[]) {
1394 fprintf(stderr, "Too many arguments!\n");
1398 if(!connect_tincd(true) || !pid)
1401 printf("%d\n", pid);
1405 int rstrip(char *value) {
1406 int len = strlen(value);
1407 while(len && strchr("\t\r\n ", value[len - 1]))
1412 char *get_my_name(bool verbose) {
1413 FILE *f = fopen(tinc_conf, "r");
1416 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1422 while(fgets(buf, sizeof buf, f)) {
1423 int len = strcspn(buf, "\t =");
1425 value += strspn(value, "\t ");
1428 value += strspn(value, "\t ");
1433 if(strcasecmp(buf, "Name"))
1437 return replace_name(value);
1443 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1447 ecdsa_t *get_pubkey(FILE *f) {
1450 while(fgets(buf, sizeof buf, f)) {
1451 int len = strcspn(buf, "\t =");
1453 value += strspn(value, "\t ");
1456 value += strspn(value, "\t ");
1461 if(strcasecmp(buf, "Ed25519PublicKey"))
1464 return ecdsa_set_base64_public_key(value);
1470 const var_t variables[] = {
1471 /* Server configuration */
1472 {"AddressFamily", VAR_SERVER},
1473 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1474 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1475 {"BindToInterface", VAR_SERVER},
1476 {"Broadcast", VAR_SERVER | VAR_SAFE},
1477 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1478 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1479 {"DecrementTTL", VAR_SERVER},
1480 {"Device", VAR_SERVER},
1481 {"DeviceStandby", VAR_SERVER},
1482 {"DeviceType", VAR_SERVER},
1483 {"DirectOnly", VAR_SERVER},
1484 {"Ed25519PrivateKeyFile", VAR_SERVER},
1485 {"ExperimentalProtocol", VAR_SERVER},
1486 {"Forwarding", VAR_SERVER},
1487 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1488 {"Hostnames", VAR_SERVER},
1489 {"IffOneQueue", VAR_SERVER},
1490 {"Interface", VAR_SERVER},
1491 {"KeyExpire", VAR_SERVER},
1492 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1493 {"LocalDiscovery", VAR_SERVER},
1494 {"MACExpire", VAR_SERVER},
1495 {"MaxConnectionBurst", VAR_SERVER},
1496 {"MaxOutputBufferSize", VAR_SERVER},
1497 {"MaxTimeout", VAR_SERVER},
1498 {"Mode", VAR_SERVER | VAR_SAFE},
1499 {"Name", VAR_SERVER},
1500 {"PingInterval", VAR_SERVER},
1501 {"PingTimeout", VAR_SERVER},
1502 {"PriorityInheritance", VAR_SERVER},
1503 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1504 {"PrivateKeyFile", VAR_SERVER},
1505 {"ProcessPriority", VAR_SERVER},
1506 {"Proxy", VAR_SERVER},
1507 {"ReplayWindow", VAR_SERVER},
1508 {"ScriptsExtension", VAR_SERVER},
1509 {"ScriptsInterpreter", VAR_SERVER},
1510 {"StrictSubnets", VAR_SERVER},
1511 {"TunnelServer", VAR_SERVER},
1512 {"UDPDiscovery", VAR_SERVER},
1513 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1514 {"UDPDiscoveryInterval", VAR_SERVER},
1515 {"UDPDiscoveryTimeout", VAR_SERVER},
1516 {"MTUInfoInterval", VAR_SERVER},
1517 {"UDPInfoInterval", VAR_SERVER},
1518 {"UDPRcvBuf", VAR_SERVER},
1519 {"UDPSndBuf", VAR_SERVER},
1520 {"UPnP", VAR_SERVER},
1521 {"UPnPDiscoverWait", VAR_SERVER},
1522 {"UPnPRefreshPeriod", VAR_SERVER},
1523 {"VDEGroup", VAR_SERVER},
1524 {"VDEPort", VAR_SERVER},
1525 /* Host configuration */
1526 {"Address", VAR_HOST | VAR_MULTIPLE},
1527 {"Cipher", VAR_SERVER | VAR_HOST},
1528 {"ClampMSS", VAR_SERVER | VAR_HOST},
1529 {"Compression", VAR_SERVER | VAR_HOST},
1530 {"Digest", VAR_SERVER | VAR_HOST},
1531 {"Ed25519PublicKey", VAR_HOST},
1532 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1533 {"IndirectData", VAR_SERVER | VAR_HOST},
1534 {"MACLength", VAR_SERVER | VAR_HOST},
1535 {"PMTU", VAR_SERVER | VAR_HOST},
1536 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1538 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1539 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1540 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1541 {"TCPOnly", VAR_SERVER | VAR_HOST},
1542 {"Weight", VAR_HOST | VAR_SAFE},
1546 static int cmd_config(int argc, char *argv[]) {
1548 fprintf(stderr, "Invalid number of arguments.\n");
1552 if(strcasecmp(argv[0], "config"))
1556 if(!strcasecmp(argv[1], "get")) {
1558 } else if(!strcasecmp(argv[1], "add")) {
1559 argv++, argc--, action = 1;
1560 } else if(!strcasecmp(argv[1], "del")) {
1561 argv++, argc--, action = -1;
1562 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1563 argv++, argc--, action = 0;
1567 fprintf(stderr, "Invalid number of arguments.\n");
1571 // Concatenate the rest of the command line
1572 strncpy(line, argv[1], sizeof line - 1);
1573 for(int i = 2; i < argc; i++) {
1574 strncat(line, " ", sizeof line - 1 - strlen(line));
1575 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1578 // Liberal parsing into node name, variable name and value.
1584 len = strcspn(line, "\t =");
1586 value += strspn(value, "\t ");
1589 value += strspn(value, "\t ");
1592 variable = strchr(line, '.');
1601 fprintf(stderr, "No variable given.\n");
1605 if(action >= 0 && !*value) {
1606 fprintf(stderr, "No value for variable given.\n");
1610 if(action < -1 && *value)
1613 /* Some simple checks. */
1615 bool warnonremove = false;
1617 for(int i = 0; variables[i].name; i++) {
1618 if(strcasecmp(variables[i].name, variable))
1622 variable = (char *)variables[i].name;
1624 /* Discourage use of obsolete variables. */
1626 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1628 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1630 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1635 /* Don't put server variables in host config files */
1637 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1639 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1641 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1646 /* Should this go into our own host config file? */
1648 if(!node && !(variables[i].type & VAR_SERVER)) {
1649 node = get_my_name(true);
1654 /* Change "add" into "set" for variables that do not allow multiple occurences.
1655 Turn on warnings when it seems variables might be removed unintentionally. */
1657 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1658 warnonremove = true;
1660 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1661 warnonremove = true;
1667 if(node && !check_id(node)) {
1668 fprintf(stderr, "Invalid name for node.\n");
1673 if(force || action < 0) {
1674 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1676 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1681 // Open the right configuration file.
1682 char filename[PATH_MAX];
1684 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1686 snprintf(filename, sizeof filename, "%s", tinc_conf);
1688 FILE *f = fopen(filename, "r");
1690 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1694 char tmpfile[PATH_MAX];
1698 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1699 tf = fopen(tmpfile, "w");
1701 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1707 // Copy the file, making modifications on the fly, unless we are just getting a value.
1711 bool removed = false;
1714 while(fgets(buf1, sizeof buf1, f)) {
1715 buf1[sizeof buf1 - 1] = 0;
1716 strncpy(buf2, buf1, sizeof buf2);
1718 // Parse line in a simple way
1722 len = strcspn(buf2, "\t =");
1723 bvalue = buf2 + len;
1724 bvalue += strspn(bvalue, "\t ");
1725 if(*bvalue == '=') {
1727 bvalue += strspn(bvalue, "\t ");
1733 if(!strcasecmp(buf2, variable)) {
1737 printf("%s\n", bvalue);
1739 } else if(action == -1) {
1740 if(!*value || !strcasecmp(bvalue, value)) {
1745 } else if(action == 0) {
1746 // Warn if "set" was used for variables that can occur multiple times
1747 if(warnonremove && strcasecmp(bvalue, value))
1748 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1750 // Already set? Delete the rest...
1754 // Otherwise, replace.
1755 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1756 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1762 } else if(action > 0) {
1763 // Check if we've already seen this variable with the same value
1764 if(!strcasecmp(bvalue, value))
1770 // Copy original line...
1771 if(fputs(buf1, tf) < 0) {
1772 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1776 // Add newline if it is missing...
1777 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1778 if(fputc('\n', tf) < 0) {
1779 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1786 // Make sure we read everything...
1787 if(ferror(f) || !feof(f)) {
1788 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1793 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1797 // Add new variable if necessary.
1798 if((action > 0 && !found)|| (action == 0 && !set)) {
1799 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1800 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1809 fprintf(stderr, "No matching configuration variables found.\n");
1814 // Make sure we wrote everything...
1816 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1820 // Could we find what we had to remove?
1821 if(action < 0 && !removed) {
1823 fprintf(stderr, "No configuration variables deleted.\n");
1827 // Replace the configuration file with the new one
1829 if(remove(filename)) {
1830 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1834 if(rename(tmpfile, filename)) {
1835 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1839 // Silently try notifying a running tincd of changes.
1840 if(connect_tincd(false))
1841 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1846 static bool try_bind(int port) {
1847 struct addrinfo *ai = NULL, *aip;
1848 struct addrinfo hint = {
1849 .ai_flags = AI_PASSIVE,
1850 .ai_family = AF_UNSPEC,
1851 .ai_socktype = SOCK_STREAM,
1852 .ai_protocol = IPPROTO_TCP,
1855 bool success = true;
1857 snprintf(portstr, sizeof portstr, "%d", port);
1859 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1862 for(aip = ai; aip; aip = aip->ai_next) {
1863 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1869 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1881 int check_port(char *name) {
1885 fprintf(stderr, "Warning: could not bind to port 655. ");
1887 for(int i = 0; i < 100; i++) {
1888 int port = 0x1000 + (rand() & 0x7fff);
1889 if(try_bind(port)) {
1890 char filename[PATH_MAX];
1891 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1892 FILE *f = fopen(filename, "a");
1894 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1895 fprintf(stderr, "Please change tinc's Port manually.\n");
1899 fprintf(f, "Port = %d\n", port);
1901 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1906 fprintf(stderr, "Please change tinc's Port manually.\n");
1910 static int cmd_init(int argc, char *argv[]) {
1911 if(!access(tinc_conf, F_OK)) {
1912 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1917 fprintf(stderr, "Too many arguments!\n");
1919 } else if(argc < 2) {
1922 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1923 if(!fgets(buf, sizeof buf, stdin)) {
1924 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1927 int len = rstrip(buf);
1929 fprintf(stderr, "No name given!\n");
1934 fprintf(stderr, "No Name given!\n");
1938 name = strdup(argv[1]);
1940 fprintf(stderr, "No Name given!\n");
1945 if(!check_id(name)) {
1946 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1950 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1951 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1955 if(mkdir(confbase, 0777) && errno != EEXIST) {
1956 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1960 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1961 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1965 FILE *f = fopen(tinc_conf, "w");
1967 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1971 fprintf(f, "Name = %s\n", name);
1974 #ifndef DISABLE_LEGACY
1975 if(!rsa_keygen(2048, false))
1979 if(!ed25519_keygen(false))
1985 char filename[PATH_MAX];
1986 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1987 if(access(filename, F_OK)) {
1988 FILE *f = fopenmask(filename, "w", 0777);
1990 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1993 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");
2002 static int cmd_generate_keys(int argc, char *argv[]) {
2003 #ifdef DISABLE_LEGACY
2008 fprintf(stderr, "Too many arguments!\n");
2013 name = get_my_name(false);
2015 #ifndef DISABLE_LEGACY
2016 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
2020 if(!ed25519_keygen(true))
2026 #ifndef DISABLE_LEGACY
2027 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2029 fprintf(stderr, "Too many arguments!\n");
2034 name = get_my_name(false);
2036 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2040 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2042 fprintf(stderr, "Too many arguments!\n");
2047 name = get_my_name(false);
2049 return !ed25519_keygen(true);
2052 static int cmd_help(int argc, char *argv[]) {
2057 static int cmd_version(int argc, char *argv[]) {
2059 fprintf(stderr, "Too many arguments!\n");
2067 static int cmd_info(int argc, char *argv[]) {
2069 fprintf(stderr, "Invalid number of arguments.\n");
2073 if(!connect_tincd(true))
2076 return info(fd, argv[1]);
2079 static const char *conffiles[] = {
2090 static int cmd_edit(int argc, char *argv[]) {
2092 fprintf(stderr, "Invalid number of arguments.\n");
2096 char filename[PATH_MAX] = "";
2098 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2099 for(int i = 0; conffiles[i]; i++) {
2100 if(!strcmp(argv[1], conffiles[i])) {
2101 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2110 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2111 char *dash = strchr(argv[1], '-');
2114 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2115 fprintf(stderr, "Invalid configuration filename.\n");
2123 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2125 xasprintf(&command, "edit \"%s\"", filename);
2127 int result = system(command);
2132 // Silently try notifying a running tincd of changes.
2133 if(connect_tincd(false))
2134 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2139 static int export(const char *name, FILE *out) {
2140 char filename[PATH_MAX];
2141 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2142 FILE *in = fopen(filename, "r");
2144 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2148 fprintf(out, "Name = %s\n", name);
2150 while(fgets(buf, sizeof buf, in)) {
2151 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2156 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2165 static int cmd_export(int argc, char *argv[]) {
2167 fprintf(stderr, "Too many arguments!\n");
2171 char *name = get_my_name(true);
2175 int result = export(name, stdout);
2183 static int cmd_export_all(int argc, char *argv[]) {
2185 fprintf(stderr, "Too many arguments!\n");
2189 DIR *dir = opendir(hosts_dir);
2191 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2199 while((ent = readdir(dir))) {
2200 if(!check_id(ent->d_name))
2206 printf("#---------------------------------------------------------------#\n");
2208 result |= export(ent->d_name, stdout);
2217 static int cmd_import(int argc, char *argv[]) {
2219 fprintf(stderr, "Too many arguments!\n");
2228 char filename[PATH_MAX] = "";
2230 bool firstline = true;
2232 while(fgets(buf, sizeof buf, in)) {
2233 if(sscanf(buf, "Name = %s", name) == 1) {
2236 if(!check_id(name)) {
2237 fprintf(stderr, "Invalid Name in input!\n");
2244 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2246 if(!force && !access(filename, F_OK)) {
2247 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2252 out = fopen(filename, "w");
2254 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2260 } else if(firstline) {
2261 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2266 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2270 if(fputs(buf, out) < 0) {
2271 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2281 fprintf(stderr, "Imported %d host configuration files.\n", count);
2284 fprintf(stderr, "No host configuration files imported.\n");
2289 static int cmd_exchange(int argc, char *argv[]) {
2290 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2293 static int cmd_exchange_all(int argc, char *argv[]) {
2294 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2297 static int switch_network(char *name) {
2298 if(strcmp(name, ".")) {
2299 if(!check_netname(name, false)) {
2300 fprintf(stderr, "Invalid character in netname!\n");
2304 if(!check_netname(name, true))
2305 fprintf(stderr, "Warning: unsafe character in netname!\n");
2314 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2321 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2322 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2323 xasprintf(&prompt, "%s> ", identname);
2328 static int cmd_network(int argc, char *argv[]) {
2330 fprintf(stderr, "Too many arguments!\n");
2335 return switch_network(argv[1]);
2337 DIR *dir = opendir(confdir);
2339 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2344 while((ent = readdir(dir))) {
2345 if(*ent->d_name == '.')
2348 if(!strcmp(ent->d_name, "tinc.conf")) {
2353 char fname[PATH_MAX];
2354 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2355 if(!access(fname, R_OK))
2356 printf("%s\n", ent->d_name);
2364 static int cmd_fsck(int argc, char *argv[]) {
2366 fprintf(stderr, "Too many arguments!\n");
2370 return fsck(orig_argv[0]);
2373 static void *readfile(FILE *in, size_t *len) {
2375 size_t alloced = 4096;
2376 char *buf = xmalloc(alloced);
2379 size_t read = fread(buf + count, 1, alloced - count, in);
2383 if(count >= alloced) {
2385 buf = xrealloc(buf, alloced);
2395 static int cmd_sign(int argc, char *argv[]) {
2397 fprintf(stderr, "Too many arguments!\n");
2402 name = get_my_name(true);
2407 char fname[PATH_MAX];
2408 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
2409 FILE *fp = fopen(fname, "r");
2411 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2415 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2418 fprintf(stderr, "Could not read private key from %s\n", fname);
2428 in = fopen(argv[1], "rb");
2430 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2439 char *data = readfile(in, &len);
2443 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2448 // Ensure we sign our name and current time as well
2449 long t = time(NULL);
2451 xasprintf(&trailer, " %s %ld", name, t);
2452 int trailer_len = strlen(trailer);
2454 data = xrealloc(data, len + trailer_len);
2455 memcpy(data + len, trailer, trailer_len);
2459 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2460 fprintf(stderr, "Error generating signature\n");
2465 b64encode(sig, sig, 64);
2468 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2469 fwrite(data, len, 1, stdout);
2475 static int cmd_verify(int argc, char *argv[]) {
2477 fprintf(stderr, "Not enough arguments!\n");
2482 fprintf(stderr, "Too many arguments!\n");
2486 char *node = argv[1];
2487 if(!strcmp(node, ".")) {
2489 name = get_my_name(true);
2494 } else if(!strcmp(node, "*")) {
2497 if(!check_id(node)) {
2498 fprintf(stderr, "Invalid node name\n");
2506 in = fopen(argv[2], "rb");
2508 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2516 char *data = readfile(in, &len);
2520 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2524 char *newline = memchr(data, '\n', len);
2525 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2526 fprintf(stderr, "Invalid input\n");
2532 size_t skip = newline - data;
2534 char signer[MAX_STRING_SIZE] = "";
2535 char sig[MAX_STRING_SIZE] = "";
2538 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2539 fprintf(stderr, "Invalid input\n");
2544 if(node && strcmp(node, signer)) {
2545 fprintf(stderr, "Signature is not made by %s\n", node);
2554 xasprintf(&trailer, " %s %ld", signer, t);
2555 int trailer_len = strlen(trailer);
2557 data = xrealloc(data, len + trailer_len);
2558 memcpy(data + len, trailer, trailer_len);
2561 newline = data + skip;
2563 char fname[PATH_MAX];
2564 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, node);
2565 FILE *fp = fopen(fname, "r");
2567 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2572 ecdsa_t *key = get_pubkey(fp);
2575 key = ecdsa_read_pem_public_key(fp);
2578 fprintf(stderr, "Could not read public key from %s\n", fname);
2586 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2587 fprintf(stderr, "Invalid signature\n");
2595 fwrite(newline, len - (newline - data), 1, stdout);
2601 static const struct {
2602 const char *command;
2603 int (*function)(int argc, char *argv[]);
2606 {"start", cmd_start},
2608 {"restart", cmd_restart},
2609 {"reload", cmd_reload},
2612 {"purge", cmd_purge},
2613 {"debug", cmd_debug},
2614 {"retry", cmd_retry},
2615 {"connect", cmd_connect},
2616 {"disconnect", cmd_disconnect},
2621 {"config", cmd_config, true},
2622 {"add", cmd_config},
2623 {"del", cmd_config},
2624 {"get", cmd_config},
2625 {"set", cmd_config},
2627 {"generate-keys", cmd_generate_keys},
2628 #ifndef DISABLE_LEGACY
2629 {"generate-rsa-keys", cmd_generate_rsa_keys},
2631 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2633 {"version", cmd_version},
2636 {"export", cmd_export},
2637 {"export-all", cmd_export_all},
2638 {"import", cmd_import},
2639 {"exchange", cmd_exchange},
2640 {"exchange-all", cmd_exchange_all},
2641 {"invite", cmd_invite},
2643 {"network", cmd_network},
2646 {"verify", cmd_verify},
2650 #ifdef HAVE_READLINE
2651 static char *complete_command(const char *text, int state) {
2659 while(commands[i].command) {
2660 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2661 return xstrdup(commands[i].command);
2668 static char *complete_dump(const char *text, int state) {
2669 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2678 if(!strncasecmp(matches[i], text, strlen(text)))
2679 return xstrdup(matches[i]);
2686 static char *complete_config(const char *text, int state) {
2694 while(variables[i].name) {
2695 char *dot = strchr(text, '.');
2697 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2699 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2703 if(!strncasecmp(variables[i].name, text, strlen(text)))
2704 return xstrdup(variables[i].name);
2712 static char *complete_info(const char *text, int state) {
2716 if(!connect_tincd(false))
2718 // Check the list of nodes
2719 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2720 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2723 while(recvline(fd, line, sizeof line)) {
2725 int n = sscanf(line, "%d %d %s", &code, &req, item);
2735 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2739 if(!strncmp(item, text, strlen(text)))
2740 return xstrdup(strip_weight(item));
2746 static char *complete_nothing(const char *text, int state) {
2750 static char **completion (const char *text, int start, int end) {
2751 char **matches = NULL;
2754 matches = rl_completion_matches(text, complete_command);
2755 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2756 matches = rl_completion_matches(text, complete_dump);
2757 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2758 matches = rl_completion_matches(text, complete_config);
2759 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2760 matches = rl_completion_matches(text, complete_config);
2761 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2762 matches = rl_completion_matches(text, complete_config);
2763 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2764 matches = rl_completion_matches(text, complete_config);
2765 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2766 matches = rl_completion_matches(text, complete_info);
2772 static int cmd_shell(int argc, char *argv[]) {
2773 xasprintf(&prompt, "%s> ", identname);
2777 int maxargs = argc + 16;
2778 char **nargv = xmalloc(maxargs * sizeof *nargv);
2780 for(int i = 0; i < argc; i++)
2783 #ifdef HAVE_READLINE
2784 rl_readline_name = "tinc";
2785 rl_completion_entry_function = complete_nothing;
2786 rl_attempted_completion_function = completion;
2787 rl_filename_completion_desired = 0;
2792 #ifdef HAVE_READLINE
2796 rl_basic_word_break_characters = "\t\n ";
2797 line = readline(prompt);
2799 copy = xstrdup(line);
2801 line = fgets(buf, sizeof buf, stdin);
2805 fputs(prompt, stdout);
2807 line = fgets(buf, sizeof buf, stdin);
2813 /* Ignore comments */
2821 char *p = line + strspn(line, " \t\n");
2822 char *next = strtok(p, " \t\n");
2825 if(nargc >= maxargs) {
2826 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2829 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2834 next = strtok(NULL, " \t\n");
2840 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
2847 for(int i = 0; commands[i].command; i++) {
2848 if(!strcasecmp(nargv[argc], commands[i].command)) {
2849 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2855 #ifdef HAVE_READLINE
2861 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2874 int main(int argc, char *argv[]) {
2875 program_name = argv[0];
2878 tty = isatty(0) && isatty(1);
2880 if(!parse_options(argc, argv))
2884 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2885 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2898 static struct WSAData wsa_state;
2900 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2901 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2910 return cmd_shell(argc, argv);
2912 for(int i = 0; commands[i].command; i++) {
2913 if(!strcasecmp(argv[optind], commands[i].command))
2914 return commands[i].function(argc - optind, argv + optind);
2917 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);