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 {"KeyExpire", VAR_SERVER},
1494 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1495 {"LocalDiscovery", VAR_SERVER},
1496 {"MACExpire", VAR_SERVER},
1497 {"MaxConnectionBurst", VAR_SERVER},
1498 {"MaxOutputBufferSize", VAR_SERVER},
1499 {"MaxTimeout", VAR_SERVER},
1500 {"Mode", VAR_SERVER | VAR_SAFE},
1501 {"Name", VAR_SERVER},
1502 {"PingInterval", VAR_SERVER},
1503 {"PingTimeout", VAR_SERVER},
1504 {"PriorityInheritance", VAR_SERVER},
1505 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1506 {"PrivateKeyFile", VAR_SERVER},
1507 {"ProcessPriority", VAR_SERVER},
1508 {"Proxy", VAR_SERVER},
1509 {"ReplayWindow", VAR_SERVER},
1510 {"ScriptsExtension", VAR_SERVER},
1511 {"ScriptsInterpreter", VAR_SERVER},
1512 {"StrictSubnets", VAR_SERVER},
1513 {"TunnelServer", VAR_SERVER},
1514 {"UDPDiscovery", VAR_SERVER},
1515 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1516 {"UDPDiscoveryInterval", VAR_SERVER},
1517 {"UDPDiscoveryTimeout", VAR_SERVER},
1518 {"MTUInfoInterval", VAR_SERVER},
1519 {"UDPInfoInterval", VAR_SERVER},
1520 {"UDPRcvBuf", VAR_SERVER},
1521 {"UDPSndBuf", VAR_SERVER},
1522 {"UPnP", VAR_SERVER},
1523 {"UPnPDiscoverWait", VAR_SERVER},
1524 {"UPnPRefreshPeriod", VAR_SERVER},
1525 {"VDEGroup", VAR_SERVER},
1526 {"VDEPort", VAR_SERVER},
1527 /* Host configuration */
1528 {"Address", VAR_HOST | VAR_MULTIPLE},
1529 {"Cipher", VAR_SERVER | VAR_HOST},
1530 {"ClampMSS", VAR_SERVER | VAR_HOST},
1531 {"Compression", VAR_SERVER | VAR_HOST},
1532 {"Digest", VAR_SERVER | VAR_HOST},
1533 {"Ed25519PublicKey", VAR_HOST},
1534 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1535 {"IndirectData", VAR_SERVER | VAR_HOST},
1536 {"MACLength", VAR_SERVER | VAR_HOST},
1537 {"PMTU", VAR_SERVER | VAR_HOST},
1538 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1540 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1541 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1542 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1543 {"TCPOnly", VAR_SERVER | VAR_HOST},
1544 {"Weight", VAR_HOST | VAR_SAFE},
1548 static int cmd_config(int argc, char *argv[]) {
1550 fprintf(stderr, "Invalid number of arguments.\n");
1554 if(strcasecmp(argv[0], "config"))
1558 if(!strcasecmp(argv[1], "get")) {
1560 } else if(!strcasecmp(argv[1], "add")) {
1561 argv++, argc--, action = 1;
1562 } else if(!strcasecmp(argv[1], "del")) {
1563 argv++, argc--, action = -1;
1564 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1565 argv++, argc--, action = 0;
1569 fprintf(stderr, "Invalid number of arguments.\n");
1573 // Concatenate the rest of the command line
1574 strncpy(line, argv[1], sizeof line - 1);
1575 for(int i = 2; i < argc; i++) {
1576 strncat(line, " ", sizeof line - 1 - strlen(line));
1577 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1580 // Liberal parsing into node name, variable name and value.
1586 len = strcspn(line, "\t =");
1588 value += strspn(value, "\t ");
1591 value += strspn(value, "\t ");
1594 variable = strchr(line, '.');
1603 fprintf(stderr, "No variable given.\n");
1607 if(action >= 0 && !*value) {
1608 fprintf(stderr, "No value for variable given.\n");
1612 if(action < -1 && *value)
1615 /* Some simple checks. */
1617 bool warnonremove = false;
1619 for(int i = 0; variables[i].name; i++) {
1620 if(strcasecmp(variables[i].name, variable))
1624 variable = (char *)variables[i].name;
1626 /* Discourage use of obsolete variables. */
1628 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1630 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1632 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1637 /* Don't put server variables in host config files */
1639 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1641 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1643 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1648 /* Should this go into our own host config file? */
1650 if(!node && !(variables[i].type & VAR_SERVER)) {
1651 node = get_my_name(true);
1656 /* Change "add" into "set" for variables that do not allow multiple occurences.
1657 Turn on warnings when it seems variables might be removed unintentionally. */
1659 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1660 warnonremove = true;
1662 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1663 warnonremove = true;
1669 if(node && !check_id(node)) {
1670 fprintf(stderr, "Invalid name for node.\n");
1675 if(force || action < 0) {
1676 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1678 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1683 // Open the right configuration file.
1684 char filename[PATH_MAX];
1686 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1688 snprintf(filename, sizeof filename, "%s", tinc_conf);
1690 FILE *f = fopen(filename, "r");
1692 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1696 char tmpfile[PATH_MAX];
1700 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1701 tf = fopen(tmpfile, "w");
1703 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1709 // Copy the file, making modifications on the fly, unless we are just getting a value.
1713 bool removed = false;
1716 while(fgets(buf1, sizeof buf1, f)) {
1717 buf1[sizeof buf1 - 1] = 0;
1718 strncpy(buf2, buf1, sizeof buf2);
1720 // Parse line in a simple way
1724 len = strcspn(buf2, "\t =");
1725 bvalue = buf2 + len;
1726 bvalue += strspn(bvalue, "\t ");
1727 if(*bvalue == '=') {
1729 bvalue += strspn(bvalue, "\t ");
1735 if(!strcasecmp(buf2, variable)) {
1739 printf("%s\n", bvalue);
1741 } else if(action == -1) {
1742 if(!*value || !strcasecmp(bvalue, value)) {
1747 } else if(action == 0) {
1748 // Warn if "set" was used for variables that can occur multiple times
1749 if(warnonremove && strcasecmp(bvalue, value))
1750 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1752 // Already set? Delete the rest...
1756 // Otherwise, replace.
1757 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1758 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1764 } else if(action > 0) {
1765 // Check if we've already seen this variable with the same value
1766 if(!strcasecmp(bvalue, value))
1772 // Copy original line...
1773 if(fputs(buf1, tf) < 0) {
1774 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1778 // Add newline if it is missing...
1779 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1780 if(fputc('\n', tf) < 0) {
1781 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1788 // Make sure we read everything...
1789 if(ferror(f) || !feof(f)) {
1790 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1795 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1799 // Add new variable if necessary.
1800 if((action > 0 && !found)|| (action == 0 && !set)) {
1801 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1802 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1811 fprintf(stderr, "No matching configuration variables found.\n");
1816 // Make sure we wrote everything...
1818 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1822 // Could we find what we had to remove?
1823 if(action < 0 && !removed) {
1825 fprintf(stderr, "No configuration variables deleted.\n");
1829 // Replace the configuration file with the new one
1831 if(remove(filename)) {
1832 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1836 if(rename(tmpfile, filename)) {
1837 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1841 // Silently try notifying a running tincd of changes.
1842 if(connect_tincd(false))
1843 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1848 static bool try_bind(int port) {
1849 struct addrinfo *ai = NULL, *aip;
1850 struct addrinfo hint = {
1851 .ai_flags = AI_PASSIVE,
1852 .ai_family = AF_UNSPEC,
1853 .ai_socktype = SOCK_STREAM,
1854 .ai_protocol = IPPROTO_TCP,
1857 bool success = true;
1859 snprintf(portstr, sizeof portstr, "%d", port);
1861 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1864 for(aip = ai; aip; aip = aip->ai_next) {
1865 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1871 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1883 int check_port(char *name) {
1887 fprintf(stderr, "Warning: could not bind to port 655. ");
1889 for(int i = 0; i < 100; i++) {
1890 int port = 0x1000 + (rand() & 0x7fff);
1891 if(try_bind(port)) {
1892 char filename[PATH_MAX];
1893 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1894 FILE *f = fopen(filename, "a");
1896 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1897 fprintf(stderr, "Please change tinc's Port manually.\n");
1901 fprintf(f, "Port = %d\n", port);
1903 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1908 fprintf(stderr, "Please change tinc's Port manually.\n");
1912 static int cmd_init(int argc, char *argv[]) {
1913 if(!access(tinc_conf, F_OK)) {
1914 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1919 fprintf(stderr, "Too many arguments!\n");
1921 } else if(argc < 2) {
1924 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1925 if(!fgets(buf, sizeof buf, stdin)) {
1926 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1929 int len = rstrip(buf);
1931 fprintf(stderr, "No name given!\n");
1936 fprintf(stderr, "No Name given!\n");
1940 name = strdup(argv[1]);
1942 fprintf(stderr, "No Name given!\n");
1947 if(!check_id(name)) {
1948 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1952 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1953 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1957 if(mkdir(confbase, 0777) && errno != EEXIST) {
1958 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1962 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1963 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1967 FILE *f = fopen(tinc_conf, "w");
1969 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1973 fprintf(f, "Name = %s\n", name);
1976 #ifndef DISABLE_LEGACY
1977 if(!rsa_keygen(2048, false))
1981 if(!ed25519_keygen(false))
1987 char filename[PATH_MAX];
1988 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1989 if(access(filename, F_OK)) {
1990 FILE *f = fopenmask(filename, "w", 0777);
1992 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1995 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");
2004 static int cmd_generate_keys(int argc, char *argv[]) {
2005 #ifdef DISABLE_LEGACY
2010 fprintf(stderr, "Too many arguments!\n");
2015 name = get_my_name(false);
2017 #ifndef DISABLE_LEGACY
2018 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
2022 if(!ed25519_keygen(true))
2028 #ifndef DISABLE_LEGACY
2029 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2031 fprintf(stderr, "Too many arguments!\n");
2036 name = get_my_name(false);
2038 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2042 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2044 fprintf(stderr, "Too many arguments!\n");
2049 name = get_my_name(false);
2051 return !ed25519_keygen(true);
2054 static int cmd_help(int argc, char *argv[]) {
2059 static int cmd_version(int argc, char *argv[]) {
2061 fprintf(stderr, "Too many arguments!\n");
2069 static int cmd_info(int argc, char *argv[]) {
2071 fprintf(stderr, "Invalid number of arguments.\n");
2075 if(!connect_tincd(true))
2078 return info(fd, argv[1]);
2081 static const char *conffiles[] = {
2092 static int cmd_edit(int argc, char *argv[]) {
2094 fprintf(stderr, "Invalid number of arguments.\n");
2098 char filename[PATH_MAX] = "";
2100 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2101 for(int i = 0; conffiles[i]; i++) {
2102 if(!strcmp(argv[1], conffiles[i])) {
2103 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2112 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2113 char *dash = strchr(argv[1], '-');
2116 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2117 fprintf(stderr, "Invalid configuration filename.\n");
2125 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2127 xasprintf(&command, "edit \"%s\"", filename);
2129 int result = system(command);
2134 // Silently try notifying a running tincd of changes.
2135 if(connect_tincd(false))
2136 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2141 static int export(const char *name, FILE *out) {
2142 char filename[PATH_MAX];
2143 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2144 FILE *in = fopen(filename, "r");
2146 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2150 fprintf(out, "Name = %s\n", name);
2152 while(fgets(buf, sizeof buf, in)) {
2153 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2158 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2167 static int cmd_export(int argc, char *argv[]) {
2169 fprintf(stderr, "Too many arguments!\n");
2173 char *name = get_my_name(true);
2177 int result = export(name, stdout);
2185 static int cmd_export_all(int argc, char *argv[]) {
2187 fprintf(stderr, "Too many arguments!\n");
2191 DIR *dir = opendir(hosts_dir);
2193 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2201 while((ent = readdir(dir))) {
2202 if(!check_id(ent->d_name))
2208 printf("#---------------------------------------------------------------#\n");
2210 result |= export(ent->d_name, stdout);
2219 static int cmd_import(int argc, char *argv[]) {
2221 fprintf(stderr, "Too many arguments!\n");
2230 char filename[PATH_MAX] = "";
2232 bool firstline = true;
2234 while(fgets(buf, sizeof buf, in)) {
2235 if(sscanf(buf, "Name = %4095s", name) == 1) {
2238 if(!check_id(name)) {
2239 fprintf(stderr, "Invalid Name in input!\n");
2246 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2248 if(!force && !access(filename, F_OK)) {
2249 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2254 out = fopen(filename, "w");
2256 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2262 } else if(firstline) {
2263 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2268 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2272 if(fputs(buf, out) < 0) {
2273 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2283 fprintf(stderr, "Imported %d host configuration files.\n", count);
2286 fprintf(stderr, "No host configuration files imported.\n");
2291 static int cmd_exchange(int argc, char *argv[]) {
2292 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2295 static int cmd_exchange_all(int argc, char *argv[]) {
2296 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2299 static int switch_network(char *name) {
2300 if(strcmp(name, ".")) {
2301 if(!check_netname(name, false)) {
2302 fprintf(stderr, "Invalid character in netname!\n");
2306 if(!check_netname(name, true))
2307 fprintf(stderr, "Warning: unsafe character in netname!\n");
2316 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2323 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2324 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2325 xasprintf(&prompt, "%s> ", identname);
2330 static int cmd_network(int argc, char *argv[]) {
2332 fprintf(stderr, "Too many arguments!\n");
2337 return switch_network(argv[1]);
2339 DIR *dir = opendir(confdir);
2341 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2346 while((ent = readdir(dir))) {
2347 if(*ent->d_name == '.')
2350 if(!strcmp(ent->d_name, "tinc.conf")) {
2355 char fname[PATH_MAX];
2356 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2357 if(!access(fname, R_OK))
2358 printf("%s\n", ent->d_name);
2366 static int cmd_fsck(int argc, char *argv[]) {
2368 fprintf(stderr, "Too many arguments!\n");
2372 return fsck(orig_argv[0]);
2375 static void *readfile(FILE *in, size_t *len) {
2377 size_t alloced = 4096;
2378 char *buf = xmalloc(alloced);
2381 size_t read = fread(buf + count, 1, alloced - count, in);
2385 if(count >= alloced) {
2387 buf = xrealloc(buf, alloced);
2397 static int cmd_sign(int argc, char *argv[]) {
2399 fprintf(stderr, "Too many arguments!\n");
2404 name = get_my_name(true);
2409 char fname[PATH_MAX];
2410 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
2411 FILE *fp = fopen(fname, "r");
2413 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2417 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2420 fprintf(stderr, "Could not read private key from %s\n", fname);
2430 in = fopen(argv[1], "rb");
2432 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2441 char *data = readfile(in, &len);
2445 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2450 // Ensure we sign our name and current time as well
2451 long t = time(NULL);
2453 xasprintf(&trailer, " %s %ld", name, t);
2454 int trailer_len = strlen(trailer);
2456 data = xrealloc(data, len + trailer_len);
2457 memcpy(data + len, trailer, trailer_len);
2461 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2462 fprintf(stderr, "Error generating signature\n");
2467 b64encode(sig, sig, 64);
2470 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2471 fwrite(data, len, 1, stdout);
2477 static int cmd_verify(int argc, char *argv[]) {
2479 fprintf(stderr, "Not enough arguments!\n");
2484 fprintf(stderr, "Too many arguments!\n");
2488 char *node = argv[1];
2489 if(!strcmp(node, ".")) {
2491 name = get_my_name(true);
2496 } else if(!strcmp(node, "*")) {
2499 if(!check_id(node)) {
2500 fprintf(stderr, "Invalid node name\n");
2508 in = fopen(argv[2], "rb");
2510 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2518 char *data = readfile(in, &len);
2522 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2526 char *newline = memchr(data, '\n', len);
2527 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2528 fprintf(stderr, "Invalid input\n");
2534 size_t skip = newline - data;
2536 char signer[MAX_STRING_SIZE] = "";
2537 char sig[MAX_STRING_SIZE] = "";
2540 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2541 fprintf(stderr, "Invalid input\n");
2546 if(node && strcmp(node, signer)) {
2547 fprintf(stderr, "Signature is not made by %s\n", node);
2556 xasprintf(&trailer, " %s %ld", signer, t);
2557 int trailer_len = strlen(trailer);
2559 data = xrealloc(data, len + trailer_len);
2560 memcpy(data + len, trailer, trailer_len);
2563 newline = data + skip;
2565 char fname[PATH_MAX];
2566 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, node);
2567 FILE *fp = fopen(fname, "r");
2569 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2574 ecdsa_t *key = get_pubkey(fp);
2577 key = ecdsa_read_pem_public_key(fp);
2580 fprintf(stderr, "Could not read public key from %s\n", fname);
2588 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2589 fprintf(stderr, "Invalid signature\n");
2597 fwrite(newline, len - (newline - data), 1, stdout);
2603 static const struct {
2604 const char *command;
2605 int (*function)(int argc, char *argv[]);
2608 {"start", cmd_start},
2610 {"restart", cmd_restart},
2611 {"reload", cmd_reload},
2614 {"purge", cmd_purge},
2615 {"debug", cmd_debug},
2616 {"retry", cmd_retry},
2617 {"connect", cmd_connect},
2618 {"disconnect", cmd_disconnect},
2623 {"config", cmd_config, true},
2624 {"add", cmd_config},
2625 {"del", cmd_config},
2626 {"get", cmd_config},
2627 {"set", cmd_config},
2629 {"generate-keys", cmd_generate_keys},
2630 #ifndef DISABLE_LEGACY
2631 {"generate-rsa-keys", cmd_generate_rsa_keys},
2633 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2635 {"version", cmd_version},
2638 {"export", cmd_export},
2639 {"export-all", cmd_export_all},
2640 {"import", cmd_import},
2641 {"exchange", cmd_exchange},
2642 {"exchange-all", cmd_exchange_all},
2643 {"invite", cmd_invite},
2645 {"network", cmd_network},
2648 {"verify", cmd_verify},
2652 #ifdef HAVE_READLINE
2653 static char *complete_command(const char *text, int state) {
2661 while(commands[i].command) {
2662 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2663 return xstrdup(commands[i].command);
2670 static char *complete_dump(const char *text, int state) {
2671 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2680 if(!strncasecmp(matches[i], text, strlen(text)))
2681 return xstrdup(matches[i]);
2688 static char *complete_config(const char *text, int state) {
2696 while(variables[i].name) {
2697 char *dot = strchr(text, '.');
2699 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2701 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2705 if(!strncasecmp(variables[i].name, text, strlen(text)))
2706 return xstrdup(variables[i].name);
2714 static char *complete_info(const char *text, int state) {
2718 if(!connect_tincd(false))
2720 // Check the list of nodes
2721 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2722 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2725 while(recvline(fd, line, sizeof line)) {
2727 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
2737 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2741 if(!strncmp(item, text, strlen(text)))
2742 return xstrdup(strip_weight(item));
2748 static char *complete_nothing(const char *text, int state) {
2752 static char **completion (const char *text, int start, int end) {
2753 char **matches = NULL;
2756 matches = rl_completion_matches(text, complete_command);
2757 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2758 matches = rl_completion_matches(text, complete_dump);
2759 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2760 matches = rl_completion_matches(text, complete_config);
2761 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2762 matches = rl_completion_matches(text, complete_config);
2763 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2764 matches = rl_completion_matches(text, complete_config);
2765 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2766 matches = rl_completion_matches(text, complete_config);
2767 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2768 matches = rl_completion_matches(text, complete_info);
2774 static int cmd_shell(int argc, char *argv[]) {
2775 xasprintf(&prompt, "%s> ", identname);
2779 int maxargs = argc + 16;
2780 char **nargv = xmalloc(maxargs * sizeof *nargv);
2782 for(int i = 0; i < argc; i++)
2785 #ifdef HAVE_READLINE
2786 rl_readline_name = "tinc";
2787 rl_completion_entry_function = complete_nothing;
2788 rl_attempted_completion_function = completion;
2789 rl_filename_completion_desired = 0;
2794 #ifdef HAVE_READLINE
2798 rl_basic_word_break_characters = "\t\n ";
2799 line = readline(prompt);
2801 copy = xstrdup(line);
2803 line = fgets(buf, sizeof buf, stdin);
2807 fputs(prompt, stdout);
2809 line = fgets(buf, sizeof buf, stdin);
2815 /* Ignore comments */
2823 char *p = line + strspn(line, " \t\n");
2824 char *next = strtok(p, " \t\n");
2827 if(nargc >= maxargs) {
2829 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2834 next = strtok(NULL, " \t\n");
2840 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
2847 for(int i = 0; commands[i].command; i++) {
2848 if(!strcasecmp(nargv[argc], commands[i].command)) {
2849 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2855 #ifdef HAVE_READLINE
2861 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2874 int main(int argc, char *argv[]) {
2875 program_name = argv[0];
2878 tty = isatty(0) && isatty(1);
2880 if(!parse_options(argc, argv))
2884 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2885 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2898 static struct WSAData wsa_state;
2900 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2901 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2910 return cmd_shell(argc, argv);
2912 for(int i = 0; commands[i].command; i++) {
2913 if(!strcasecmp(argv[optind], commands[i].command))
2914 return commands[i].function(argc - optind, argv + optind);
2917 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);