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 %u: %s\n", pid, strerror(errno));
958 fprintf(stderr, "Sent TERM signal to process with PID %u.\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));
1038 if(!fgets(buf, sizeof buf, f)) {
1039 fprintf(stderr, "Invalid invitation file %s", fname);
1045 char *eol = buf + strlen(buf);
1046 while(strchr("\t \r\n", *--eol))
1048 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1049 fprintf(stderr, "Invalid invitation file %s", fname);
1054 printf("%s %s\n", ent->d_name, buf + 7);
1060 fprintf(stderr, "No outstanding invitations.\n");
1065 static int cmd_dump(int argc, char *argv[]) {
1066 bool only_reachable = false;
1068 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1069 if(strcasecmp(argv[2], "nodes")) {
1070 fprintf(stderr, "`reachable' only supported for nodes.\n");
1074 only_reachable = true;
1080 fprintf(stderr, "Invalid number of arguments.\n");
1085 if(!strcasecmp(argv[1], "invitations"))
1086 return dump_invitations();
1088 if(!connect_tincd(true))
1093 if(!strcasecmp(argv[1], "nodes"))
1094 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1095 else if(!strcasecmp(argv[1], "edges"))
1096 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1097 else if(!strcasecmp(argv[1], "subnets"))
1098 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1099 else if(!strcasecmp(argv[1], "connections"))
1100 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1101 else if(!strcasecmp(argv[1], "graph")) {
1102 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1103 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1105 } else if(!strcasecmp(argv[1], "digraph")) {
1106 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1107 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1110 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1116 printf("graph {\n");
1117 else if(do_graph == 2)
1118 printf("digraph {\n");
1120 while(recvline(fd, line, sizeof line)) {
1121 char node1[4096], node2[4096];
1122 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1124 if(do_graph && req == REQ_DUMP_NODES)
1142 char local_host[4096];
1143 char local_port[4096];
1146 int cipher, digest, maclength, compression, distance, socket, weight;
1147 short int pmtu, minmtu, maxmtu;
1148 unsigned int options, status_int;
1149 node_status_t status;
1150 long int last_state_change;
1153 case REQ_DUMP_NODES: {
1154 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);
1156 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1160 memcpy(&status, &status_int, sizeof status);
1163 const char *color = "black";
1164 if(!strcmp(host, "MYSELF"))
1166 else if(!status.reachable)
1168 else if(strcmp(via, node))
1170 else if(!status.validkey)
1174 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1176 if(only_reachable && !status.reachable)
1178 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",
1179 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1183 case REQ_DUMP_EDGES: {
1184 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);
1186 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1191 float w = 1 + 65536.0 / weight;
1192 if(do_graph == 1 && strcmp(node1, node2) > 0)
1193 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1194 else if(do_graph == 2)
1195 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1197 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);
1201 case REQ_DUMP_SUBNETS: {
1202 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1204 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1207 printf("%s owner %s\n", strip_weight(subnet), node);
1210 case REQ_DUMP_CONNECTIONS: {
1211 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1213 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1216 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1220 fprintf(stderr, "Unable to parse dump from tincd.\n");
1225 fprintf(stderr, "Error receiving dump.\n");
1229 static int cmd_purge(int argc, char *argv[]) {
1231 fprintf(stderr, "Too many arguments!\n");
1235 if(!connect_tincd(true))
1238 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1239 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1240 fprintf(stderr, "Could not purge old information.\n");
1247 static int cmd_debug(int argc, char *argv[]) {
1249 fprintf(stderr, "Invalid number of arguments.\n");
1253 if(!connect_tincd(true))
1256 int debuglevel = atoi(argv[1]);
1259 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1260 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1261 fprintf(stderr, "Could not set debug level.\n");
1265 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1269 static int cmd_retry(int argc, char *argv[]) {
1271 fprintf(stderr, "Too many arguments!\n");
1275 if(!connect_tincd(true))
1278 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1279 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1280 fprintf(stderr, "Could not retry outgoing connections.\n");
1287 static int cmd_connect(int argc, char *argv[]) {
1289 fprintf(stderr, "Invalid number of arguments.\n");
1293 if(!check_id(argv[1])) {
1294 fprintf(stderr, "Invalid name for node.\n");
1298 if(!connect_tincd(true))
1301 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1302 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1303 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1310 static int cmd_disconnect(int argc, char *argv[]) {
1312 fprintf(stderr, "Invalid number of arguments.\n");
1316 if(!check_id(argv[1])) {
1317 fprintf(stderr, "Invalid name for node.\n");
1321 if(!connect_tincd(true))
1324 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1325 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1326 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1333 static int cmd_top(int argc, char *argv[]) {
1335 fprintf(stderr, "Too many arguments!\n");
1340 if(!connect_tincd(true))
1346 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1351 static int cmd_pcap(int argc, char *argv[]) {
1353 fprintf(stderr, "Too many arguments!\n");
1357 if(!connect_tincd(true))
1360 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1365 static void sigint_handler(int sig) {
1366 fprintf(stderr, "\n");
1367 shutdown(fd, SHUT_RDWR);
1371 static int cmd_log(int argc, char *argv[]) {
1373 fprintf(stderr, "Too many arguments!\n");
1377 if(!connect_tincd(true))
1381 signal(SIGINT, sigint_handler);
1384 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1387 signal(SIGINT, SIG_DFL);
1395 static int cmd_pid(int argc, char *argv[]) {
1397 fprintf(stderr, "Too many arguments!\n");
1401 if(!connect_tincd(true) || !pid)
1404 printf("%d\n", pid);
1408 int rstrip(char *value) {
1409 int len = strlen(value);
1410 while(len && strchr("\t\r\n ", value[len - 1]))
1415 char *get_my_name(bool verbose) {
1416 FILE *f = fopen(tinc_conf, "r");
1419 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1425 while(fgets(buf, sizeof buf, f)) {
1426 int len = strcspn(buf, "\t =");
1428 value += strspn(value, "\t ");
1431 value += strspn(value, "\t ");
1436 if(strcasecmp(buf, "Name"))
1440 return replace_name(value);
1446 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1450 ecdsa_t *get_pubkey(FILE *f) {
1453 while(fgets(buf, sizeof buf, f)) {
1454 int len = strcspn(buf, "\t =");
1456 value += strspn(value, "\t ");
1459 value += strspn(value, "\t ");
1464 if(strcasecmp(buf, "Ed25519PublicKey"))
1467 return ecdsa_set_base64_public_key(value);
1473 const var_t variables[] = {
1474 /* Server configuration */
1475 {"AddressFamily", VAR_SERVER},
1476 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1477 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1478 {"BindToInterface", VAR_SERVER},
1479 {"Broadcast", VAR_SERVER | VAR_SAFE},
1480 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1481 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1482 {"DecrementTTL", VAR_SERVER},
1483 {"Device", VAR_SERVER},
1484 {"DeviceStandby", VAR_SERVER},
1485 {"DeviceType", VAR_SERVER},
1486 {"DirectOnly", VAR_SERVER},
1487 {"Ed25519PrivateKeyFile", VAR_SERVER},
1488 {"ExperimentalProtocol", VAR_SERVER},
1489 {"Forwarding", VAR_SERVER},
1490 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1491 {"Hostnames", VAR_SERVER},
1492 {"IffOneQueue", VAR_SERVER},
1493 {"Interface", VAR_SERVER},
1494 {"KeyExpire", VAR_SERVER},
1495 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1496 {"LocalDiscovery", VAR_SERVER},
1497 {"MACExpire", VAR_SERVER},
1498 {"MaxConnectionBurst", VAR_SERVER},
1499 {"MaxOutputBufferSize", VAR_SERVER},
1500 {"MaxTimeout", VAR_SERVER},
1501 {"Mode", VAR_SERVER | VAR_SAFE},
1502 {"Name", VAR_SERVER},
1503 {"PingInterval", VAR_SERVER},
1504 {"PingTimeout", VAR_SERVER},
1505 {"PriorityInheritance", VAR_SERVER},
1506 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1507 {"PrivateKeyFile", VAR_SERVER},
1508 {"ProcessPriority", VAR_SERVER},
1509 {"Proxy", VAR_SERVER},
1510 {"ReplayWindow", VAR_SERVER},
1511 {"ScriptsExtension", VAR_SERVER},
1512 {"ScriptsInterpreter", VAR_SERVER},
1513 {"StrictSubnets", VAR_SERVER},
1514 {"TunnelServer", VAR_SERVER},
1515 {"UDPDiscovery", VAR_SERVER},
1516 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1517 {"UDPDiscoveryInterval", VAR_SERVER},
1518 {"UDPDiscoveryTimeout", VAR_SERVER},
1519 {"MTUInfoInterval", VAR_SERVER},
1520 {"UDPInfoInterval", VAR_SERVER},
1521 {"UDPRcvBuf", VAR_SERVER},
1522 {"UDPSndBuf", VAR_SERVER},
1523 {"UPnP", VAR_SERVER},
1524 {"UPnPDiscoverWait", VAR_SERVER},
1525 {"UPnPRefreshPeriod", VAR_SERVER},
1526 {"VDEGroup", VAR_SERVER},
1527 {"VDEPort", VAR_SERVER},
1528 /* Host configuration */
1529 {"Address", VAR_HOST | VAR_MULTIPLE},
1530 {"Cipher", VAR_SERVER | VAR_HOST},
1531 {"ClampMSS", VAR_SERVER | VAR_HOST},
1532 {"Compression", VAR_SERVER | VAR_HOST},
1533 {"Digest", VAR_SERVER | VAR_HOST},
1534 {"Ed25519PublicKey", VAR_HOST},
1535 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1536 {"IndirectData", VAR_SERVER | VAR_HOST},
1537 {"MACLength", VAR_SERVER | VAR_HOST},
1538 {"PMTU", VAR_SERVER | VAR_HOST},
1539 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1541 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1542 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1543 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1544 {"TCPOnly", VAR_SERVER | VAR_HOST},
1545 {"Weight", VAR_HOST | VAR_SAFE},
1549 static int cmd_config(int argc, char *argv[]) {
1551 fprintf(stderr, "Invalid number of arguments.\n");
1555 if(strcasecmp(argv[0], "config"))
1559 if(!strcasecmp(argv[1], "get")) {
1561 } else if(!strcasecmp(argv[1], "add")) {
1562 argv++, argc--, action = 1;
1563 } else if(!strcasecmp(argv[1], "del")) {
1564 argv++, argc--, action = -1;
1565 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1566 argv++, argc--, action = 0;
1570 fprintf(stderr, "Invalid number of arguments.\n");
1574 // Concatenate the rest of the command line
1575 strncpy(line, argv[1], sizeof line - 1);
1576 for(int i = 2; i < argc; i++) {
1577 strncat(line, " ", sizeof line - 1 - strlen(line));
1578 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1581 // Liberal parsing into node name, variable name and value.
1587 len = strcspn(line, "\t =");
1589 value += strspn(value, "\t ");
1592 value += strspn(value, "\t ");
1595 variable = strchr(line, '.');
1604 fprintf(stderr, "No variable given.\n");
1608 if(action >= 0 && !*value) {
1609 fprintf(stderr, "No value for variable given.\n");
1613 if(action < -1 && *value)
1616 /* Some simple checks. */
1618 bool warnonremove = false;
1620 for(int i = 0; variables[i].name; i++) {
1621 if(strcasecmp(variables[i].name, variable))
1625 variable = (char *)variables[i].name;
1627 /* Discourage use of obsolete variables. */
1629 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1631 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1633 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1638 /* Don't put server variables in host config files */
1640 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1642 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1644 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1649 /* Should this go into our own host config file? */
1651 if(!node && !(variables[i].type & VAR_SERVER)) {
1652 node = get_my_name(true);
1657 /* Change "add" into "set" for variables that do not allow multiple occurences.
1658 Turn on warnings when it seems variables might be removed unintentionally. */
1660 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1661 warnonremove = true;
1663 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1664 warnonremove = true;
1670 if(node && !check_id(node)) {
1671 fprintf(stderr, "Invalid name for node.\n");
1676 if(force || action < 0) {
1677 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1679 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1684 // Open the right configuration file.
1685 char filename[PATH_MAX];
1687 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1689 snprintf(filename, sizeof filename, "%s", tinc_conf);
1691 FILE *f = fopen(filename, "r");
1693 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1697 char tmpfile[PATH_MAX];
1701 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1702 tf = fopen(tmpfile, "w");
1704 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1710 // Copy the file, making modifications on the fly, unless we are just getting a value.
1714 bool removed = false;
1717 while(fgets(buf1, sizeof buf1, f)) {
1718 buf1[sizeof buf1 - 1] = 0;
1719 strncpy(buf2, buf1, sizeof buf2);
1721 // Parse line in a simple way
1725 len = strcspn(buf2, "\t =");
1726 bvalue = buf2 + len;
1727 bvalue += strspn(bvalue, "\t ");
1728 if(*bvalue == '=') {
1730 bvalue += strspn(bvalue, "\t ");
1736 if(!strcasecmp(buf2, variable)) {
1740 printf("%s\n", bvalue);
1742 } else if(action == -1) {
1743 if(!*value || !strcasecmp(bvalue, value)) {
1748 } else if(action == 0) {
1749 // Warn if "set" was used for variables that can occur multiple times
1750 if(warnonremove && strcasecmp(bvalue, value))
1751 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1753 // Already set? Delete the rest...
1757 // Otherwise, replace.
1758 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1759 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1765 } else if(action > 0) {
1766 // Check if we've already seen this variable with the same value
1767 if(!strcasecmp(bvalue, value))
1773 // Copy original line...
1774 if(fputs(buf1, tf) < 0) {
1775 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1779 // Add newline if it is missing...
1780 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1781 if(fputc('\n', tf) < 0) {
1782 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1789 // Make sure we read everything...
1790 if(ferror(f) || !feof(f)) {
1791 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1796 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1800 // Add new variable if necessary.
1801 if((action > 0 && !found)|| (action == 0 && !set)) {
1802 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1803 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1812 fprintf(stderr, "No matching configuration variables found.\n");
1817 // Make sure we wrote everything...
1819 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1823 // Could we find what we had to remove?
1824 if(action < 0 && !removed) {
1826 fprintf(stderr, "No configuration variables deleted.\n");
1830 // Replace the configuration file with the new one
1832 if(remove(filename)) {
1833 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1837 if(rename(tmpfile, filename)) {
1838 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1842 // Silently try notifying a running tincd of changes.
1843 if(connect_tincd(false))
1844 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1849 static bool try_bind(int port) {
1850 struct addrinfo *ai = NULL, *aip;
1851 struct addrinfo hint = {
1852 .ai_flags = AI_PASSIVE,
1853 .ai_family = AF_UNSPEC,
1854 .ai_socktype = SOCK_STREAM,
1855 .ai_protocol = IPPROTO_TCP,
1858 bool success = true;
1860 snprintf(portstr, sizeof portstr, "%d", port);
1862 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1865 for(aip = ai; aip; aip = aip->ai_next) {
1866 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1872 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1884 int check_port(char *name) {
1888 fprintf(stderr, "Warning: could not bind to port 655. ");
1890 for(int i = 0; i < 100; i++) {
1891 int port = 0x1000 + (rand() & 0x7fff);
1892 if(try_bind(port)) {
1893 char filename[PATH_MAX];
1894 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1895 FILE *f = fopen(filename, "a");
1897 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1898 fprintf(stderr, "Please change tinc's Port manually.\n");
1902 fprintf(f, "Port = %d\n", port);
1904 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1909 fprintf(stderr, "Please change tinc's Port manually.\n");
1913 static int cmd_init(int argc, char *argv[]) {
1914 if(!access(tinc_conf, F_OK)) {
1915 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1920 fprintf(stderr, "Too many arguments!\n");
1922 } else if(argc < 2) {
1925 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1926 if(!fgets(buf, sizeof buf, stdin)) {
1927 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1930 int len = rstrip(buf);
1932 fprintf(stderr, "No name given!\n");
1937 fprintf(stderr, "No Name given!\n");
1941 name = strdup(argv[1]);
1943 fprintf(stderr, "No Name given!\n");
1948 if(!check_id(name)) {
1949 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1953 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1954 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1958 if(mkdir(confbase, 0777) && errno != EEXIST) {
1959 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1963 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1964 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1968 FILE *f = fopen(tinc_conf, "w");
1970 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1974 fprintf(f, "Name = %s\n", name);
1977 #ifndef DISABLE_LEGACY
1978 if(!rsa_keygen(2048, false))
1982 if(!ed25519_keygen(false))
1988 char filename[PATH_MAX];
1989 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1990 if(access(filename, F_OK)) {
1991 FILE *f = fopenmask(filename, "w", 0777);
1993 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1996 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");
2005 static int cmd_generate_keys(int argc, char *argv[]) {
2006 #ifdef DISABLE_LEGACY
2011 fprintf(stderr, "Too many arguments!\n");
2016 name = get_my_name(false);
2018 #ifndef DISABLE_LEGACY
2019 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
2023 if(!ed25519_keygen(true))
2029 #ifndef DISABLE_LEGACY
2030 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2032 fprintf(stderr, "Too many arguments!\n");
2037 name = get_my_name(false);
2039 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2043 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2045 fprintf(stderr, "Too many arguments!\n");
2050 name = get_my_name(false);
2052 return !ed25519_keygen(true);
2055 static int cmd_help(int argc, char *argv[]) {
2060 static int cmd_version(int argc, char *argv[]) {
2062 fprintf(stderr, "Too many arguments!\n");
2070 static int cmd_info(int argc, char *argv[]) {
2072 fprintf(stderr, "Invalid number of arguments.\n");
2076 if(!connect_tincd(true))
2079 return info(fd, argv[1]);
2082 static const char *conffiles[] = {
2093 static int cmd_edit(int argc, char *argv[]) {
2095 fprintf(stderr, "Invalid number of arguments.\n");
2099 char filename[PATH_MAX] = "";
2101 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2102 for(int i = 0; conffiles[i]; i++) {
2103 if(!strcmp(argv[1], conffiles[i])) {
2104 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2113 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2114 char *dash = strchr(argv[1], '-');
2117 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2118 fprintf(stderr, "Invalid configuration filename.\n");
2126 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2128 xasprintf(&command, "edit \"%s\"", filename);
2130 int result = system(command);
2135 // Silently try notifying a running tincd of changes.
2136 if(connect_tincd(false))
2137 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2142 static int export(const char *name, FILE *out) {
2143 char filename[PATH_MAX];
2144 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2145 FILE *in = fopen(filename, "r");
2147 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2151 fprintf(out, "Name = %s\n", name);
2153 while(fgets(buf, sizeof buf, in)) {
2154 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2159 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2168 static int cmd_export(int argc, char *argv[]) {
2170 fprintf(stderr, "Too many arguments!\n");
2174 char *name = get_my_name(true);
2178 int result = export(name, stdout);
2186 static int cmd_export_all(int argc, char *argv[]) {
2188 fprintf(stderr, "Too many arguments!\n");
2192 DIR *dir = opendir(hosts_dir);
2194 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2202 while((ent = readdir(dir))) {
2203 if(!check_id(ent->d_name))
2209 printf("#---------------------------------------------------------------#\n");
2211 result |= export(ent->d_name, stdout);
2220 static int cmd_import(int argc, char *argv[]) {
2222 fprintf(stderr, "Too many arguments!\n");
2231 char filename[PATH_MAX] = "";
2233 bool firstline = true;
2235 while(fgets(buf, sizeof buf, in)) {
2236 if(sscanf(buf, "Name = %4095s", name) == 1) {
2239 if(!check_id(name)) {
2240 fprintf(stderr, "Invalid Name in input!\n");
2247 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2249 if(!force && !access(filename, F_OK)) {
2250 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2255 out = fopen(filename, "w");
2257 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2263 } else if(firstline) {
2264 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2269 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2273 if(fputs(buf, out) < 0) {
2274 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2284 fprintf(stderr, "Imported %d host configuration files.\n", count);
2287 fprintf(stderr, "No host configuration files imported.\n");
2292 static int cmd_exchange(int argc, char *argv[]) {
2293 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2296 static int cmd_exchange_all(int argc, char *argv[]) {
2297 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2300 static int switch_network(char *name) {
2301 if(strcmp(name, ".")) {
2302 if(!check_netname(name, false)) {
2303 fprintf(stderr, "Invalid character in netname!\n");
2307 if(!check_netname(name, true))
2308 fprintf(stderr, "Warning: unsafe character in netname!\n");
2317 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2324 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2325 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2326 xasprintf(&prompt, "%s> ", identname);
2331 static int cmd_network(int argc, char *argv[]) {
2333 fprintf(stderr, "Too many arguments!\n");
2338 return switch_network(argv[1]);
2340 DIR *dir = opendir(confdir);
2342 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2347 while((ent = readdir(dir))) {
2348 if(*ent->d_name == '.')
2351 if(!strcmp(ent->d_name, "tinc.conf")) {
2356 char fname[PATH_MAX];
2357 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2358 if(!access(fname, R_OK))
2359 printf("%s\n", ent->d_name);
2367 static int cmd_fsck(int argc, char *argv[]) {
2369 fprintf(stderr, "Too many arguments!\n");
2373 return fsck(orig_argv[0]);
2376 static void *readfile(FILE *in, size_t *len) {
2378 size_t alloced = 4096;
2379 char *buf = xmalloc(alloced);
2382 size_t read = fread(buf + count, 1, alloced - count, in);
2386 if(count >= alloced) {
2388 buf = xrealloc(buf, alloced);
2398 static int cmd_sign(int argc, char *argv[]) {
2400 fprintf(stderr, "Too many arguments!\n");
2405 name = get_my_name(true);
2410 char fname[PATH_MAX];
2411 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
2412 FILE *fp = fopen(fname, "r");
2414 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2418 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2421 fprintf(stderr, "Could not read private key from %s\n", fname);
2431 in = fopen(argv[1], "rb");
2433 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2442 char *data = readfile(in, &len);
2446 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2451 // Ensure we sign our name and current time as well
2452 long t = time(NULL);
2454 xasprintf(&trailer, " %s %ld", name, t);
2455 int trailer_len = strlen(trailer);
2457 data = xrealloc(data, len + trailer_len);
2458 memcpy(data + len, trailer, trailer_len);
2462 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2463 fprintf(stderr, "Error generating signature\n");
2468 b64encode(sig, sig, 64);
2471 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2472 fwrite(data, len, 1, stdout);
2478 static int cmd_verify(int argc, char *argv[]) {
2480 fprintf(stderr, "Not enough arguments!\n");
2485 fprintf(stderr, "Too many arguments!\n");
2489 char *node = argv[1];
2490 if(!strcmp(node, ".")) {
2492 name = get_my_name(true);
2497 } else if(!strcmp(node, "*")) {
2500 if(!check_id(node)) {
2501 fprintf(stderr, "Invalid node name\n");
2509 in = fopen(argv[2], "rb");
2511 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2519 char *data = readfile(in, &len);
2523 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2527 char *newline = memchr(data, '\n', len);
2528 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2529 fprintf(stderr, "Invalid input\n");
2535 size_t skip = newline - data;
2537 char signer[MAX_STRING_SIZE] = "";
2538 char sig[MAX_STRING_SIZE] = "";
2541 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2542 fprintf(stderr, "Invalid input\n");
2547 if(node && strcmp(node, signer)) {
2548 fprintf(stderr, "Signature is not made by %s\n", node);
2557 xasprintf(&trailer, " %s %ld", signer, t);
2558 int trailer_len = strlen(trailer);
2560 data = xrealloc(data, len + trailer_len);
2561 memcpy(data + len, trailer, trailer_len);
2564 newline = data + skip;
2566 char fname[PATH_MAX];
2567 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, node);
2568 FILE *fp = fopen(fname, "r");
2570 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2575 ecdsa_t *key = get_pubkey(fp);
2578 key = ecdsa_read_pem_public_key(fp);
2581 fprintf(stderr, "Could not read public key from %s\n", fname);
2589 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2590 fprintf(stderr, "Invalid signature\n");
2598 fwrite(newline, len - (newline - data), 1, stdout);
2604 static const struct {
2605 const char *command;
2606 int (*function)(int argc, char *argv[]);
2609 {"start", cmd_start},
2611 {"restart", cmd_restart},
2612 {"reload", cmd_reload},
2615 {"purge", cmd_purge},
2616 {"debug", cmd_debug},
2617 {"retry", cmd_retry},
2618 {"connect", cmd_connect},
2619 {"disconnect", cmd_disconnect},
2624 {"config", cmd_config, true},
2625 {"add", cmd_config},
2626 {"del", cmd_config},
2627 {"get", cmd_config},
2628 {"set", cmd_config},
2630 {"generate-keys", cmd_generate_keys},
2631 #ifndef DISABLE_LEGACY
2632 {"generate-rsa-keys", cmd_generate_rsa_keys},
2634 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2636 {"version", cmd_version},
2639 {"export", cmd_export},
2640 {"export-all", cmd_export_all},
2641 {"import", cmd_import},
2642 {"exchange", cmd_exchange},
2643 {"exchange-all", cmd_exchange_all},
2644 {"invite", cmd_invite},
2646 {"network", cmd_network},
2649 {"verify", cmd_verify},
2653 #ifdef HAVE_READLINE
2654 static char *complete_command(const char *text, int state) {
2662 while(commands[i].command) {
2663 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2664 return xstrdup(commands[i].command);
2671 static char *complete_dump(const char *text, int state) {
2672 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2681 if(!strncasecmp(matches[i], text, strlen(text)))
2682 return xstrdup(matches[i]);
2689 static char *complete_config(const char *text, int state) {
2697 while(variables[i].name) {
2698 char *dot = strchr(text, '.');
2700 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2702 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2706 if(!strncasecmp(variables[i].name, text, strlen(text)))
2707 return xstrdup(variables[i].name);
2715 static char *complete_info(const char *text, int state) {
2719 if(!connect_tincd(false))
2721 // Check the list of nodes
2722 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2723 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2726 while(recvline(fd, line, sizeof line)) {
2728 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
2738 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2742 if(!strncmp(item, text, strlen(text)))
2743 return xstrdup(strip_weight(item));
2749 static char *complete_nothing(const char *text, int state) {
2753 static char **completion (const char *text, int start, int end) {
2754 char **matches = NULL;
2757 matches = rl_completion_matches(text, complete_command);
2758 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2759 matches = rl_completion_matches(text, complete_dump);
2760 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2761 matches = rl_completion_matches(text, complete_config);
2762 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2763 matches = rl_completion_matches(text, complete_config);
2764 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2765 matches = rl_completion_matches(text, complete_config);
2766 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2767 matches = rl_completion_matches(text, complete_config);
2768 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2769 matches = rl_completion_matches(text, complete_info);
2775 static int cmd_shell(int argc, char *argv[]) {
2776 xasprintf(&prompt, "%s> ", identname);
2780 int maxargs = argc + 16;
2781 char **nargv = xmalloc(maxargs * sizeof *nargv);
2783 for(int i = 0; i < argc; i++)
2786 #ifdef HAVE_READLINE
2787 rl_readline_name = "tinc";
2788 rl_completion_entry_function = complete_nothing;
2789 rl_attempted_completion_function = completion;
2790 rl_filename_completion_desired = 0;
2795 #ifdef HAVE_READLINE
2799 rl_basic_word_break_characters = "\t\n ";
2800 line = readline(prompt);
2802 copy = xstrdup(line);
2804 line = fgets(buf, sizeof buf, stdin);
2808 fputs(prompt, stdout);
2810 line = fgets(buf, sizeof buf, stdin);
2816 /* Ignore comments */
2824 char *p = line + strspn(line, " \t\n");
2825 char *next = strtok(p, " \t\n");
2828 if(nargc >= maxargs) {
2829 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2832 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2837 next = strtok(NULL, " \t\n");
2843 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
2850 for(int i = 0; commands[i].command; i++) {
2851 if(!strcasecmp(nargv[argc], commands[i].command)) {
2852 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2858 #ifdef HAVE_READLINE
2864 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2877 int main(int argc, char *argv[]) {
2878 program_name = argv[0];
2881 tty = isatty(0) && isatty(1);
2883 if(!parse_options(argc, argv))
2887 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2888 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2901 static struct WSAData wsa_state;
2903 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2904 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2913 return cmd_shell(argc, argv);
2915 for(int i = 0; commands[i].command; i++) {
2916 if(!strcasecmp(argv[optind], commands[i].command))
2917 return commands[i].function(argc - optind, argv + optind);
2920 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);