2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2016 Guus Sliepen <guus@tinc-vpn.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "readline/readline.h"
26 #include "readline/history.h"
31 #include "control_common.h"
36 #include "invitation.h"
45 #define MSG_NOSIGNAL 0
48 static char **orig_argv;
51 /* If nonzero, display usage information and exit. */
52 static bool show_help = false;
54 /* If nonzero, print the version on standard output and exit. */
55 static bool show_version = false;
57 static char *name = NULL;
58 static char controlcookie[1025];
59 char *tinc_conf = NULL;
60 char *hosts_dir = NULL;
63 // Horrible global variables...
72 bool confbasegiven = false;
73 bool netnamegiven = false;
74 char *scriptinterpreter = NULL;
75 char *scriptextension = "";
78 static struct option const long_options[] = {
79 {"batch", no_argument, NULL, 'b'},
80 {"config", required_argument, NULL, 'c'},
81 {"net", required_argument, NULL, 'n'},
82 {"help", no_argument, NULL, 1},
83 {"version", no_argument, NULL, 2},
84 {"pidfile", required_argument, NULL, 3},
85 {"force", no_argument, NULL, 4},
89 static void version(void) {
90 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
91 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
92 printf("Copyright (C) 1998-2016 Ivo Timmermans, Guus Sliepen and others.\n"
93 "See the AUTHORS file for a complete list.\n\n"
94 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
95 "and you are welcome to redistribute it under certain conditions;\n"
96 "see the file COPYING for details.\n");
99 static void usage(bool status) {
101 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
103 printf("Usage: %s [options] command\n\n", program_name);
104 printf("Valid options are:\n"
105 " -b, --batch Don't ask for anything (non-interactive mode).\n"
106 " -c, --config=DIR Read configuration options from DIR.\n"
107 " -n, --net=NETNAME Connect to net NETNAME.\n"
108 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
109 " --force Force some commands to work despite warnings.\n"
110 " --help Display this help and exit.\n"
111 " --version Output version information and exit.\n"
113 "Valid commands are:\n"
114 " init [name] Create initial configuration files.\n"
115 " get VARIABLE Print current value of VARIABLE\n"
116 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
117 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
118 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
119 " start [tincd options] Start tincd.\n"
120 " stop Stop tincd.\n"
121 " restart [tincd options] Restart tincd.\n"
122 " reload Partially reload configuration of running tincd.\n"
123 " pid Show PID of currently running tincd.\n"
124 #ifdef DISABLE_LEGACY
125 " generate-keys Generate a new Ed25519 public/private keypair.\n"
127 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
128 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
130 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
131 " dump Dump a list of one of the following things:\n"
132 " [reachable] nodes - all known nodes in the VPN\n"
133 " edges - all known connections in the VPN\n"
134 " subnets - all known subnets in the VPN\n"
135 " connections - all meta connections with ourself\n"
136 " [di]graph - graph of the VPN in dotty format\n"
137 " invitations - outstanding invitations\n"
138 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
139 " purge Purge unreachable nodes\n"
140 " debug N Set debug level\n"
141 " retry Retry all outgoing connections\n"
142 " disconnect NODE Close meta connection with NODE\n"
144 " top Show real-time statistics\n"
146 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
147 " log [level] Dump log output [up to the specified level]\n"
148 " export Export host configuration of local node to standard output\n"
149 " export-all Export all host configuration files to standard output\n"
150 " import Import host configuration file(s) from standard input\n"
151 " exchange Same as export followed by import\n"
152 " exchange-all Same as export-all followed by import\n"
153 " invite NODE [...] Generate an invitation for NODE\n"
154 " join INVITATION Join a VPN using an INVITATION\n"
155 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
156 " fsck Check the configuration files for problems.\n"
157 " sign [FILE] Generate a signed version of a file.\n"
158 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
160 printf("Report bugs to tinc@tinc-vpn.org.\n");
164 static bool parse_options(int argc, char **argv) {
166 int option_index = 0;
168 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
170 case 0: /* long option */
177 case 'c': /* config file */
178 confbase = xstrdup(optarg);
179 confbasegiven = true;
182 case 'n': /* net name given */
183 netname = xstrdup(optarg);
186 case 1: /* show help */
190 case 2: /* show version */
194 case 3: /* open control socket here */
195 pidfilename = xstrdup(optarg);
202 case '?': /* wrong options */
211 if(!netname && (netname = getenv("NETNAME")))
212 netname = xstrdup(netname);
214 /* netname "." is special: a "top-level name" */
216 if(netname && (!*netname || !strcmp(netname, "."))) {
221 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
222 fprintf(stderr, "Invalid character in netname!\n");
229 /* Open a file with the desired permissions, minus the umask.
230 Also, if we want to create an executable file, we call fchmod()
231 to set the executable bits. */
233 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
234 mode_t mask = umask(0);
237 FILE *f = fopen(filename, mode);
240 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
245 if((perms & 0444) && f)
246 fchmod(fileno(f), perms);
252 static void disable_old_keys(const char *filename, const char *what) {
253 char tmpfile[PATH_MAX] = "";
255 bool disabled = false;
260 r = fopen(filename, "r");
264 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
266 struct stat st = {.st_mode = 0600};
267 fstat(fileno(r), &st);
268 w = fopenmask(tmpfile, "w", st.st_mode);
270 while(fgets(buf, sizeof buf, r)) {
271 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
272 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
278 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
284 if(block || ed25519pubkey)
286 if(fputs(buf, w) < 0) {
292 if(block && !strncmp(buf, "-----END ", 9))
299 if(ferror(r) || fclose(r) < 0)
304 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
311 // We cannot atomically replace files on Windows.
312 char bakfile[PATH_MAX] = "";
313 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
314 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
315 rename(bakfile, filename);
317 if(rename(tmpfile, filename)) {
319 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
324 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
331 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
333 char directory[PATH_MAX] = ".";
337 /* Check stdin and stdout */
339 /* Ask for a file and/or directory name. */
340 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
342 if(fgets(buf, sizeof buf, stdin) == NULL) {
343 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
347 size_t len = strlen(buf);
356 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
358 if(filename[0] != '/') {
360 /* The directory is a relative path or a filename. */
361 getcwd(directory, sizeof directory);
362 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
366 disable_old_keys(filename, what);
368 /* Open it first to keep the inode busy */
370 r = fopenmask(filename, mode, perms);
373 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
381 Generate a public/private Ed25519 keypair, and ask for a file to store
384 static bool ed25519_keygen(bool ask) {
387 char fname[PATH_MAX];
389 fprintf(stderr, "Generating Ed25519 keypair:\n");
391 if(!(key = ecdsa_generate())) {
392 fprintf(stderr, "Error during key generation!\n");
395 fprintf(stderr, "Done.\n");
397 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
398 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
403 if(!ecdsa_write_pem_private_key(key, f)) {
404 fprintf(stderr, "Error writing private key!\n");
411 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
413 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.pub", confbase);
415 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
420 char *pubkey = ecdsa_get_base64_public_key(key);
421 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
436 #ifndef DISABLE_LEGACY
438 Generate a public/private RSA keypair, and ask for a file to store
441 static bool rsa_keygen(int bits, bool ask) {
444 char fname[PATH_MAX];
446 // Make sure the key size is a multiple of 8 bits.
449 // Make sure that a valid key size is used.
450 if(bits < 1024 || bits > 8192) {
451 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
453 } else if(bits < 2048) {
454 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
457 fprintf(stderr, "Generating %d bits keys:\n", bits);
459 if(!(key = rsa_generate(bits, 0x10001))) {
460 fprintf(stderr, "Error during key generation!\n");
463 fprintf(stderr, "Done.\n");
465 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.priv", confbase);
466 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
471 if(!rsa_write_pem_private_key(key, f)) {
472 fprintf(stderr, "Error writing private key!\n");
479 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
481 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.pub", confbase);
483 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
488 if(!rsa_write_pem_public_key(key, f)) {
489 fprintf(stderr, "Error writing public key!\n");
509 bool recvline(int fd, char *line, size_t len) {
510 char *newline = NULL;
515 while(!(newline = memchr(buffer, '\n', blen))) {
516 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
517 if(result == -1 && sockerrno == EINTR)
524 if(newline - buffer >= len)
527 len = newline - buffer;
529 memcpy(line, buffer, len);
531 memmove(buffer, newline + 1, blen - len - 1);
537 bool recvdata(int fd, char *data, size_t len) {
542 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
543 if(result == -1 && sockerrno == EINTR)
550 memcpy(data, buffer, len);
551 memmove(buffer, buffer + len, blen - len);
557 bool sendline(int fd, char *format, ...) {
558 static char buffer[4096];
563 va_start(ap, format);
564 blen = vsnprintf(buffer, sizeof buffer, format, ap);
565 buffer[sizeof buffer - 1] = 0;
568 if(blen < 1 || blen >= sizeof buffer)
575 int result = send(fd, p, blen, MSG_NOSIGNAL);
576 if(result == -1 && sockerrno == EINTR)
587 static void pcap(int fd, FILE *out, int snaplen) {
588 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
596 uint32_t tz_accuracy;
603 snaplen ?: sizeof data,
616 fwrite(&header, sizeof header, 1, out);
620 while(recvline(fd, line, sizeof line)) {
622 int n = sscanf(line, "%d %d %d", &code, &req, &len);
623 gettimeofday(&tv, NULL);
624 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
626 if(!recvdata(fd, data, len))
628 packet.tv_sec = tv.tv_sec;
629 packet.tv_usec = tv.tv_usec;
631 packet.origlen = len;
632 fwrite(&packet, sizeof packet, 1, out);
633 fwrite(data, len, 1, out);
638 static void logcontrol(int fd, FILE *out, int level) {
639 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
643 while(recvline(fd, line, sizeof line)) {
645 int n = sscanf(line, "%d %d %d", &code, &req, &len);
646 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
648 if(!recvdata(fd, data, len))
650 fwrite(data, len, 1, out);
657 static bool remove_service(void) {
658 SC_HANDLE manager = NULL;
659 SC_HANDLE service = NULL;
660 SERVICE_STATUS status = {0};
662 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
664 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
668 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
671 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
675 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
676 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
678 fprintf(stderr, "%s service stopped\n", identname);
680 if(!DeleteService(service)) {
681 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
685 fprintf(stderr, "%s service removed\n", identname);
691 bool connect_tincd(bool verbose) {
696 struct timeval tv = {0, 0};
697 if(select(fd + 1, &r, NULL, NULL, &tv)) {
698 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
706 FILE *f = fopen(pidfilename, "r");
709 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
716 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
718 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
724 if ((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
725 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
726 /* clean up the stale socket and pid file */
728 unlink(unixsocketname);
733 struct sockaddr_un sa;
734 sa.sun_family = AF_UNIX;
735 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
737 fd = socket(AF_UNIX, SOCK_STREAM, 0);
740 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
744 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
746 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
752 struct addrinfo hints = {
753 .ai_family = AF_UNSPEC,
754 .ai_socktype = SOCK_STREAM,
755 .ai_protocol = IPPROTO_TCP,
759 struct addrinfo *res = NULL;
761 if(getaddrinfo(host, port, &hints, &res) || !res) {
763 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
767 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
770 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
775 unsigned long arg = 0;
777 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
779 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
783 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
785 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
795 static const int one = 1;
796 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
802 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
804 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
810 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
812 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
814 fprintf(stderr, "Could not fully establish control socket connection\n");
824 static int cmd_start(int argc, char *argv[]) {
825 if(connect_tincd(false)) {
827 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
829 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
834 char *slash = strrchr(program_name, '/');
837 if ((c = strrchr(program_name, '\\')) > slash)
842 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
847 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
852 Windows has no real concept of an "argv array". A command line is just one string.
853 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
854 it uses quotes to handle spaces in arguments.
855 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
856 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
857 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
859 xasprintf(&arg0, "\"%s\"", arg0);
861 nargv[nargc++] = arg0;
862 for(int i = 1; i < optind; i++)
863 nargv[nargc++] = orig_argv[i];
864 for(int i = 1; i < argc; i++)
865 nargv[nargc++] = argv[i];
868 int status = spawnvp(_P_WAIT, c, nargv);
870 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
875 int pfd[2] = {-1, -1};
876 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
877 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
884 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
892 snprintf(buf, sizeof buf, "%d", pfd[1]);
893 setenv("TINC_UMBILICAL", buf, true);
894 exit(execvp(c, nargv));
901 int status = -1, result;
903 signal(SIGINT, SIG_IGN);
906 // Pass all log messages from the umbilical to stderr.
907 // A nul-byte right before closure means tincd started succesfully.
912 while((len = read(pfd[0], buf, sizeof buf)) > 0) {
913 failure = buf[len - 1];
924 // Make sure the child process is really gone.
925 result = waitpid(pid, &status, 0);
928 signal(SIGINT, SIG_DFL);
931 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
932 fprintf(stderr, "Error starting %s\n", c);
940 static int cmd_stop(int argc, char *argv[]) {
942 fprintf(stderr, "Too many arguments!\n");
947 if(!connect_tincd(true)) {
949 if(kill(pid, SIGTERM)) {
950 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
954 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
955 waitpid(pid, NULL, 0);
962 sendline(fd, "%d %d", CONTROL, REQ_STOP);
964 while(recvline(fd, line, sizeof line)) {
965 // Wait for tincd to close the connection...
968 if(!remove_service())
978 static int cmd_restart(int argc, char *argv[]) {
980 return cmd_start(argc, argv);
983 static int cmd_reload(int argc, char *argv[]) {
985 fprintf(stderr, "Too many arguments!\n");
989 if(!connect_tincd(true))
992 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
993 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
994 fprintf(stderr, "Could not reload configuration.\n");
1002 static int dump_invitations(void) {
1003 char dname[PATH_MAX];
1004 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
1005 DIR *dir = opendir(dname);
1007 if(errno == ENOENT) {
1008 fprintf(stderr, "No outstanding invitations.\n");
1012 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1019 while((ent = readdir(dir))) {
1020 char buf[MAX_STRING_SIZE];
1021 if(b64decode(ent->d_name, buf, 24) != 18)
1024 char fname[PATH_MAX];
1025 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
1026 FILE *f = fopen(fname, "r");
1028 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1034 if(!fgets(buf, sizeof buf, f)) {
1035 fprintf(stderr, "Invalid invitation file %s", fname);
1041 char *eol = buf + strlen(buf);
1042 while(strchr("\t \r\n", *--eol))
1044 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1045 fprintf(stderr, "Invalid invitation file %s", fname);
1050 printf("%s %s\n", ent->d_name, buf + 7);
1056 fprintf(stderr, "No outstanding invitations.\n");
1061 static int cmd_dump(int argc, char *argv[]) {
1062 bool only_reachable = false;
1064 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1065 if(strcasecmp(argv[2], "nodes")) {
1066 fprintf(stderr, "`reachable' only supported for nodes.\n");
1070 only_reachable = true;
1076 fprintf(stderr, "Invalid number of arguments.\n");
1081 if(!strcasecmp(argv[1], "invitations"))
1082 return dump_invitations();
1084 if(!connect_tincd(true))
1089 if(!strcasecmp(argv[1], "nodes"))
1090 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1091 else if(!strcasecmp(argv[1], "edges"))
1092 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1093 else if(!strcasecmp(argv[1], "subnets"))
1094 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1095 else if(!strcasecmp(argv[1], "connections"))
1096 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1097 else if(!strcasecmp(argv[1], "graph")) {
1098 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1099 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1101 } else if(!strcasecmp(argv[1], "digraph")) {
1102 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1103 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1106 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1112 printf("graph {\n");
1113 else if(do_graph == 2)
1114 printf("digraph {\n");
1116 while(recvline(fd, line, sizeof line)) {
1117 char node1[4096], node2[4096];
1118 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1120 if(do_graph && req == REQ_DUMP_NODES)
1138 char local_host[4096];
1139 char local_port[4096];
1142 int cipher, digest, maclength, compression, distance, socket, weight;
1143 short int pmtu, minmtu, maxmtu;
1144 unsigned int options, status_int;
1145 node_status_t status;
1146 long int last_state_change;
1149 case REQ_DUMP_NODES: {
1150 int n = sscanf(line, "%*d %*d %s %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
1152 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1156 memcpy(&status, &status_int, sizeof status);
1159 const char *color = "black";
1160 if(!strcmp(host, "MYSELF"))
1162 else if(!status.reachable)
1164 else if(strcmp(via, node))
1166 else if(!status.validkey)
1170 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1172 if(only_reachable && !status.reachable)
1174 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",
1175 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1179 case REQ_DUMP_EDGES: {
1180 int n = sscanf(line, "%*d %*d %s %s %s port %s %s port %s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
1182 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1187 float w = 1 + 65536.0 / weight;
1188 if(do_graph == 1 && strcmp(node1, node2) > 0)
1189 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1190 else if(do_graph == 2)
1191 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1193 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);
1197 case REQ_DUMP_SUBNETS: {
1198 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1200 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1203 printf("%s owner %s\n", strip_weight(subnet), node);
1206 case REQ_DUMP_CONNECTIONS: {
1207 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1209 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1212 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1216 fprintf(stderr, "Unable to parse dump from tincd.\n");
1221 fprintf(stderr, "Error receiving dump.\n");
1225 static int cmd_purge(int argc, char *argv[]) {
1227 fprintf(stderr, "Too many arguments!\n");
1231 if(!connect_tincd(true))
1234 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1235 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1236 fprintf(stderr, "Could not purge old information.\n");
1243 static int cmd_debug(int argc, char *argv[]) {
1245 fprintf(stderr, "Invalid number of arguments.\n");
1249 if(!connect_tincd(true))
1252 int debuglevel = atoi(argv[1]);
1255 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1256 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1257 fprintf(stderr, "Could not set debug level.\n");
1261 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1265 static int cmd_retry(int argc, char *argv[]) {
1267 fprintf(stderr, "Too many arguments!\n");
1271 if(!connect_tincd(true))
1274 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1275 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1276 fprintf(stderr, "Could not retry outgoing connections.\n");
1283 static int cmd_connect(int argc, char *argv[]) {
1285 fprintf(stderr, "Invalid number of arguments.\n");
1289 if(!check_id(argv[1])) {
1290 fprintf(stderr, "Invalid name for node.\n");
1294 if(!connect_tincd(true))
1297 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1298 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1299 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1306 static int cmd_disconnect(int argc, char *argv[]) {
1308 fprintf(stderr, "Invalid number of arguments.\n");
1312 if(!check_id(argv[1])) {
1313 fprintf(stderr, "Invalid name for node.\n");
1317 if(!connect_tincd(true))
1320 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1321 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1322 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1329 static int cmd_top(int argc, char *argv[]) {
1331 fprintf(stderr, "Too many arguments!\n");
1336 if(!connect_tincd(true))
1342 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1347 static int cmd_pcap(int argc, char *argv[]) {
1349 fprintf(stderr, "Too many arguments!\n");
1353 if(!connect_tincd(true))
1356 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1361 static void sigint_handler(int sig) {
1362 fprintf(stderr, "\n");
1363 shutdown(fd, SHUT_RDWR);
1367 static int cmd_log(int argc, char *argv[]) {
1369 fprintf(stderr, "Too many arguments!\n");
1373 if(!connect_tincd(true))
1377 signal(SIGINT, sigint_handler);
1380 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1383 signal(SIGINT, SIG_DFL);
1391 static int cmd_pid(int argc, char *argv[]) {
1393 fprintf(stderr, "Too many arguments!\n");
1397 if(!connect_tincd(true) || !pid)
1400 printf("%d\n", pid);
1404 int rstrip(char *value) {
1405 int len = strlen(value);
1406 while(len && strchr("\t\r\n ", value[len - 1]))
1411 char *get_my_name(bool verbose) {
1412 FILE *f = fopen(tinc_conf, "r");
1415 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1421 while(fgets(buf, sizeof buf, f)) {
1422 int len = strcspn(buf, "\t =");
1424 value += strspn(value, "\t ");
1427 value += strspn(value, "\t ");
1432 if(strcasecmp(buf, "Name"))
1436 return replace_name(value);
1442 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1446 ecdsa_t *get_pubkey(FILE *f) {
1449 while(fgets(buf, sizeof buf, f)) {
1450 int len = strcspn(buf, "\t =");
1452 value += strspn(value, "\t ");
1455 value += strspn(value, "\t ");
1460 if(strcasecmp(buf, "Ed25519PublicKey"))
1463 return ecdsa_set_base64_public_key(value);
1469 const var_t variables[] = {
1470 /* Server configuration */
1471 {"AddressFamily", VAR_SERVER},
1472 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1473 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1474 {"BindToInterface", VAR_SERVER},
1475 {"Broadcast", VAR_SERVER | VAR_SAFE},
1476 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1477 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1478 {"DecrementTTL", VAR_SERVER},
1479 {"Device", VAR_SERVER},
1480 {"DeviceStandby", VAR_SERVER},
1481 {"DeviceType", VAR_SERVER},
1482 {"DirectOnly", VAR_SERVER},
1483 {"Ed25519PrivateKeyFile", VAR_SERVER},
1484 {"ExperimentalProtocol", VAR_SERVER},
1485 {"Forwarding", VAR_SERVER},
1486 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1487 {"Hostnames", VAR_SERVER},
1488 {"IffOneQueue", VAR_SERVER},
1489 {"Interface", VAR_SERVER},
1490 {"KeyExpire", VAR_SERVER},
1491 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1492 {"LocalDiscovery", VAR_SERVER},
1493 {"MACExpire", VAR_SERVER},
1494 {"MaxConnectionBurst", VAR_SERVER},
1495 {"MaxOutputBufferSize", VAR_SERVER},
1496 {"MaxTimeout", VAR_SERVER},
1497 {"Mode", VAR_SERVER | VAR_SAFE},
1498 {"Name", VAR_SERVER},
1499 {"PingInterval", VAR_SERVER},
1500 {"PingTimeout", VAR_SERVER},
1501 {"PriorityInheritance", VAR_SERVER},
1502 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1503 {"PrivateKeyFile", VAR_SERVER},
1504 {"ProcessPriority", VAR_SERVER},
1505 {"Proxy", VAR_SERVER},
1506 {"ReplayWindow", VAR_SERVER},
1507 {"ScriptsExtension", VAR_SERVER},
1508 {"ScriptsInterpreter", VAR_SERVER},
1509 {"StrictSubnets", VAR_SERVER},
1510 {"TunnelServer", VAR_SERVER},
1511 {"UDPDiscovery", VAR_SERVER},
1512 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1513 {"UDPDiscoveryInterval", VAR_SERVER},
1514 {"UDPDiscoveryTimeout", VAR_SERVER},
1515 {"MTUInfoInterval", VAR_SERVER},
1516 {"UDPInfoInterval", VAR_SERVER},
1517 {"UDPRcvBuf", VAR_SERVER},
1518 {"UDPSndBuf", VAR_SERVER},
1519 {"UPnP", VAR_SERVER},
1520 {"UPnPDiscoverWait", VAR_SERVER},
1521 {"UPnPRefreshPeriod", VAR_SERVER},
1522 {"VDEGroup", VAR_SERVER},
1523 {"VDEPort", VAR_SERVER},
1524 /* Host configuration */
1525 {"Address", VAR_HOST | VAR_MULTIPLE},
1526 {"Cipher", VAR_SERVER | VAR_HOST},
1527 {"ClampMSS", VAR_SERVER | VAR_HOST},
1528 {"Compression", VAR_SERVER | VAR_HOST},
1529 {"Digest", VAR_SERVER | VAR_HOST},
1530 {"Ed25519PublicKey", VAR_HOST},
1531 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1532 {"IndirectData", VAR_SERVER | VAR_HOST},
1533 {"MACLength", VAR_SERVER | VAR_HOST},
1534 {"PMTU", VAR_SERVER | VAR_HOST},
1535 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1537 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1538 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1539 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1540 {"TCPOnly", VAR_SERVER | VAR_HOST},
1541 {"Weight", VAR_HOST | VAR_SAFE},
1545 static int cmd_config(int argc, char *argv[]) {
1547 fprintf(stderr, "Invalid number of arguments.\n");
1551 if(strcasecmp(argv[0], "config"))
1555 if(!strcasecmp(argv[1], "get")) {
1557 } else if(!strcasecmp(argv[1], "add")) {
1558 argv++, argc--, action = 1;
1559 } else if(!strcasecmp(argv[1], "del")) {
1560 argv++, argc--, action = -1;
1561 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1562 argv++, argc--, action = 0;
1566 fprintf(stderr, "Invalid number of arguments.\n");
1570 // Concatenate the rest of the command line
1571 strncpy(line, argv[1], sizeof line - 1);
1572 for(int i = 2; i < argc; i++) {
1573 strncat(line, " ", sizeof line - 1 - strlen(line));
1574 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1577 // Liberal parsing into node name, variable name and value.
1583 len = strcspn(line, "\t =");
1585 value += strspn(value, "\t ");
1588 value += strspn(value, "\t ");
1591 variable = strchr(line, '.');
1600 fprintf(stderr, "No variable given.\n");
1604 if(action >= 0 && !*value) {
1605 fprintf(stderr, "No value for variable given.\n");
1609 if(action < -1 && *value)
1612 /* Some simple checks. */
1614 bool warnonremove = false;
1616 for(int i = 0; variables[i].name; i++) {
1617 if(strcasecmp(variables[i].name, variable))
1621 variable = (char *)variables[i].name;
1623 /* Discourage use of obsolete variables. */
1625 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1627 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1629 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1634 /* Don't put server variables in host config files */
1636 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1638 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1640 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1645 /* Should this go into our own host config file? */
1647 if(!node && !(variables[i].type & VAR_SERVER)) {
1648 node = get_my_name(true);
1653 /* Change "add" into "set" for variables that do not allow multiple occurences.
1654 Turn on warnings when it seems variables might be removed unintentionally. */
1656 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1657 warnonremove = true;
1659 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1660 warnonremove = true;
1666 if(node && !check_id(node)) {
1667 fprintf(stderr, "Invalid name for node.\n");
1672 if(force || action < 0) {
1673 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1675 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1680 // Open the right configuration file.
1681 char filename[PATH_MAX];
1683 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1685 snprintf(filename, sizeof filename, "%s", tinc_conf);
1687 FILE *f = fopen(filename, "r");
1689 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1693 char tmpfile[PATH_MAX];
1697 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1698 tf = fopen(tmpfile, "w");
1700 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1706 // Copy the file, making modifications on the fly, unless we are just getting a value.
1710 bool removed = false;
1713 while(fgets(buf1, sizeof buf1, f)) {
1714 buf1[sizeof buf1 - 1] = 0;
1715 strncpy(buf2, buf1, sizeof buf2);
1717 // Parse line in a simple way
1721 len = strcspn(buf2, "\t =");
1722 bvalue = buf2 + len;
1723 bvalue += strspn(bvalue, "\t ");
1724 if(*bvalue == '=') {
1726 bvalue += strspn(bvalue, "\t ");
1732 if(!strcasecmp(buf2, variable)) {
1736 printf("%s\n", bvalue);
1738 } else if(action == -1) {
1739 if(!*value || !strcasecmp(bvalue, value)) {
1744 } else if(action == 0) {
1745 // Warn if "set" was used for variables that can occur multiple times
1746 if(warnonremove && strcasecmp(bvalue, value))
1747 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1749 // Already set? Delete the rest...
1753 // Otherwise, replace.
1754 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1755 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1761 } else if(action > 0) {
1762 // Check if we've already seen this variable with the same value
1763 if(!strcasecmp(bvalue, value))
1769 // Copy original line...
1770 if(fputs(buf1, tf) < 0) {
1771 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1775 // Add newline if it is missing...
1776 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1777 if(fputc('\n', tf) < 0) {
1778 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1785 // Make sure we read everything...
1786 if(ferror(f) || !feof(f)) {
1787 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1792 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1796 // Add new variable if necessary.
1797 if((action > 0 && !found)|| (action == 0 && !set)) {
1798 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1799 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1808 fprintf(stderr, "No matching configuration variables found.\n");
1813 // Make sure we wrote everything...
1815 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1819 // Could we find what we had to remove?
1820 if(action < 0 && !removed) {
1822 fprintf(stderr, "No configuration variables deleted.\n");
1826 // Replace the configuration file with the new one
1828 if(remove(filename)) {
1829 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1833 if(rename(tmpfile, filename)) {
1834 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1838 // Silently try notifying a running tincd of changes.
1839 if(connect_tincd(false))
1840 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1845 static bool try_bind(int port) {
1846 struct addrinfo *ai = NULL, *aip;
1847 struct addrinfo hint = {
1848 .ai_flags = AI_PASSIVE,
1849 .ai_family = AF_UNSPEC,
1850 .ai_socktype = SOCK_STREAM,
1851 .ai_protocol = IPPROTO_TCP,
1854 bool success = true;
1856 snprintf(portstr, sizeof portstr, "%d", port);
1858 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1861 for(aip = ai; aip; aip = aip->ai_next) {
1862 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1868 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1880 int check_port(char *name) {
1884 fprintf(stderr, "Warning: could not bind to port 655. ");
1886 for(int i = 0; i < 100; i++) {
1887 int port = 0x1000 + (rand() & 0x7fff);
1888 if(try_bind(port)) {
1889 char filename[PATH_MAX];
1890 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1891 FILE *f = fopen(filename, "a");
1893 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1894 fprintf(stderr, "Please change tinc's Port manually.\n");
1898 fprintf(f, "Port = %d\n", port);
1900 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1905 fprintf(stderr, "Please change tinc's Port manually.\n");
1909 static int cmd_init(int argc, char *argv[]) {
1910 if(!access(tinc_conf, F_OK)) {
1911 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1916 fprintf(stderr, "Too many arguments!\n");
1918 } else if(argc < 2) {
1921 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1922 if(!fgets(buf, sizeof buf, stdin)) {
1923 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1926 int len = rstrip(buf);
1928 fprintf(stderr, "No name given!\n");
1933 fprintf(stderr, "No Name given!\n");
1937 name = strdup(argv[1]);
1939 fprintf(stderr, "No Name given!\n");
1944 if(!check_id(name)) {
1945 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1949 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1950 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1954 if(mkdir(confbase, 0777) && errno != EEXIST) {
1955 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1959 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1960 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1964 FILE *f = fopen(tinc_conf, "w");
1966 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1970 fprintf(f, "Name = %s\n", name);
1973 #ifndef DISABLE_LEGACY
1974 if(!rsa_keygen(2048, false))
1978 if(!ed25519_keygen(false))
1984 char filename[PATH_MAX];
1985 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1986 if(access(filename, F_OK)) {
1987 FILE *f = fopenmask(filename, "w", 0777);
1989 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1992 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");
2001 static int cmd_generate_keys(int argc, char *argv[]) {
2002 #ifdef DISABLE_LEGACY
2007 fprintf(stderr, "Too many arguments!\n");
2012 name = get_my_name(false);
2014 #ifndef DISABLE_LEGACY
2015 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
2019 if(!ed25519_keygen(true))
2025 #ifndef DISABLE_LEGACY
2026 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2028 fprintf(stderr, "Too many arguments!\n");
2033 name = get_my_name(false);
2035 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2039 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2041 fprintf(stderr, "Too many arguments!\n");
2046 name = get_my_name(false);
2048 return !ed25519_keygen(true);
2051 static int cmd_help(int argc, char *argv[]) {
2056 static int cmd_version(int argc, char *argv[]) {
2058 fprintf(stderr, "Too many arguments!\n");
2066 static int cmd_info(int argc, char *argv[]) {
2068 fprintf(stderr, "Invalid number of arguments.\n");
2072 if(!connect_tincd(true))
2075 return info(fd, argv[1]);
2078 static const char *conffiles[] = {
2089 static int cmd_edit(int argc, char *argv[]) {
2091 fprintf(stderr, "Invalid number of arguments.\n");
2095 char filename[PATH_MAX] = "";
2097 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2098 for(int i = 0; conffiles[i]; i++) {
2099 if(!strcmp(argv[1], conffiles[i])) {
2100 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2109 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2110 char *dash = strchr(argv[1], '-');
2113 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2114 fprintf(stderr, "Invalid configuration filename.\n");
2122 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2124 xasprintf(&command, "edit \"%s\"", filename);
2126 int result = system(command);
2131 // Silently try notifying a running tincd of changes.
2132 if(connect_tincd(false))
2133 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2138 static int export(const char *name, FILE *out) {
2139 char filename[PATH_MAX];
2140 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2141 FILE *in = fopen(filename, "r");
2143 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2147 fprintf(out, "Name = %s\n", name);
2149 while(fgets(buf, sizeof buf, in)) {
2150 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2155 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2164 static int cmd_export(int argc, char *argv[]) {
2166 fprintf(stderr, "Too many arguments!\n");
2170 char *name = get_my_name(true);
2174 int result = export(name, stdout);
2182 static int cmd_export_all(int argc, char *argv[]) {
2184 fprintf(stderr, "Too many arguments!\n");
2188 DIR *dir = opendir(hosts_dir);
2190 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2198 while((ent = readdir(dir))) {
2199 if(!check_id(ent->d_name))
2205 printf("#---------------------------------------------------------------#\n");
2207 result |= export(ent->d_name, stdout);
2216 static int cmd_import(int argc, char *argv[]) {
2218 fprintf(stderr, "Too many arguments!\n");
2227 char filename[PATH_MAX] = "";
2229 bool firstline = true;
2231 while(fgets(buf, sizeof buf, in)) {
2232 if(sscanf(buf, "Name = %s", name) == 1) {
2235 if(!check_id(name)) {
2236 fprintf(stderr, "Invalid Name in input!\n");
2243 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2245 if(!force && !access(filename, F_OK)) {
2246 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2251 out = fopen(filename, "w");
2253 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2259 } else if(firstline) {
2260 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2265 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2269 if(fputs(buf, out) < 0) {
2270 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2280 fprintf(stderr, "Imported %d host configuration files.\n", count);
2283 fprintf(stderr, "No host configuration files imported.\n");
2288 static int cmd_exchange(int argc, char *argv[]) {
2289 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2292 static int cmd_exchange_all(int argc, char *argv[]) {
2293 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2296 static int switch_network(char *name) {
2297 if(strcmp(name, ".")) {
2298 if(!check_netname(name, false)) {
2299 fprintf(stderr, "Invalid character in netname!\n");
2303 if(!check_netname(name, true))
2304 fprintf(stderr, "Warning: unsafe character in netname!\n");
2313 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2320 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2321 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2322 xasprintf(&prompt, "%s> ", identname);
2327 static int cmd_network(int argc, char *argv[]) {
2329 fprintf(stderr, "Too many arguments!\n");
2334 return switch_network(argv[1]);
2336 DIR *dir = opendir(confdir);
2338 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2343 while((ent = readdir(dir))) {
2344 if(*ent->d_name == '.')
2347 if(!strcmp(ent->d_name, "tinc.conf")) {
2352 char fname[PATH_MAX];
2353 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2354 if(!access(fname, R_OK))
2355 printf("%s\n", ent->d_name);
2363 static int cmd_fsck(int argc, char *argv[]) {
2365 fprintf(stderr, "Too many arguments!\n");
2369 return fsck(orig_argv[0]);
2372 static void *readfile(FILE *in, size_t *len) {
2374 size_t alloced = 4096;
2375 char *buf = xmalloc(alloced);
2378 size_t read = fread(buf + count, 1, alloced - count, in);
2382 if(count >= alloced) {
2384 buf = xrealloc(buf, alloced);
2394 static int cmd_sign(int argc, char *argv[]) {
2396 fprintf(stderr, "Too many arguments!\n");
2401 name = get_my_name(true);
2406 char fname[PATH_MAX];
2407 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
2408 FILE *fp = fopen(fname, "r");
2410 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2414 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2417 fprintf(stderr, "Could not read private key from %s\n", fname);
2427 in = fopen(argv[1], "rb");
2429 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2438 char *data = readfile(in, &len);
2442 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2447 // Ensure we sign our name and current time as well
2448 long t = time(NULL);
2450 xasprintf(&trailer, " %s %ld", name, t);
2451 int trailer_len = strlen(trailer);
2453 data = xrealloc(data, len + trailer_len);
2454 memcpy(data + len, trailer, trailer_len);
2458 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2459 fprintf(stderr, "Error generating signature\n");
2464 b64encode(sig, sig, 64);
2467 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2468 fwrite(data, len, 1, stdout);
2474 static int cmd_verify(int argc, char *argv[]) {
2476 fprintf(stderr, "Not enough arguments!\n");
2481 fprintf(stderr, "Too many arguments!\n");
2485 char *node = argv[1];
2486 if(!strcmp(node, ".")) {
2488 name = get_my_name(true);
2493 } else if(!strcmp(node, "*")) {
2496 if(!check_id(node)) {
2497 fprintf(stderr, "Invalid node name\n");
2505 in = fopen(argv[2], "rb");
2507 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2515 char *data = readfile(in, &len);
2519 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2523 char *newline = memchr(data, '\n', len);
2524 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2525 fprintf(stderr, "Invalid input\n");
2531 size_t skip = newline - data;
2533 char signer[MAX_STRING_SIZE] = "";
2534 char sig[MAX_STRING_SIZE] = "";
2537 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2538 fprintf(stderr, "Invalid input\n");
2543 if(node && strcmp(node, signer)) {
2544 fprintf(stderr, "Signature is not made by %s\n", node);
2553 xasprintf(&trailer, " %s %ld", signer, t);
2554 int trailer_len = strlen(trailer);
2556 data = xrealloc(data, len + trailer_len);
2557 memcpy(data + len, trailer, trailer_len);
2560 newline = data + skip;
2562 char fname[PATH_MAX];
2563 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, node);
2564 FILE *fp = fopen(fname, "r");
2566 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2571 ecdsa_t *key = get_pubkey(fp);
2574 key = ecdsa_read_pem_public_key(fp);
2577 fprintf(stderr, "Could not read public key from %s\n", fname);
2585 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2586 fprintf(stderr, "Invalid signature\n");
2594 fwrite(newline, len - (newline - data), 1, stdout);
2600 static const struct {
2601 const char *command;
2602 int (*function)(int argc, char *argv[]);
2605 {"start", cmd_start},
2607 {"restart", cmd_restart},
2608 {"reload", cmd_reload},
2611 {"purge", cmd_purge},
2612 {"debug", cmd_debug},
2613 {"retry", cmd_retry},
2614 {"connect", cmd_connect},
2615 {"disconnect", cmd_disconnect},
2620 {"config", cmd_config, true},
2621 {"add", cmd_config},
2622 {"del", cmd_config},
2623 {"get", cmd_config},
2624 {"set", cmd_config},
2626 {"generate-keys", cmd_generate_keys},
2627 #ifndef DISABLE_LEGACY
2628 {"generate-rsa-keys", cmd_generate_rsa_keys},
2630 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2632 {"version", cmd_version},
2635 {"export", cmd_export},
2636 {"export-all", cmd_export_all},
2637 {"import", cmd_import},
2638 {"exchange", cmd_exchange},
2639 {"exchange-all", cmd_exchange_all},
2640 {"invite", cmd_invite},
2642 {"network", cmd_network},
2645 {"verify", cmd_verify},
2649 #ifdef HAVE_READLINE
2650 static char *complete_command(const char *text, int state) {
2658 while(commands[i].command) {
2659 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2660 return xstrdup(commands[i].command);
2667 static char *complete_dump(const char *text, int state) {
2668 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2677 if(!strncasecmp(matches[i], text, strlen(text)))
2678 return xstrdup(matches[i]);
2685 static char *complete_config(const char *text, int state) {
2693 while(variables[i].name) {
2694 char *dot = strchr(text, '.');
2696 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2698 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2702 if(!strncasecmp(variables[i].name, text, strlen(text)))
2703 return xstrdup(variables[i].name);
2711 static char *complete_info(const char *text, int state) {
2715 if(!connect_tincd(false))
2717 // Check the list of nodes
2718 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2719 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2722 while(recvline(fd, line, sizeof line)) {
2724 int n = sscanf(line, "%d %d %s", &code, &req, item);
2734 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2738 if(!strncmp(item, text, strlen(text)))
2739 return xstrdup(strip_weight(item));
2745 static char *complete_nothing(const char *text, int state) {
2749 static char **completion (const char *text, int start, int end) {
2750 char **matches = NULL;
2753 matches = rl_completion_matches(text, complete_command);
2754 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2755 matches = rl_completion_matches(text, complete_dump);
2756 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2757 matches = rl_completion_matches(text, complete_config);
2758 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2759 matches = rl_completion_matches(text, complete_config);
2760 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2761 matches = rl_completion_matches(text, complete_config);
2762 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2763 matches = rl_completion_matches(text, complete_config);
2764 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2765 matches = rl_completion_matches(text, complete_info);
2771 static int cmd_shell(int argc, char *argv[]) {
2772 xasprintf(&prompt, "%s> ", identname);
2776 int maxargs = argc + 16;
2777 char **nargv = xmalloc(maxargs * sizeof *nargv);
2779 for(int i = 0; i < argc; i++)
2782 #ifdef HAVE_READLINE
2783 rl_readline_name = "tinc";
2784 rl_completion_entry_function = complete_nothing;
2785 rl_attempted_completion_function = completion;
2786 rl_filename_completion_desired = 0;
2791 #ifdef HAVE_READLINE
2795 rl_basic_word_break_characters = "\t\n ";
2796 line = readline(prompt);
2798 copy = xstrdup(line);
2800 line = fgets(buf, sizeof buf, stdin);
2804 fputs(prompt, stdout);
2806 line = fgets(buf, sizeof buf, stdin);
2812 /* Ignore comments */
2820 char *p = line + strspn(line, " \t\n");
2821 char *next = strtok(p, " \t\n");
2824 if(nargc >= maxargs) {
2825 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2828 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2833 next = strtok(NULL, " \t\n");
2839 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
2846 for(int i = 0; commands[i].command; i++) {
2847 if(!strcasecmp(nargv[argc], commands[i].command)) {
2848 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2854 #ifdef HAVE_READLINE
2860 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2873 int main(int argc, char *argv[]) {
2874 program_name = argv[0];
2877 tty = isatty(0) && isatty(1);
2879 if(!parse_options(argc, argv))
2883 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2884 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2897 static struct WSAData wsa_state;
2899 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2900 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2909 return cmd_shell(argc, argv);
2911 for(int i = 0; commands[i].command; i++) {
2912 if(!strcasecmp(argv[optind], commands[i].command))
2913 return commands[i].function(argc - optind, argv + optind);
2916 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);