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 = "";
81 static struct option const long_options[] = {
82 {"batch", no_argument, NULL, 'b'},
83 {"config", required_argument, NULL, 'c'},
84 {"net", required_argument, NULL, 'n'},
85 {"help", no_argument, NULL, 1},
86 {"version", no_argument, NULL, 2},
87 {"pidfile", required_argument, NULL, 3},
88 {"force", no_argument, NULL, 4},
92 static void version(void) {
93 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
94 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
95 printf("Copyright (C) 1998-2017 Ivo Timmermans, Guus Sliepen and others.\n"
96 "See the AUTHORS file for a complete list.\n\n"
97 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
98 "and you are welcome to redistribute it under certain conditions;\n"
99 "see the file COPYING for details.\n");
102 static void usage(bool status) {
104 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
106 printf("Usage: %s [options] command\n\n", program_name);
107 printf("Valid options are:\n"
108 " -b, --batch Don't ask for anything (non-interactive mode).\n"
109 " -c, --config=DIR Read configuration options from DIR.\n"
110 " -n, --net=NETNAME Connect to net NETNAME.\n"
111 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
112 " --force Force some commands to work despite warnings.\n"
113 " --help Display this help and exit.\n"
114 " --version Output version information and exit.\n"
116 "Valid commands are:\n"
117 " init [name] Create initial configuration files.\n"
118 " get VARIABLE Print current value of VARIABLE\n"
119 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
120 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
121 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
122 " start [tincd options] Start tincd.\n"
123 " stop Stop tincd.\n"
124 " restart [tincd options] Restart tincd.\n"
125 " reload Partially reload configuration of running tincd.\n"
126 " pid Show PID of currently running tincd.\n"
127 #ifdef DISABLE_LEGACY
128 " generate-keys Generate a new Ed25519 public/private keypair.\n"
130 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
131 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
133 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
134 " dump Dump a list of one of the following things:\n"
135 " [reachable] nodes - all known nodes in the VPN\n"
136 " edges - all known connections in the VPN\n"
137 " subnets - all known subnets in the VPN\n"
138 " connections - all meta connections with ourself\n"
139 " [di]graph - graph of the VPN in dotty format\n"
140 " invitations - outstanding invitations\n"
141 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
142 " purge Purge unreachable nodes\n"
143 " debug N Set debug level\n"
144 " retry Retry all outgoing connections\n"
145 " disconnect NODE Close meta connection with NODE\n"
147 " top Show real-time statistics\n"
149 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
150 " log [level] Dump log output [up to the specified level]\n"
151 " export Export host configuration of local node to standard output\n"
152 " export-all Export all host configuration files to standard output\n"
153 " import Import host configuration file(s) from standard input\n"
154 " exchange Same as export followed by import\n"
155 " exchange-all Same as export-all followed by import\n"
156 " invite NODE [...] Generate an invitation for NODE\n"
157 " join INVITATION Join a VPN using an INVITATION\n"
158 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
159 " fsck Check the configuration files for problems.\n"
160 " sign [FILE] Generate a signed version of a file.\n"
161 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
163 printf("Report bugs to tinc@tinc-vpn.org.\n");
167 static bool parse_options(int argc, char **argv) {
169 int option_index = 0;
171 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
173 case 0: /* long option */
180 case 'c': /* config file */
181 confbase = xstrdup(optarg);
182 confbasegiven = true;
185 case 'n': /* net name given */
186 netname = xstrdup(optarg);
189 case 1: /* show help */
193 case 2: /* show version */
197 case 3: /* open control socket here */
198 pidfilename = xstrdup(optarg);
205 case '?': /* wrong options */
214 if(!netname && (netname = getenv("NETNAME")))
215 netname = xstrdup(netname);
217 /* netname "." is special: a "top-level name" */
219 if(netname && (!*netname || !strcmp(netname, "."))) {
224 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
225 fprintf(stderr, "Invalid character in netname!\n");
232 /* Open a file with the desired permissions, minus the umask.
233 Also, if we want to create an executable file, we call fchmod()
234 to set the executable bits. */
236 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
237 mode_t mask = umask(0);
240 FILE *f = fopen(filename, mode);
243 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
248 if((perms & 0444) && f)
249 fchmod(fileno(f), perms);
255 static void disable_old_keys(const char *filename, const char *what) {
256 char tmpfile[PATH_MAX] = "";
258 bool disabled = false;
263 r = fopen(filename, "r");
267 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
269 struct stat st = {.st_mode = 0600};
270 fstat(fileno(r), &st);
271 w = fopenmask(tmpfile, "w", st.st_mode);
273 while(fgets(buf, sizeof buf, r)) {
274 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
275 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
281 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
287 if(block || ed25519pubkey)
289 if(fputs(buf, w) < 0) {
295 if(block && !strncmp(buf, "-----END ", 9))
302 if(ferror(r) || fclose(r) < 0)
307 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
314 // We cannot atomically replace files on Windows.
315 char bakfile[PATH_MAX] = "";
316 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
317 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
318 rename(bakfile, filename);
320 if(rename(tmpfile, filename)) {
322 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
327 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
334 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
336 char directory[PATH_MAX] = ".";
340 /* Check stdin and stdout */
342 /* Ask for a file and/or directory name. */
343 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
345 if(fgets(buf, sizeof buf, stdin) == NULL) {
346 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
350 size_t len = strlen(buf);
359 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
361 if(filename[0] != '/') {
363 /* The directory is a relative path or a filename. */
364 getcwd(directory, sizeof directory);
365 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
369 disable_old_keys(filename, what);
371 /* Open it first to keep the inode busy */
373 r = fopenmask(filename, mode, perms);
376 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
384 Generate a public/private Ed25519 keypair, and ask for a file to store
387 static bool ed25519_keygen(bool ask) {
390 char fname[PATH_MAX];
392 fprintf(stderr, "Generating Ed25519 keypair:\n");
394 if(!(key = ecdsa_generate())) {
395 fprintf(stderr, "Error during key generation!\n");
398 fprintf(stderr, "Done.\n");
400 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
401 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
406 if(!ecdsa_write_pem_private_key(key, f)) {
407 fprintf(stderr, "Error writing private key!\n");
414 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
416 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.pub", confbase);
418 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
423 char *pubkey = ecdsa_get_base64_public_key(key);
424 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
439 #ifndef DISABLE_LEGACY
441 Generate a public/private RSA keypair, and ask for a file to store
444 static bool rsa_keygen(int bits, bool ask) {
447 char fname[PATH_MAX];
449 // Make sure the key size is a multiple of 8 bits.
452 // Make sure that a valid key size is used.
453 if(bits < 1024 || bits > 8192) {
454 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
456 } else if(bits < 2048) {
457 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
460 fprintf(stderr, "Generating %d bits keys:\n", bits);
462 if(!(key = rsa_generate(bits, 0x10001))) {
463 fprintf(stderr, "Error during key generation!\n");
466 fprintf(stderr, "Done.\n");
468 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.priv", confbase);
469 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
474 if(!rsa_write_pem_private_key(key, f)) {
475 fprintf(stderr, "Error writing private key!\n");
482 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
484 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.pub", confbase);
486 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
491 if(!rsa_write_pem_public_key(key, f)) {
492 fprintf(stderr, "Error writing public key!\n");
512 bool recvline(int fd, char *line, size_t len) {
513 char *newline = NULL;
518 while(!(newline = memchr(buffer, '\n', blen))) {
519 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
520 if(result == -1 && sockerrno == EINTR)
527 if(newline - buffer >= len)
530 len = newline - buffer;
532 memcpy(line, buffer, len);
534 memmove(buffer, newline + 1, blen - len - 1);
540 bool recvdata(int fd, char *data, size_t len) {
545 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
546 if(result == -1 && sockerrno == EINTR)
553 memcpy(data, buffer, len);
554 memmove(buffer, buffer + len, blen - len);
560 bool sendline(int fd, char *format, ...) {
561 static char buffer[4096];
566 va_start(ap, format);
567 blen = vsnprintf(buffer, sizeof buffer, format, ap);
568 buffer[sizeof buffer - 1] = 0;
571 if(blen < 1 || blen >= sizeof buffer)
578 int result = send(fd, p, blen, MSG_NOSIGNAL);
579 if(result == -1 && sockerrno == EINTR)
590 static void pcap(int fd, FILE *out, int snaplen) {
591 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
599 uint32_t tz_accuracy;
606 snaplen ?: sizeof data,
619 fwrite(&header, sizeof header, 1, out);
623 while(recvline(fd, line, sizeof line)) {
625 int n = sscanf(line, "%d %d %d", &code, &req, &len);
626 gettimeofday(&tv, NULL);
627 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
629 if(!recvdata(fd, data, len))
631 packet.tv_sec = tv.tv_sec;
632 packet.tv_usec = tv.tv_usec;
634 packet.origlen = len;
635 fwrite(&packet, sizeof packet, 1, out);
636 fwrite(data, len, 1, out);
641 static void logcontrol(int fd, FILE *out, int level) {
642 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
646 while(recvline(fd, line, sizeof line)) {
648 int n = sscanf(line, "%d %d %d", &code, &req, &len);
649 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
651 if(!recvdata(fd, data, len))
653 fwrite(data, len, 1, out);
660 static bool remove_service(void) {
661 SC_HANDLE manager = NULL;
662 SC_HANDLE service = NULL;
663 SERVICE_STATUS status = {0};
665 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
667 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
671 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
674 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
678 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
679 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
681 fprintf(stderr, "%s service stopped\n", identname);
683 if(!DeleteService(service)) {
684 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
688 fprintf(stderr, "%s service removed\n", identname);
694 bool connect_tincd(bool verbose) {
699 struct timeval tv = {0, 0};
700 if(select(fd + 1, &r, NULL, NULL, &tv)) {
701 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
709 FILE *f = fopen(pidfilename, "r");
712 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
719 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
721 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
729 if ((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
730 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
731 /* clean up the stale socket and pid file */
733 unlink(unixsocketname);
737 struct sockaddr_un sa;
738 sa.sun_family = AF_UNIX;
739 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
741 fd = socket(AF_UNIX, SOCK_STREAM, 0);
744 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
748 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
750 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
756 struct addrinfo hints = {
757 .ai_family = AF_UNSPEC,
758 .ai_socktype = SOCK_STREAM,
759 .ai_protocol = IPPROTO_TCP,
763 struct addrinfo *res = NULL;
765 if(getaddrinfo(host, port, &hints, &res) || !res) {
767 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
771 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
774 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
779 unsigned long arg = 0;
781 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
783 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
787 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
789 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
799 static const int one = 1;
800 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
806 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
808 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
814 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
816 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
818 fprintf(stderr, "Could not fully establish control socket connection\n");
828 static int cmd_start(int argc, char *argv[]) {
829 if(connect_tincd(false)) {
831 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
833 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
838 char *slash = strrchr(program_name, '/');
841 if ((c = strrchr(program_name, '\\')) > slash)
846 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
851 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
856 Windows has no real concept of an "argv array". A command line is just one string.
857 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
858 it uses quotes to handle spaces in arguments.
859 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
860 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
861 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
863 xasprintf(&arg0, "\"%s\"", arg0);
865 nargv[nargc++] = arg0;
866 for(int i = 1; i < optind; i++)
867 nargv[nargc++] = orig_argv[i];
868 for(int i = 1; i < argc; i++)
869 nargv[nargc++] = argv[i];
872 int status = spawnvp(_P_WAIT, c, nargv);
874 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
879 int pfd[2] = {-1, -1};
880 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
881 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
888 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
896 snprintf(buf, sizeof buf, "%d", pfd[1]);
897 setenv("TINC_UMBILICAL", buf, true);
898 exit(execvp(c, nargv));
905 int status = -1, result;
907 signal(SIGINT, SIG_IGN);
910 // Pass all log messages from the umbilical to stderr.
911 // A nul-byte right before closure means tincd started succesfully.
916 while((len = read(pfd[0], buf, sizeof buf)) > 0) {
917 failure = buf[len - 1];
928 // Make sure the child process is really gone.
929 result = waitpid(pid, &status, 0);
932 signal(SIGINT, SIG_DFL);
935 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
936 fprintf(stderr, "Error starting %s\n", c);
944 static int cmd_stop(int argc, char *argv[]) {
946 fprintf(stderr, "Too many arguments!\n");
951 if(!connect_tincd(true)) {
953 if(kill(pid, SIGTERM)) {
954 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
958 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
959 waitpid(pid, NULL, 0);
966 sendline(fd, "%d %d", CONTROL, REQ_STOP);
968 while(recvline(fd, line, sizeof line)) {
969 // Wait for tincd to close the connection...
972 if(!remove_service())
982 static int cmd_restart(int argc, char *argv[]) {
984 return cmd_start(argc, argv);
987 static int cmd_reload(int argc, char *argv[]) {
989 fprintf(stderr, "Too many arguments!\n");
993 if(!connect_tincd(true))
996 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
997 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
998 fprintf(stderr, "Could not reload configuration.\n");
1006 static int dump_invitations(void) {
1007 char dname[PATH_MAX];
1008 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
1009 DIR *dir = opendir(dname);
1011 if(errno == ENOENT) {
1012 fprintf(stderr, "No outstanding invitations.\n");
1016 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1023 while((ent = readdir(dir))) {
1024 char buf[MAX_STRING_SIZE];
1025 if(b64decode(ent->d_name, buf, 24) != 18)
1028 char fname[PATH_MAX];
1029 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
1030 FILE *f = fopen(fname, "r");
1032 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1037 if(!fgets(buf, sizeof buf, f)) {
1038 fprintf(stderr, "Invalid invitation file %s", fname);
1044 char *eol = buf + strlen(buf);
1045 while(strchr("\t \r\n", *--eol))
1047 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1048 fprintf(stderr, "Invalid invitation file %s", fname);
1053 printf("%s %s\n", ent->d_name, buf + 7);
1059 fprintf(stderr, "No outstanding invitations.\n");
1064 static int cmd_dump(int argc, char *argv[]) {
1065 bool only_reachable = false;
1067 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1068 if(strcasecmp(argv[2], "nodes")) {
1069 fprintf(stderr, "`reachable' only supported for nodes.\n");
1073 only_reachable = true;
1079 fprintf(stderr, "Invalid number of arguments.\n");
1084 if(!strcasecmp(argv[1], "invitations"))
1085 return dump_invitations();
1087 if(!connect_tincd(true))
1092 if(!strcasecmp(argv[1], "nodes"))
1093 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1094 else if(!strcasecmp(argv[1], "edges"))
1095 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1096 else if(!strcasecmp(argv[1], "subnets"))
1097 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1098 else if(!strcasecmp(argv[1], "connections"))
1099 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1100 else if(!strcasecmp(argv[1], "graph")) {
1101 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1102 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1104 } else if(!strcasecmp(argv[1], "digraph")) {
1105 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1106 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1109 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1115 printf("graph {\n");
1116 else if(do_graph == 2)
1117 printf("digraph {\n");
1119 while(recvline(fd, line, sizeof line)) {
1120 char node1[4096], node2[4096];
1121 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1123 if(do_graph && req == REQ_DUMP_NODES)
1141 char local_host[4096];
1142 char local_port[4096];
1145 int cipher, digest, maclength, compression, distance, socket, weight;
1146 short int pmtu, minmtu, maxmtu;
1147 unsigned int options, status_int;
1148 node_status_t status;
1149 long int last_state_change;
1152 case REQ_DUMP_NODES: {
1153 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %d %d %d %d %x %x %4095s %4095s %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);
1155 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1159 memcpy(&status, &status_int, sizeof status);
1162 const char *color = "black";
1163 if(!strcmp(host, "MYSELF"))
1165 else if(!status.reachable)
1167 else if(strcmp(via, node))
1169 else if(!status.validkey)
1173 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1175 if(only_reachable && !status.reachable)
1177 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",
1178 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1182 case REQ_DUMP_EDGES: {
1183 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %4095s port %4095s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
1185 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1190 float w = 1 + 65536.0 / weight;
1191 if(do_graph == 1 && strcmp(node1, node2) > 0)
1192 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1193 else if(do_graph == 2)
1194 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1196 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);
1200 case REQ_DUMP_SUBNETS: {
1201 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1203 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1206 printf("%s owner %s\n", strip_weight(subnet), node);
1209 case REQ_DUMP_CONNECTIONS: {
1210 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1212 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1215 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1219 fprintf(stderr, "Unable to parse dump from tincd.\n");
1224 fprintf(stderr, "Error receiving dump.\n");
1228 static int cmd_purge(int argc, char *argv[]) {
1230 fprintf(stderr, "Too many arguments!\n");
1234 if(!connect_tincd(true))
1237 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1238 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1239 fprintf(stderr, "Could not purge old information.\n");
1246 static int cmd_debug(int argc, char *argv[]) {
1248 fprintf(stderr, "Invalid number of arguments.\n");
1252 if(!connect_tincd(true))
1255 int debuglevel = atoi(argv[1]);
1258 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1259 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1260 fprintf(stderr, "Could not set debug level.\n");
1264 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1268 static int cmd_retry(int argc, char *argv[]) {
1270 fprintf(stderr, "Too many arguments!\n");
1274 if(!connect_tincd(true))
1277 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1278 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1279 fprintf(stderr, "Could not retry outgoing connections.\n");
1286 static int cmd_connect(int argc, char *argv[]) {
1288 fprintf(stderr, "Invalid number of arguments.\n");
1292 if(!check_id(argv[1])) {
1293 fprintf(stderr, "Invalid name for node.\n");
1297 if(!connect_tincd(true))
1300 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1301 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1302 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1309 static int cmd_disconnect(int argc, char *argv[]) {
1311 fprintf(stderr, "Invalid number of arguments.\n");
1315 if(!check_id(argv[1])) {
1316 fprintf(stderr, "Invalid name for node.\n");
1320 if(!connect_tincd(true))
1323 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1324 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1325 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1332 static int cmd_top(int argc, char *argv[]) {
1334 fprintf(stderr, "Too many arguments!\n");
1339 if(!connect_tincd(true))
1345 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1350 static int cmd_pcap(int argc, char *argv[]) {
1352 fprintf(stderr, "Too many arguments!\n");
1356 if(!connect_tincd(true))
1359 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1364 static void sigint_handler(int sig) {
1365 fprintf(stderr, "\n");
1366 shutdown(fd, SHUT_RDWR);
1370 static int cmd_log(int argc, char *argv[]) {
1372 fprintf(stderr, "Too many arguments!\n");
1376 if(!connect_tincd(true))
1380 signal(SIGINT, sigint_handler);
1383 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1386 signal(SIGINT, SIG_DFL);
1394 static int cmd_pid(int argc, char *argv[]) {
1396 fprintf(stderr, "Too many arguments!\n");
1400 if(!connect_tincd(true) || !pid)
1403 printf("%d\n", pid);
1407 int rstrip(char *value) {
1408 int len = strlen(value);
1409 while(len && strchr("\t\r\n ", value[len - 1]))
1414 char *get_my_name(bool verbose) {
1415 FILE *f = fopen(tinc_conf, "r");
1418 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1424 while(fgets(buf, sizeof buf, f)) {
1425 int len = strcspn(buf, "\t =");
1427 value += strspn(value, "\t ");
1430 value += strspn(value, "\t ");
1435 if(strcasecmp(buf, "Name"))
1439 return replace_name(value);
1445 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1449 ecdsa_t *get_pubkey(FILE *f) {
1452 while(fgets(buf, sizeof buf, f)) {
1453 int len = strcspn(buf, "\t =");
1455 value += strspn(value, "\t ");
1458 value += strspn(value, "\t ");
1463 if(strcasecmp(buf, "Ed25519PublicKey"))
1466 return ecdsa_set_base64_public_key(value);
1472 const var_t variables[] = {
1473 /* Server configuration */
1474 {"AddressFamily", VAR_SERVER},
1475 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1476 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1477 {"BindToInterface", VAR_SERVER},
1478 {"Broadcast", VAR_SERVER | VAR_SAFE},
1479 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1480 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1481 {"DecrementTTL", VAR_SERVER},
1482 {"Device", VAR_SERVER},
1483 {"DeviceStandby", VAR_SERVER},
1484 {"DeviceType", VAR_SERVER},
1485 {"DirectOnly", VAR_SERVER},
1486 {"Ed25519PrivateKeyFile", VAR_SERVER},
1487 {"ExperimentalProtocol", VAR_SERVER},
1488 {"Forwarding", VAR_SERVER},
1489 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1490 {"Hostnames", VAR_SERVER},
1491 {"IffOneQueue", VAR_SERVER},
1492 {"Interface", VAR_SERVER},
1493 {"InvitationExpire", VAR_SERVER},
1494 {"KeyExpire", VAR_SERVER},
1495 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1496 {"LocalDiscovery", VAR_SERVER},
1497 {"LogLevel", VAR_SERVER},
1498 {"MACExpire", VAR_SERVER},
1499 {"MaxConnectionBurst", VAR_SERVER},
1500 {"MaxOutputBufferSize", VAR_SERVER},
1501 {"MaxTimeout", VAR_SERVER},
1502 {"Mode", VAR_SERVER | VAR_SAFE},
1503 {"Name", VAR_SERVER},
1504 {"PingInterval", VAR_SERVER},
1505 {"PingTimeout", VAR_SERVER},
1506 {"PriorityInheritance", VAR_SERVER},
1507 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1508 {"PrivateKeyFile", VAR_SERVER},
1509 {"ProcessPriority", VAR_SERVER},
1510 {"Proxy", VAR_SERVER},
1511 {"ReplayWindow", VAR_SERVER},
1512 {"ScriptsExtension", VAR_SERVER},
1513 {"ScriptsInterpreter", VAR_SERVER},
1514 {"StrictSubnets", VAR_SERVER},
1515 {"TunnelServer", VAR_SERVER},
1516 {"UDPDiscovery", VAR_SERVER},
1517 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1518 {"UDPDiscoveryInterval", VAR_SERVER},
1519 {"UDPDiscoveryTimeout", VAR_SERVER},
1520 {"MTUInfoInterval", VAR_SERVER},
1521 {"UDPInfoInterval", VAR_SERVER},
1522 {"UDPRcvBuf", VAR_SERVER},
1523 {"UDPSndBuf", VAR_SERVER},
1524 {"UPnP", VAR_SERVER},
1525 {"UPnPDiscoverWait", VAR_SERVER},
1526 {"UPnPRefreshPeriod", VAR_SERVER},
1527 {"VDEGroup", VAR_SERVER},
1528 {"VDEPort", VAR_SERVER},
1529 /* Host configuration */
1530 {"Address", VAR_HOST | VAR_MULTIPLE},
1531 {"Cipher", VAR_SERVER | VAR_HOST},
1532 {"ClampMSS", VAR_SERVER | VAR_HOST},
1533 {"Compression", VAR_SERVER | VAR_HOST},
1534 {"Digest", VAR_SERVER | VAR_HOST},
1535 {"Ed25519PublicKey", VAR_HOST},
1536 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1537 {"IndirectData", VAR_SERVER | VAR_HOST},
1538 {"MACLength", VAR_SERVER | VAR_HOST},
1539 {"PMTU", VAR_SERVER | VAR_HOST},
1540 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1542 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1543 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1544 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1545 {"TCPOnly", VAR_SERVER | VAR_HOST},
1546 {"Weight", VAR_HOST | VAR_SAFE},
1550 static int cmd_config(int argc, char *argv[]) {
1552 fprintf(stderr, "Invalid number of arguments.\n");
1556 if(strcasecmp(argv[0], "config"))
1560 if(!strcasecmp(argv[1], "get")) {
1562 } else if(!strcasecmp(argv[1], "add")) {
1563 argv++, argc--, action = 1;
1564 } else if(!strcasecmp(argv[1], "del")) {
1565 argv++, argc--, action = -1;
1566 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1567 argv++, argc--, action = 0;
1571 fprintf(stderr, "Invalid number of arguments.\n");
1575 // Concatenate the rest of the command line
1576 strncpy(line, argv[1], sizeof line - 1);
1577 for(int i = 2; i < argc; i++) {
1578 strncat(line, " ", sizeof line - 1 - strlen(line));
1579 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1582 // Liberal parsing into node name, variable name and value.
1588 len = strcspn(line, "\t =");
1590 value += strspn(value, "\t ");
1593 value += strspn(value, "\t ");
1596 variable = strchr(line, '.');
1605 fprintf(stderr, "No variable given.\n");
1609 if(action >= 0 && !*value) {
1610 fprintf(stderr, "No value for variable given.\n");
1614 if(action < -1 && *value)
1617 /* Some simple checks. */
1619 bool warnonremove = false;
1621 for(int i = 0; variables[i].name; i++) {
1622 if(strcasecmp(variables[i].name, variable))
1626 variable = (char *)variables[i].name;
1628 /* Discourage use of obsolete variables. */
1630 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1632 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1634 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1639 /* Don't put server variables in host config files */
1641 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1643 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1645 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1650 /* Should this go into our own host config file? */
1652 if(!node && !(variables[i].type & VAR_SERVER)) {
1653 node = get_my_name(true);
1658 /* Change "add" into "set" for variables that do not allow multiple occurences.
1659 Turn on warnings when it seems variables might be removed unintentionally. */
1661 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1662 warnonremove = true;
1664 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1665 warnonremove = true;
1671 if(node && !check_id(node)) {
1672 fprintf(stderr, "Invalid name for node.\n");
1677 if(force || action < 0) {
1678 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1680 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1685 // Open the right configuration file.
1686 char filename[PATH_MAX];
1688 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1690 snprintf(filename, sizeof filename, "%s", tinc_conf);
1692 FILE *f = fopen(filename, "r");
1694 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1698 char tmpfile[PATH_MAX];
1702 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1703 tf = fopen(tmpfile, "w");
1705 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1711 // Copy the file, making modifications on the fly, unless we are just getting a value.
1715 bool removed = false;
1718 while(fgets(buf1, sizeof buf1, f)) {
1719 buf1[sizeof buf1 - 1] = 0;
1720 strncpy(buf2, buf1, sizeof buf2);
1722 // Parse line in a simple way
1726 len = strcspn(buf2, "\t =");
1727 bvalue = buf2 + len;
1728 bvalue += strspn(bvalue, "\t ");
1729 if(*bvalue == '=') {
1731 bvalue += strspn(bvalue, "\t ");
1737 if(!strcasecmp(buf2, variable)) {
1741 printf("%s\n", bvalue);
1743 } else if(action == -1) {
1744 if(!*value || !strcasecmp(bvalue, value)) {
1749 } else if(action == 0) {
1750 // Warn if "set" was used for variables that can occur multiple times
1751 if(warnonremove && strcasecmp(bvalue, value))
1752 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1754 // Already set? Delete the rest...
1758 // Otherwise, replace.
1759 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1760 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1766 } else if(action > 0) {
1767 // Check if we've already seen this variable with the same value
1768 if(!strcasecmp(bvalue, value))
1774 // Copy original line...
1775 if(fputs(buf1, tf) < 0) {
1776 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1780 // Add newline if it is missing...
1781 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1782 if(fputc('\n', tf) < 0) {
1783 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1790 // Make sure we read everything...
1791 if(ferror(f) || !feof(f)) {
1792 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1797 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1801 // Add new variable if necessary.
1802 if((action > 0 && !found)|| (action == 0 && !set)) {
1803 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1804 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1813 fprintf(stderr, "No matching configuration variables found.\n");
1818 // Make sure we wrote everything...
1820 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1824 // Could we find what we had to remove?
1825 if(action < 0 && !removed) {
1827 fprintf(stderr, "No configuration variables deleted.\n");
1831 // Replace the configuration file with the new one
1833 if(remove(filename)) {
1834 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1838 if(rename(tmpfile, filename)) {
1839 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1843 // Silently try notifying a running tincd of changes.
1844 if(connect_tincd(false))
1845 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1850 static bool try_bind(int port) {
1851 struct addrinfo *ai = NULL, *aip;
1852 struct addrinfo hint = {
1853 .ai_flags = AI_PASSIVE,
1854 .ai_family = AF_UNSPEC,
1855 .ai_socktype = SOCK_STREAM,
1856 .ai_protocol = IPPROTO_TCP,
1859 bool success = true;
1861 snprintf(portstr, sizeof portstr, "%d", port);
1863 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1866 for(aip = ai; aip; aip = aip->ai_next) {
1867 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1873 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1885 int check_port(char *name) {
1889 fprintf(stderr, "Warning: could not bind to port 655. ");
1891 for(int i = 0; i < 100; i++) {
1892 int port = 0x1000 + (rand() & 0x7fff);
1893 if(try_bind(port)) {
1894 char filename[PATH_MAX];
1895 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1896 FILE *f = fopen(filename, "a");
1898 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1899 fprintf(stderr, "Please change tinc's Port manually.\n");
1903 fprintf(f, "Port = %d\n", port);
1905 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1910 fprintf(stderr, "Please change tinc's Port manually.\n");
1914 static int cmd_init(int argc, char *argv[]) {
1915 if(!access(tinc_conf, F_OK)) {
1916 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1921 fprintf(stderr, "Too many arguments!\n");
1923 } else if(argc < 2) {
1926 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1927 if(!fgets(buf, sizeof buf, stdin)) {
1928 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1931 int len = rstrip(buf);
1933 fprintf(stderr, "No name given!\n");
1938 fprintf(stderr, "No Name given!\n");
1942 name = strdup(argv[1]);
1944 fprintf(stderr, "No Name given!\n");
1949 if(!check_id(name)) {
1950 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1954 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1955 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1959 if(mkdir(confbase, 0777) && errno != EEXIST) {
1960 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1964 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1965 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1969 FILE *f = fopen(tinc_conf, "w");
1971 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1975 fprintf(f, "Name = %s\n", name);
1978 #ifndef DISABLE_LEGACY
1979 if(!rsa_keygen(2048, false))
1983 if(!ed25519_keygen(false))
1989 char filename[PATH_MAX];
1990 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1991 if(access(filename, F_OK)) {
1992 FILE *f = fopenmask(filename, "w", 0777);
1994 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1997 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");
2006 static int cmd_generate_keys(int argc, char *argv[]) {
2007 #ifdef DISABLE_LEGACY
2012 fprintf(stderr, "Too many arguments!\n");
2017 name = get_my_name(false);
2019 #ifndef DISABLE_LEGACY
2020 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
2024 if(!ed25519_keygen(true))
2030 #ifndef DISABLE_LEGACY
2031 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2033 fprintf(stderr, "Too many arguments!\n");
2038 name = get_my_name(false);
2040 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2044 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2046 fprintf(stderr, "Too many arguments!\n");
2051 name = get_my_name(false);
2053 return !ed25519_keygen(true);
2056 static int cmd_help(int argc, char *argv[]) {
2061 static int cmd_version(int argc, char *argv[]) {
2063 fprintf(stderr, "Too many arguments!\n");
2071 static int cmd_info(int argc, char *argv[]) {
2073 fprintf(stderr, "Invalid number of arguments.\n");
2077 if(!connect_tincd(true))
2080 return info(fd, argv[1]);
2083 static const char *conffiles[] = {
2094 static int cmd_edit(int argc, char *argv[]) {
2096 fprintf(stderr, "Invalid number of arguments.\n");
2100 char filename[PATH_MAX] = "";
2102 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2103 for(int i = 0; conffiles[i]; i++) {
2104 if(!strcmp(argv[1], conffiles[i])) {
2105 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2114 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2115 char *dash = strchr(argv[1], '-');
2118 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2119 fprintf(stderr, "Invalid configuration filename.\n");
2127 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2129 xasprintf(&command, "edit \"%s\"", filename);
2131 int result = system(command);
2136 // Silently try notifying a running tincd of changes.
2137 if(connect_tincd(false))
2138 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2143 static int export(const char *name, FILE *out) {
2144 char filename[PATH_MAX];
2145 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2146 FILE *in = fopen(filename, "r");
2148 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2152 fprintf(out, "Name = %s\n", name);
2154 while(fgets(buf, sizeof buf, in)) {
2155 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2160 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2169 static int cmd_export(int argc, char *argv[]) {
2171 fprintf(stderr, "Too many arguments!\n");
2175 char *name = get_my_name(true);
2179 int result = export(name, stdout);
2187 static int cmd_export_all(int argc, char *argv[]) {
2189 fprintf(stderr, "Too many arguments!\n");
2193 DIR *dir = opendir(hosts_dir);
2195 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2203 while((ent = readdir(dir))) {
2204 if(!check_id(ent->d_name))
2210 printf("#---------------------------------------------------------------#\n");
2212 result |= export(ent->d_name, stdout);
2221 static int cmd_import(int argc, char *argv[]) {
2223 fprintf(stderr, "Too many arguments!\n");
2232 char filename[PATH_MAX] = "";
2234 bool firstline = true;
2236 while(fgets(buf, sizeof buf, in)) {
2237 if(sscanf(buf, "Name = %4095s", name) == 1) {
2240 if(!check_id(name)) {
2241 fprintf(stderr, "Invalid Name in input!\n");
2248 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2250 if(!force && !access(filename, F_OK)) {
2251 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2256 out = fopen(filename, "w");
2258 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2264 } else if(firstline) {
2265 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2270 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2274 if(fputs(buf, out) < 0) {
2275 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2285 fprintf(stderr, "Imported %d host configuration files.\n", count);
2288 fprintf(stderr, "No host configuration files imported.\n");
2293 static int cmd_exchange(int argc, char *argv[]) {
2294 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2297 static int cmd_exchange_all(int argc, char *argv[]) {
2298 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2301 static int switch_network(char *name) {
2302 if(strcmp(name, ".")) {
2303 if(!check_netname(name, false)) {
2304 fprintf(stderr, "Invalid character in netname!\n");
2308 if(!check_netname(name, true))
2309 fprintf(stderr, "Warning: unsafe character in netname!\n");
2318 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2325 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2326 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2327 xasprintf(&prompt, "%s> ", identname);
2332 static int cmd_network(int argc, char *argv[]) {
2334 fprintf(stderr, "Too many arguments!\n");
2339 return switch_network(argv[1]);
2341 DIR *dir = opendir(confdir);
2343 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2348 while((ent = readdir(dir))) {
2349 if(*ent->d_name == '.')
2352 if(!strcmp(ent->d_name, "tinc.conf")) {
2357 char fname[PATH_MAX];
2358 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2359 if(!access(fname, R_OK))
2360 printf("%s\n", ent->d_name);
2368 static int cmd_fsck(int argc, char *argv[]) {
2370 fprintf(stderr, "Too many arguments!\n");
2374 return fsck(orig_argv[0]);
2377 static void *readfile(FILE *in, size_t *len) {
2379 size_t alloced = 4096;
2380 char *buf = xmalloc(alloced);
2383 size_t read = fread(buf + count, 1, alloced - count, in);
2387 if(count >= alloced) {
2389 buf = xrealloc(buf, alloced);
2399 static int cmd_sign(int argc, char *argv[]) {
2401 fprintf(stderr, "Too many arguments!\n");
2406 name = get_my_name(true);
2411 char fname[PATH_MAX];
2412 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
2413 FILE *fp = fopen(fname, "r");
2415 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2419 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2422 fprintf(stderr, "Could not read private key from %s\n", fname);
2432 in = fopen(argv[1], "rb");
2434 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2443 char *data = readfile(in, &len);
2447 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2452 // Ensure we sign our name and current time as well
2453 long t = time(NULL);
2455 xasprintf(&trailer, " %s %ld", name, t);
2456 int trailer_len = strlen(trailer);
2458 data = xrealloc(data, len + trailer_len);
2459 memcpy(data + len, trailer, trailer_len);
2463 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2464 fprintf(stderr, "Error generating signature\n");
2469 b64encode(sig, sig, 64);
2472 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2473 fwrite(data, len, 1, stdout);
2479 static int cmd_verify(int argc, char *argv[]) {
2481 fprintf(stderr, "Not enough arguments!\n");
2486 fprintf(stderr, "Too many arguments!\n");
2490 char *node = argv[1];
2491 if(!strcmp(node, ".")) {
2493 name = get_my_name(true);
2498 } else if(!strcmp(node, "*")) {
2501 if(!check_id(node)) {
2502 fprintf(stderr, "Invalid node name\n");
2510 in = fopen(argv[2], "rb");
2512 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2520 char *data = readfile(in, &len);
2524 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2528 char *newline = memchr(data, '\n', len);
2529 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2530 fprintf(stderr, "Invalid input\n");
2536 size_t skip = newline - data;
2538 char signer[MAX_STRING_SIZE] = "";
2539 char sig[MAX_STRING_SIZE] = "";
2542 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2543 fprintf(stderr, "Invalid input\n");
2548 if(node && strcmp(node, signer)) {
2549 fprintf(stderr, "Signature is not made by %s\n", node);
2558 xasprintf(&trailer, " %s %ld", signer, t);
2559 int trailer_len = strlen(trailer);
2561 data = xrealloc(data, len + trailer_len);
2562 memcpy(data + len, trailer, trailer_len);
2565 newline = data + skip;
2567 char fname[PATH_MAX];
2568 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, node);
2569 FILE *fp = fopen(fname, "r");
2571 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2576 ecdsa_t *key = get_pubkey(fp);
2579 key = ecdsa_read_pem_public_key(fp);
2582 fprintf(stderr, "Could not read public key from %s\n", fname);
2590 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2591 fprintf(stderr, "Invalid signature\n");
2599 fwrite(newline, len - (newline - data), 1, stdout);
2605 static const struct {
2606 const char *command;
2607 int (*function)(int argc, char *argv[]);
2610 {"start", cmd_start},
2612 {"restart", cmd_restart},
2613 {"reload", cmd_reload},
2616 {"purge", cmd_purge},
2617 {"debug", cmd_debug},
2618 {"retry", cmd_retry},
2619 {"connect", cmd_connect},
2620 {"disconnect", cmd_disconnect},
2625 {"config", cmd_config, true},
2626 {"add", cmd_config},
2627 {"del", cmd_config},
2628 {"get", cmd_config},
2629 {"set", cmd_config},
2631 {"generate-keys", cmd_generate_keys},
2632 #ifndef DISABLE_LEGACY
2633 {"generate-rsa-keys", cmd_generate_rsa_keys},
2635 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2637 {"version", cmd_version},
2640 {"export", cmd_export},
2641 {"export-all", cmd_export_all},
2642 {"import", cmd_import},
2643 {"exchange", cmd_exchange},
2644 {"exchange-all", cmd_exchange_all},
2645 {"invite", cmd_invite},
2647 {"network", cmd_network},
2650 {"verify", cmd_verify},
2654 #ifdef HAVE_READLINE
2655 static char *complete_command(const char *text, int state) {
2663 while(commands[i].command) {
2664 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2665 return xstrdup(commands[i].command);
2672 static char *complete_dump(const char *text, int state) {
2673 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2682 if(!strncasecmp(matches[i], text, strlen(text)))
2683 return xstrdup(matches[i]);
2690 static char *complete_config(const char *text, int state) {
2698 while(variables[i].name) {
2699 char *dot = strchr(text, '.');
2701 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2703 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2707 if(!strncasecmp(variables[i].name, text, strlen(text)))
2708 return xstrdup(variables[i].name);
2716 static char *complete_info(const char *text, int state) {
2720 if(!connect_tincd(false))
2722 // Check the list of nodes
2723 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2724 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2727 while(recvline(fd, line, sizeof line)) {
2729 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
2739 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2743 if(!strncmp(item, text, strlen(text)))
2744 return xstrdup(strip_weight(item));
2750 static char *complete_nothing(const char *text, int state) {
2754 static char **completion (const char *text, int start, int end) {
2755 char **matches = NULL;
2758 matches = rl_completion_matches(text, complete_command);
2759 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2760 matches = rl_completion_matches(text, complete_dump);
2761 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2762 matches = rl_completion_matches(text, complete_config);
2763 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2764 matches = rl_completion_matches(text, complete_config);
2765 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2766 matches = rl_completion_matches(text, complete_config);
2767 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2768 matches = rl_completion_matches(text, complete_config);
2769 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2770 matches = rl_completion_matches(text, complete_info);
2776 static int cmd_shell(int argc, char *argv[]) {
2777 xasprintf(&prompt, "%s> ", identname);
2781 int maxargs = argc + 16;
2782 char **nargv = xmalloc(maxargs * sizeof *nargv);
2784 for(int i = 0; i < argc; i++)
2787 #ifdef HAVE_READLINE
2788 rl_readline_name = "tinc";
2789 rl_completion_entry_function = complete_nothing;
2790 rl_attempted_completion_function = completion;
2791 rl_filename_completion_desired = 0;
2796 #ifdef HAVE_READLINE
2800 rl_basic_word_break_characters = "\t\n ";
2801 line = readline(prompt);
2803 copy = xstrdup(line);
2805 line = fgets(buf, sizeof buf, stdin);
2809 fputs(prompt, stdout);
2811 line = fgets(buf, sizeof buf, stdin);
2817 /* Ignore comments */
2825 char *p = line + strspn(line, " \t\n");
2826 char *next = strtok(p, " \t\n");
2829 if(nargc >= maxargs) {
2831 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2836 next = strtok(NULL, " \t\n");
2842 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
2849 for(int i = 0; commands[i].command; i++) {
2850 if(!strcasecmp(nargv[argc], commands[i].command)) {
2851 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2857 #ifdef HAVE_READLINE
2863 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2876 int main(int argc, char *argv[]) {
2877 program_name = argv[0];
2880 tty = isatty(0) && isatty(1);
2882 if(!parse_options(argc, argv))
2886 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2887 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2900 static struct WSAData wsa_state;
2902 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2903 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2912 return cmd_shell(argc, argv);
2914 for(int i = 0; commands[i].command; i++) {
2915 if(!strcasecmp(argv[optind], commands[i].command))
2916 return commands[i].function(argc - optind, argv + optind);
2919 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);