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 // Force them to be between 1024 and 8192 bits long.
455 fprintf(stderr, "Generating %d bits keys:\n", bits);
457 if(!(key = rsa_generate(bits, 0x10001))) {
458 fprintf(stderr, "Error during key generation!\n");
461 fprintf(stderr, "Done.\n");
463 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.priv", confbase);
464 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
469 if(!rsa_write_pem_private_key(key, f)) {
470 fprintf(stderr, "Error writing private key!\n");
477 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
479 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.pub", confbase);
481 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
486 if(!rsa_write_pem_public_key(key, f)) {
487 fprintf(stderr, "Error writing public key!\n");
507 bool recvline(int fd, char *line, size_t len) {
508 char *newline = NULL;
513 while(!(newline = memchr(buffer, '\n', blen))) {
514 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
515 if(result == -1 && sockerrno == EINTR)
522 if(newline - buffer >= len)
525 len = newline - buffer;
527 memcpy(line, buffer, len);
529 memmove(buffer, newline + 1, blen - len - 1);
535 bool recvdata(int fd, char *data, size_t len) {
540 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
541 if(result == -1 && sockerrno == EINTR)
548 memcpy(data, buffer, len);
549 memmove(buffer, buffer + len, blen - len);
555 bool sendline(int fd, char *format, ...) {
556 static char buffer[4096];
561 va_start(ap, format);
562 blen = vsnprintf(buffer, sizeof buffer, format, ap);
563 buffer[sizeof buffer - 1] = 0;
566 if(blen < 1 || blen >= sizeof buffer)
573 int result = send(fd, p, blen, MSG_NOSIGNAL);
574 if(result == -1 && sockerrno == EINTR)
585 static void pcap(int fd, FILE *out, int snaplen) {
586 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
594 uint32_t tz_accuracy;
601 snaplen ?: sizeof data,
614 fwrite(&header, sizeof header, 1, out);
618 while(recvline(fd, line, sizeof line)) {
620 int n = sscanf(line, "%d %d %d", &code, &req, &len);
621 gettimeofday(&tv, NULL);
622 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
624 if(!recvdata(fd, data, len))
626 packet.tv_sec = tv.tv_sec;
627 packet.tv_usec = tv.tv_usec;
629 packet.origlen = len;
630 fwrite(&packet, sizeof packet, 1, out);
631 fwrite(data, len, 1, out);
636 static void logcontrol(int fd, FILE *out, int level) {
637 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
641 while(recvline(fd, line, sizeof line)) {
643 int n = sscanf(line, "%d %d %d", &code, &req, &len);
644 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
646 if(!recvdata(fd, data, len))
648 fwrite(data, len, 1, out);
655 static bool remove_service(void) {
656 SC_HANDLE manager = NULL;
657 SC_HANDLE service = NULL;
658 SERVICE_STATUS status = {0};
660 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
662 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
666 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
669 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
673 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
674 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
676 fprintf(stderr, "%s service stopped\n", identname);
678 if(!DeleteService(service)) {
679 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
683 fprintf(stderr, "%s service removed\n", identname);
689 bool connect_tincd(bool verbose) {
694 struct timeval tv = {0, 0};
695 if(select(fd + 1, &r, NULL, NULL, &tv)) {
696 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
704 FILE *f = fopen(pidfilename, "r");
707 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
714 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
716 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
722 if ((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
723 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
724 /* clean up the stale socket and pid file */
726 unlink(unixsocketname);
731 struct sockaddr_un sa;
732 sa.sun_family = AF_UNIX;
733 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
735 fd = socket(AF_UNIX, SOCK_STREAM, 0);
738 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
742 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
744 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
750 struct addrinfo hints = {
751 .ai_family = AF_UNSPEC,
752 .ai_socktype = SOCK_STREAM,
753 .ai_protocol = IPPROTO_TCP,
757 struct addrinfo *res = NULL;
759 if(getaddrinfo(host, port, &hints, &res) || !res) {
761 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
765 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
768 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
773 unsigned long arg = 0;
775 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
777 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
781 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
783 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
793 static const int one = 1;
794 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
800 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
802 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
808 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
810 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
812 fprintf(stderr, "Could not fully establish control socket connection\n");
822 static int cmd_start(int argc, char *argv[]) {
823 if(connect_tincd(false)) {
825 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
827 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
832 char *slash = strrchr(program_name, '/');
835 if ((c = strrchr(program_name, '\\')) > slash)
840 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
845 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
850 Windows has no real concept of an "argv array". A command line is just one string.
851 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
852 it uses quotes to handle spaces in arguments.
853 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
854 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
855 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
857 xasprintf(&arg0, "\"%s\"", arg0);
859 nargv[nargc++] = arg0;
860 for(int i = 1; i < optind; i++)
861 nargv[nargc++] = orig_argv[i];
862 for(int i = 1; i < argc; i++)
863 nargv[nargc++] = argv[i];
866 int status = spawnvp(_P_WAIT, c, nargv);
868 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
873 int pfd[2] = {-1, -1};
874 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
875 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
882 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
890 snprintf(buf, sizeof buf, "%d", pfd[1]);
891 setenv("TINC_UMBILICAL", buf, true);
892 exit(execvp(c, nargv));
899 int status = -1, result;
901 signal(SIGINT, SIG_IGN);
904 // Pass all log messages from the umbilical to stderr.
905 // A nul-byte right before closure means tincd started succesfully.
910 while((len = read(pfd[0], buf, sizeof buf)) > 0) {
911 failure = buf[len - 1];
922 // Make sure the child process is really gone.
923 result = waitpid(pid, &status, 0);
926 signal(SIGINT, SIG_DFL);
929 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
930 fprintf(stderr, "Error starting %s\n", c);
938 static int cmd_stop(int argc, char *argv[]) {
940 fprintf(stderr, "Too many arguments!\n");
945 if(!connect_tincd(true)) {
947 if(kill(pid, SIGTERM)) {
948 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
952 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
953 waitpid(pid, NULL, 0);
960 sendline(fd, "%d %d", CONTROL, REQ_STOP);
962 while(recvline(fd, line, sizeof line)) {
963 // Wait for tincd to close the connection...
966 if(!remove_service())
976 static int cmd_restart(int argc, char *argv[]) {
978 return cmd_start(argc, argv);
981 static int cmd_reload(int argc, char *argv[]) {
983 fprintf(stderr, "Too many arguments!\n");
987 if(!connect_tincd(true))
990 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
991 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
992 fprintf(stderr, "Could not reload configuration.\n");
1000 static int dump_invitations(void) {
1001 char dname[PATH_MAX];
1002 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
1003 DIR *dir = opendir(dname);
1005 if(errno == ENOENT) {
1006 fprintf(stderr, "No outstanding invitations.\n");
1010 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1017 while((ent = readdir(dir))) {
1018 char buf[MAX_STRING_SIZE];
1019 if(b64decode(ent->d_name, buf, 24) != 18)
1022 char fname[PATH_MAX];
1023 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
1024 FILE *f = fopen(fname, "r");
1026 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1032 if(!fgets(buf, sizeof buf, f)) {
1033 fprintf(stderr, "Invalid invitation file %s", fname);
1039 char *eol = buf + strlen(buf);
1040 while(strchr("\t \r\n", *--eol))
1042 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1043 fprintf(stderr, "Invalid invitation file %s", fname);
1048 printf("%s %s\n", ent->d_name, buf + 7);
1054 fprintf(stderr, "No outstanding invitations.\n");
1059 static int cmd_dump(int argc, char *argv[]) {
1060 bool only_reachable = false;
1062 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1063 if(strcasecmp(argv[2], "nodes")) {
1064 fprintf(stderr, "`reachable' only supported for nodes.\n");
1068 only_reachable = true;
1074 fprintf(stderr, "Invalid number of arguments.\n");
1079 if(!strcasecmp(argv[1], "invitations"))
1080 return dump_invitations();
1082 if(!connect_tincd(true))
1087 if(!strcasecmp(argv[1], "nodes"))
1088 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1089 else if(!strcasecmp(argv[1], "edges"))
1090 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1091 else if(!strcasecmp(argv[1], "subnets"))
1092 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1093 else if(!strcasecmp(argv[1], "connections"))
1094 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1095 else if(!strcasecmp(argv[1], "graph")) {
1096 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1097 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1099 } else if(!strcasecmp(argv[1], "digraph")) {
1100 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1101 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1104 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1110 printf("graph {\n");
1111 else if(do_graph == 2)
1112 printf("digraph {\n");
1114 while(recvline(fd, line, sizeof line)) {
1115 char node1[4096], node2[4096];
1116 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1118 if(do_graph && req == REQ_DUMP_NODES)
1136 char local_host[4096];
1137 char local_port[4096];
1140 int cipher, digest, maclength, compression, distance, socket, weight;
1141 short int pmtu, minmtu, maxmtu;
1142 unsigned int options, status_int;
1143 node_status_t status;
1144 long int last_state_change;
1147 case REQ_DUMP_NODES: {
1148 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);
1150 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1154 memcpy(&status, &status_int, sizeof status);
1157 const char *color = "black";
1158 if(!strcmp(host, "MYSELF"))
1160 else if(!status.reachable)
1162 else if(strcmp(via, node))
1164 else if(!status.validkey)
1168 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1170 if(only_reachable && !status.reachable)
1172 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",
1173 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1177 case REQ_DUMP_EDGES: {
1178 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);
1180 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1185 float w = 1 + 65536.0 / weight;
1186 if(do_graph == 1 && strcmp(node1, node2) > 0)
1187 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1188 else if(do_graph == 2)
1189 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1191 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);
1195 case REQ_DUMP_SUBNETS: {
1196 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1198 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1201 printf("%s owner %s\n", strip_weight(subnet), node);
1204 case REQ_DUMP_CONNECTIONS: {
1205 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1207 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1210 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1214 fprintf(stderr, "Unable to parse dump from tincd.\n");
1219 fprintf(stderr, "Error receiving dump.\n");
1223 static int cmd_purge(int argc, char *argv[]) {
1225 fprintf(stderr, "Too many arguments!\n");
1229 if(!connect_tincd(true))
1232 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1233 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1234 fprintf(stderr, "Could not purge old information.\n");
1241 static int cmd_debug(int argc, char *argv[]) {
1243 fprintf(stderr, "Invalid number of arguments.\n");
1247 if(!connect_tincd(true))
1250 int debuglevel = atoi(argv[1]);
1253 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1254 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1255 fprintf(stderr, "Could not set debug level.\n");
1259 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1263 static int cmd_retry(int argc, char *argv[]) {
1265 fprintf(stderr, "Too many arguments!\n");
1269 if(!connect_tincd(true))
1272 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1273 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1274 fprintf(stderr, "Could not retry outgoing connections.\n");
1281 static int cmd_connect(int argc, char *argv[]) {
1283 fprintf(stderr, "Invalid number of arguments.\n");
1287 if(!check_id(argv[1])) {
1288 fprintf(stderr, "Invalid name for node.\n");
1292 if(!connect_tincd(true))
1295 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1296 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1297 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1304 static int cmd_disconnect(int argc, char *argv[]) {
1306 fprintf(stderr, "Invalid number of arguments.\n");
1310 if(!check_id(argv[1])) {
1311 fprintf(stderr, "Invalid name for node.\n");
1315 if(!connect_tincd(true))
1318 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1319 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1320 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1327 static int cmd_top(int argc, char *argv[]) {
1329 fprintf(stderr, "Too many arguments!\n");
1334 if(!connect_tincd(true))
1340 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1345 static int cmd_pcap(int argc, char *argv[]) {
1347 fprintf(stderr, "Too many arguments!\n");
1351 if(!connect_tincd(true))
1354 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1359 static void sigint_handler(int sig) {
1360 fprintf(stderr, "\n");
1361 shutdown(fd, SHUT_RDWR);
1365 static int cmd_log(int argc, char *argv[]) {
1367 fprintf(stderr, "Too many arguments!\n");
1371 if(!connect_tincd(true))
1375 signal(SIGINT, sigint_handler);
1378 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1381 signal(SIGINT, SIG_DFL);
1389 static int cmd_pid(int argc, char *argv[]) {
1391 fprintf(stderr, "Too many arguments!\n");
1395 if(!connect_tincd(true) || !pid)
1398 printf("%d\n", pid);
1402 int rstrip(char *value) {
1403 int len = strlen(value);
1404 while(len && strchr("\t\r\n ", value[len - 1]))
1409 char *get_my_name(bool verbose) {
1410 FILE *f = fopen(tinc_conf, "r");
1413 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1419 while(fgets(buf, sizeof buf, f)) {
1420 int len = strcspn(buf, "\t =");
1422 value += strspn(value, "\t ");
1425 value += strspn(value, "\t ");
1430 if(strcasecmp(buf, "Name"))
1434 return replace_name(value);
1440 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1444 ecdsa_t *get_pubkey(FILE *f) {
1447 while(fgets(buf, sizeof buf, f)) {
1448 int len = strcspn(buf, "\t =");
1450 value += strspn(value, "\t ");
1453 value += strspn(value, "\t ");
1458 if(strcasecmp(buf, "Ed25519PublicKey"))
1461 return ecdsa_set_base64_public_key(value);
1467 const var_t variables[] = {
1468 /* Server configuration */
1469 {"AddressFamily", VAR_SERVER},
1470 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1471 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1472 {"BindToInterface", VAR_SERVER},
1473 {"Broadcast", VAR_SERVER | VAR_SAFE},
1474 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1475 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1476 {"DecrementTTL", VAR_SERVER},
1477 {"Device", VAR_SERVER},
1478 {"DeviceStandby", VAR_SERVER},
1479 {"DeviceType", VAR_SERVER},
1480 {"DirectOnly", VAR_SERVER},
1481 {"Ed25519PrivateKeyFile", VAR_SERVER},
1482 {"ExperimentalProtocol", VAR_SERVER},
1483 {"Forwarding", VAR_SERVER},
1484 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1485 {"Hostnames", VAR_SERVER},
1486 {"IffOneQueue", VAR_SERVER},
1487 {"Interface", VAR_SERVER},
1488 {"KeyExpire", VAR_SERVER},
1489 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1490 {"LocalDiscovery", VAR_SERVER},
1491 {"MACExpire", VAR_SERVER},
1492 {"MaxConnectionBurst", VAR_SERVER},
1493 {"MaxOutputBufferSize", VAR_SERVER},
1494 {"MaxTimeout", VAR_SERVER},
1495 {"Mode", VAR_SERVER | VAR_SAFE},
1496 {"Name", VAR_SERVER},
1497 {"PingInterval", VAR_SERVER},
1498 {"PingTimeout", VAR_SERVER},
1499 {"PriorityInheritance", VAR_SERVER},
1500 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1501 {"PrivateKeyFile", VAR_SERVER},
1502 {"ProcessPriority", VAR_SERVER},
1503 {"Proxy", VAR_SERVER},
1504 {"ReplayWindow", VAR_SERVER},
1505 {"ScriptsExtension", VAR_SERVER},
1506 {"ScriptsInterpreter", VAR_SERVER},
1507 {"StrictSubnets", VAR_SERVER},
1508 {"TunnelServer", VAR_SERVER},
1509 {"UDPDiscovery", VAR_SERVER},
1510 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1511 {"UDPDiscoveryInterval", VAR_SERVER},
1512 {"UDPDiscoveryTimeout", VAR_SERVER},
1513 {"MTUInfoInterval", VAR_SERVER},
1514 {"UDPInfoInterval", VAR_SERVER},
1515 {"UDPRcvBuf", VAR_SERVER},
1516 {"UDPSndBuf", VAR_SERVER},
1517 {"UPnP", VAR_SERVER},
1518 {"UPnPDiscoverWait", VAR_SERVER},
1519 {"UPnPRefreshPeriod", VAR_SERVER},
1520 {"VDEGroup", VAR_SERVER},
1521 {"VDEPort", VAR_SERVER},
1522 /* Host configuration */
1523 {"Address", VAR_HOST | VAR_MULTIPLE},
1524 {"Cipher", VAR_SERVER | VAR_HOST},
1525 {"ClampMSS", VAR_SERVER | VAR_HOST},
1526 {"Compression", VAR_SERVER | VAR_HOST},
1527 {"Digest", VAR_SERVER | VAR_HOST},
1528 {"Ed25519PublicKey", VAR_HOST},
1529 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1530 {"IndirectData", VAR_SERVER | VAR_HOST},
1531 {"MACLength", VAR_SERVER | VAR_HOST},
1532 {"PMTU", VAR_SERVER | VAR_HOST},
1533 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1535 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1536 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1537 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1538 {"TCPOnly", VAR_SERVER | VAR_HOST},
1539 {"Weight", VAR_HOST | VAR_SAFE},
1543 static int cmd_config(int argc, char *argv[]) {
1545 fprintf(stderr, "Invalid number of arguments.\n");
1549 if(strcasecmp(argv[0], "config"))
1553 if(!strcasecmp(argv[1], "get")) {
1555 } else if(!strcasecmp(argv[1], "add")) {
1556 argv++, argc--, action = 1;
1557 } else if(!strcasecmp(argv[1], "del")) {
1558 argv++, argc--, action = -1;
1559 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1560 argv++, argc--, action = 0;
1564 fprintf(stderr, "Invalid number of arguments.\n");
1568 // Concatenate the rest of the command line
1569 strncpy(line, argv[1], sizeof line - 1);
1570 for(int i = 2; i < argc; i++) {
1571 strncat(line, " ", sizeof line - 1 - strlen(line));
1572 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1575 // Liberal parsing into node name, variable name and value.
1581 len = strcspn(line, "\t =");
1583 value += strspn(value, "\t ");
1586 value += strspn(value, "\t ");
1589 variable = strchr(line, '.');
1598 fprintf(stderr, "No variable given.\n");
1602 if(action >= 0 && !*value) {
1603 fprintf(stderr, "No value for variable given.\n");
1607 if(action < -1 && *value)
1610 /* Some simple checks. */
1612 bool warnonremove = false;
1614 for(int i = 0; variables[i].name; i++) {
1615 if(strcasecmp(variables[i].name, variable))
1619 variable = (char *)variables[i].name;
1621 /* Discourage use of obsolete variables. */
1623 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1625 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1627 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1632 /* Don't put server variables in host config files */
1634 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1636 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1638 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1643 /* Should this go into our own host config file? */
1645 if(!node && !(variables[i].type & VAR_SERVER)) {
1646 node = get_my_name(true);
1651 /* Change "add" into "set" for variables that do not allow multiple occurences.
1652 Turn on warnings when it seems variables might be removed unintentionally. */
1654 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1655 warnonremove = true;
1657 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1658 warnonremove = true;
1664 if(node && !check_id(node)) {
1665 fprintf(stderr, "Invalid name for node.\n");
1670 if(force || action < 0) {
1671 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1673 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1678 // Open the right configuration file.
1679 char filename[PATH_MAX];
1681 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1683 snprintf(filename, sizeof filename, "%s", tinc_conf);
1685 FILE *f = fopen(filename, "r");
1687 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1691 char tmpfile[PATH_MAX];
1695 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1696 tf = fopen(tmpfile, "w");
1698 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1704 // Copy the file, making modifications on the fly, unless we are just getting a value.
1708 bool removed = false;
1711 while(fgets(buf1, sizeof buf1, f)) {
1712 buf1[sizeof buf1 - 1] = 0;
1713 strncpy(buf2, buf1, sizeof buf2);
1715 // Parse line in a simple way
1719 len = strcspn(buf2, "\t =");
1720 bvalue = buf2 + len;
1721 bvalue += strspn(bvalue, "\t ");
1722 if(*bvalue == '=') {
1724 bvalue += strspn(bvalue, "\t ");
1730 if(!strcasecmp(buf2, variable)) {
1734 printf("%s\n", bvalue);
1736 } else if(action == -1) {
1737 if(!*value || !strcasecmp(bvalue, value)) {
1742 } else if(action == 0) {
1743 // Warn if "set" was used for variables that can occur multiple times
1744 if(warnonremove && strcasecmp(bvalue, value))
1745 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1747 // Already set? Delete the rest...
1751 // Otherwise, replace.
1752 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1753 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1759 } else if(action > 0) {
1760 // Check if we've already seen this variable with the same value
1761 if(!strcasecmp(bvalue, value))
1767 // Copy original line...
1768 if(fputs(buf1, tf) < 0) {
1769 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1773 // Add newline if it is missing...
1774 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1775 if(fputc('\n', tf) < 0) {
1776 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1783 // Make sure we read everything...
1784 if(ferror(f) || !feof(f)) {
1785 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1790 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1794 // Add new variable if necessary.
1795 if((action > 0 && !found)|| (action == 0 && !set)) {
1796 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1797 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1806 fprintf(stderr, "No matching configuration variables found.\n");
1811 // Make sure we wrote everything...
1813 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1817 // Could we find what we had to remove?
1818 if(action < 0 && !removed) {
1820 fprintf(stderr, "No configuration variables deleted.\n");
1824 // Replace the configuration file with the new one
1826 if(remove(filename)) {
1827 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1831 if(rename(tmpfile, filename)) {
1832 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1836 // Silently try notifying a running tincd of changes.
1837 if(connect_tincd(false))
1838 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1843 static bool try_bind(int port) {
1844 struct addrinfo *ai = NULL, *aip;
1845 struct addrinfo hint = {
1846 .ai_flags = AI_PASSIVE,
1847 .ai_family = AF_UNSPEC,
1848 .ai_socktype = SOCK_STREAM,
1849 .ai_protocol = IPPROTO_TCP,
1852 bool success = true;
1854 snprintf(portstr, sizeof portstr, "%d", port);
1856 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1859 for(aip = ai; aip; aip = aip->ai_next) {
1860 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1866 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1878 int check_port(char *name) {
1882 fprintf(stderr, "Warning: could not bind to port 655. ");
1884 for(int i = 0; i < 100; i++) {
1885 int port = 0x1000 + (rand() & 0x7fff);
1886 if(try_bind(port)) {
1887 char filename[PATH_MAX];
1888 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1889 FILE *f = fopen(filename, "a");
1891 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1892 fprintf(stderr, "Please change tinc's Port manually.\n");
1896 fprintf(f, "Port = %d\n", port);
1898 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1903 fprintf(stderr, "Please change tinc's Port manually.\n");
1907 static int cmd_init(int argc, char *argv[]) {
1908 if(!access(tinc_conf, F_OK)) {
1909 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1914 fprintf(stderr, "Too many arguments!\n");
1916 } else if(argc < 2) {
1919 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1920 if(!fgets(buf, sizeof buf, stdin)) {
1921 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1924 int len = rstrip(buf);
1926 fprintf(stderr, "No name given!\n");
1931 fprintf(stderr, "No Name given!\n");
1935 name = strdup(argv[1]);
1937 fprintf(stderr, "No Name given!\n");
1942 if(!check_id(name)) {
1943 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1947 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1948 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1952 if(mkdir(confbase, 0777) && errno != EEXIST) {
1953 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1957 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1958 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1962 FILE *f = fopen(tinc_conf, "w");
1964 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1968 fprintf(f, "Name = %s\n", name);
1971 #ifndef DISABLE_LEGACY
1972 if(!rsa_keygen(2048, false))
1976 if(!ed25519_keygen(false))
1982 char filename[PATH_MAX];
1983 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1984 if(access(filename, F_OK)) {
1985 FILE *f = fopenmask(filename, "w", 0777);
1987 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1990 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");
1999 static int cmd_generate_keys(int argc, char *argv[]) {
2000 #ifdef DISABLE_LEGACY
2005 fprintf(stderr, "Too many arguments!\n");
2010 name = get_my_name(false);
2012 #ifndef DISABLE_LEGACY
2013 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
2017 if(!ed25519_keygen(true))
2023 #ifndef DISABLE_LEGACY
2024 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2026 fprintf(stderr, "Too many arguments!\n");
2031 name = get_my_name(false);
2033 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2037 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2039 fprintf(stderr, "Too many arguments!\n");
2044 name = get_my_name(false);
2046 return !ed25519_keygen(true);
2049 static int cmd_help(int argc, char *argv[]) {
2054 static int cmd_version(int argc, char *argv[]) {
2056 fprintf(stderr, "Too many arguments!\n");
2064 static int cmd_info(int argc, char *argv[]) {
2066 fprintf(stderr, "Invalid number of arguments.\n");
2070 if(!connect_tincd(true))
2073 return info(fd, argv[1]);
2076 static const char *conffiles[] = {
2087 static int cmd_edit(int argc, char *argv[]) {
2089 fprintf(stderr, "Invalid number of arguments.\n");
2093 char filename[PATH_MAX] = "";
2095 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2096 for(int i = 0; conffiles[i]; i++) {
2097 if(!strcmp(argv[1], conffiles[i])) {
2098 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2107 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2108 char *dash = strchr(argv[1], '-');
2111 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2112 fprintf(stderr, "Invalid configuration filename.\n");
2120 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2122 xasprintf(&command, "edit \"%s\"", filename);
2124 int result = system(command);
2129 // Silently try notifying a running tincd of changes.
2130 if(connect_tincd(false))
2131 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2136 static int export(const char *name, FILE *out) {
2137 char filename[PATH_MAX];
2138 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2139 FILE *in = fopen(filename, "r");
2141 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2145 fprintf(out, "Name = %s\n", name);
2147 while(fgets(buf, sizeof buf, in)) {
2148 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2153 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2162 static int cmd_export(int argc, char *argv[]) {
2164 fprintf(stderr, "Too many arguments!\n");
2168 char *name = get_my_name(true);
2172 int result = export(name, stdout);
2180 static int cmd_export_all(int argc, char *argv[]) {
2182 fprintf(stderr, "Too many arguments!\n");
2186 DIR *dir = opendir(hosts_dir);
2188 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2196 while((ent = readdir(dir))) {
2197 if(!check_id(ent->d_name))
2203 printf("#---------------------------------------------------------------#\n");
2205 result |= export(ent->d_name, stdout);
2214 static int cmd_import(int argc, char *argv[]) {
2216 fprintf(stderr, "Too many arguments!\n");
2225 char filename[PATH_MAX] = "";
2227 bool firstline = true;
2229 while(fgets(buf, sizeof buf, in)) {
2230 if(sscanf(buf, "Name = %s", name) == 1) {
2233 if(!check_id(name)) {
2234 fprintf(stderr, "Invalid Name in input!\n");
2241 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2243 if(!force && !access(filename, F_OK)) {
2244 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2249 out = fopen(filename, "w");
2251 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2257 } else if(firstline) {
2258 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2263 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2267 if(fputs(buf, out) < 0) {
2268 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2278 fprintf(stderr, "Imported %d host configuration files.\n", count);
2281 fprintf(stderr, "No host configuration files imported.\n");
2286 static int cmd_exchange(int argc, char *argv[]) {
2287 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2290 static int cmd_exchange_all(int argc, char *argv[]) {
2291 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2294 static int switch_network(char *name) {
2295 if(strcmp(name, ".")) {
2296 if(!check_netname(name, false)) {
2297 fprintf(stderr, "Invalid character in netname!\n");
2301 if(!check_netname(name, true))
2302 fprintf(stderr, "Warning: unsafe character in netname!\n");
2311 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2318 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2319 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2320 xasprintf(&prompt, "%s> ", identname);
2325 static int cmd_network(int argc, char *argv[]) {
2327 fprintf(stderr, "Too many arguments!\n");
2332 return switch_network(argv[1]);
2334 DIR *dir = opendir(confdir);
2336 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2341 while((ent = readdir(dir))) {
2342 if(*ent->d_name == '.')
2345 if(!strcmp(ent->d_name, "tinc.conf")) {
2350 char fname[PATH_MAX];
2351 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2352 if(!access(fname, R_OK))
2353 printf("%s\n", ent->d_name);
2361 static int cmd_fsck(int argc, char *argv[]) {
2363 fprintf(stderr, "Too many arguments!\n");
2367 return fsck(orig_argv[0]);
2370 static void *readfile(FILE *in, size_t *len) {
2372 size_t alloced = 4096;
2373 char *buf = xmalloc(alloced);
2376 size_t read = fread(buf + count, 1, alloced - count, in);
2380 if(count >= alloced) {
2382 buf = xrealloc(buf, alloced);
2392 static int cmd_sign(int argc, char *argv[]) {
2394 fprintf(stderr, "Too many arguments!\n");
2399 name = get_my_name(true);
2404 char fname[PATH_MAX];
2405 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
2406 FILE *fp = fopen(fname, "r");
2408 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2412 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2415 fprintf(stderr, "Could not read private key from %s\n", fname);
2425 in = fopen(argv[1], "rb");
2427 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2436 char *data = readfile(in, &len);
2440 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2445 // Ensure we sign our name and current time as well
2446 long t = time(NULL);
2448 xasprintf(&trailer, " %s %ld", name, t);
2449 int trailer_len = strlen(trailer);
2451 data = xrealloc(data, len + trailer_len);
2452 memcpy(data + len, trailer, trailer_len);
2456 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2457 fprintf(stderr, "Error generating signature\n");
2462 b64encode(sig, sig, 64);
2465 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2466 fwrite(data, len, 1, stdout);
2472 static int cmd_verify(int argc, char *argv[]) {
2474 fprintf(stderr, "Not enough arguments!\n");
2479 fprintf(stderr, "Too many arguments!\n");
2483 char *node = argv[1];
2484 if(!strcmp(node, ".")) {
2486 name = get_my_name(true);
2491 } else if(!strcmp(node, "*")) {
2494 if(!check_id(node)) {
2495 fprintf(stderr, "Invalid node name\n");
2503 in = fopen(argv[2], "rb");
2505 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2513 char *data = readfile(in, &len);
2517 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2521 char *newline = memchr(data, '\n', len);
2522 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2523 fprintf(stderr, "Invalid input\n");
2529 size_t skip = newline - data;
2531 char signer[MAX_STRING_SIZE] = "";
2532 char sig[MAX_STRING_SIZE] = "";
2535 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2536 fprintf(stderr, "Invalid input\n");
2541 if(node && strcmp(node, signer)) {
2542 fprintf(stderr, "Signature is not made by %s\n", node);
2551 xasprintf(&trailer, " %s %ld", signer, t);
2552 int trailer_len = strlen(trailer);
2554 data = xrealloc(data, len + trailer_len);
2555 memcpy(data + len, trailer, trailer_len);
2558 newline = data + skip;
2560 char fname[PATH_MAX];
2561 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, node);
2562 FILE *fp = fopen(fname, "r");
2564 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2569 ecdsa_t *key = get_pubkey(fp);
2572 key = ecdsa_read_pem_public_key(fp);
2575 fprintf(stderr, "Could not read public key from %s\n", fname);
2583 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2584 fprintf(stderr, "Invalid signature\n");
2592 fwrite(newline, len - (newline - data), 1, stdout);
2598 static const struct {
2599 const char *command;
2600 int (*function)(int argc, char *argv[]);
2603 {"start", cmd_start},
2605 {"restart", cmd_restart},
2606 {"reload", cmd_reload},
2609 {"purge", cmd_purge},
2610 {"debug", cmd_debug},
2611 {"retry", cmd_retry},
2612 {"connect", cmd_connect},
2613 {"disconnect", cmd_disconnect},
2618 {"config", cmd_config, true},
2619 {"add", cmd_config},
2620 {"del", cmd_config},
2621 {"get", cmd_config},
2622 {"set", cmd_config},
2624 {"generate-keys", cmd_generate_keys},
2625 #ifndef DISABLE_LEGACY
2626 {"generate-rsa-keys", cmd_generate_rsa_keys},
2628 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2630 {"version", cmd_version},
2633 {"export", cmd_export},
2634 {"export-all", cmd_export_all},
2635 {"import", cmd_import},
2636 {"exchange", cmd_exchange},
2637 {"exchange-all", cmd_exchange_all},
2638 {"invite", cmd_invite},
2640 {"network", cmd_network},
2643 {"verify", cmd_verify},
2647 #ifdef HAVE_READLINE
2648 static char *complete_command(const char *text, int state) {
2656 while(commands[i].command) {
2657 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2658 return xstrdup(commands[i].command);
2665 static char *complete_dump(const char *text, int state) {
2666 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2675 if(!strncasecmp(matches[i], text, strlen(text)))
2676 return xstrdup(matches[i]);
2683 static char *complete_config(const char *text, int state) {
2691 while(variables[i].name) {
2692 char *dot = strchr(text, '.');
2694 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2696 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2700 if(!strncasecmp(variables[i].name, text, strlen(text)))
2701 return xstrdup(variables[i].name);
2709 static char *complete_info(const char *text, int state) {
2713 if(!connect_tincd(false))
2715 // Check the list of nodes
2716 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2717 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2720 while(recvline(fd, line, sizeof line)) {
2722 int n = sscanf(line, "%d %d %s", &code, &req, item);
2732 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2736 if(!strncmp(item, text, strlen(text)))
2737 return xstrdup(strip_weight(item));
2743 static char *complete_nothing(const char *text, int state) {
2747 static char **completion (const char *text, int start, int end) {
2748 char **matches = NULL;
2751 matches = rl_completion_matches(text, complete_command);
2752 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2753 matches = rl_completion_matches(text, complete_dump);
2754 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2755 matches = rl_completion_matches(text, complete_config);
2756 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2757 matches = rl_completion_matches(text, complete_config);
2758 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2759 matches = rl_completion_matches(text, complete_config);
2760 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2761 matches = rl_completion_matches(text, complete_config);
2762 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2763 matches = rl_completion_matches(text, complete_info);
2769 static int cmd_shell(int argc, char *argv[]) {
2770 xasprintf(&prompt, "%s> ", identname);
2774 int maxargs = argc + 16;
2775 char **nargv = xmalloc(maxargs * sizeof *nargv);
2777 for(int i = 0; i < argc; i++)
2780 #ifdef HAVE_READLINE
2781 rl_readline_name = "tinc";
2782 rl_completion_entry_function = complete_nothing;
2783 rl_attempted_completion_function = completion;
2784 rl_filename_completion_desired = 0;
2789 #ifdef HAVE_READLINE
2793 rl_basic_word_break_characters = "\t\n ";
2794 line = readline(prompt);
2796 copy = xstrdup(line);
2798 line = fgets(buf, sizeof buf, stdin);
2802 fputs(prompt, stdout);
2804 line = fgets(buf, sizeof buf, stdin);
2810 /* Ignore comments */
2818 char *p = line + strspn(line, " \t\n");
2819 char *next = strtok(p, " \t\n");
2822 if(nargc >= maxargs) {
2823 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2826 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2831 next = strtok(NULL, " \t\n");
2837 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
2844 for(int i = 0; commands[i].command; i++) {
2845 if(!strcasecmp(nargv[argc], commands[i].command)) {
2846 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2852 #ifdef HAVE_READLINE
2858 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2871 int main(int argc, char *argv[]) {
2872 program_name = argv[0];
2875 tty = isatty(0) && isatty(1);
2877 if(!parse_options(argc, argv))
2881 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2882 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2895 static struct WSAData wsa_state;
2897 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2898 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2907 return cmd_shell(argc, argv);
2909 for(int i = 0; commands[i].command; i++) {
2910 if(!strcasecmp(argv[optind], commands[i].command))
2911 return commands[i].function(argc - optind, argv + optind);
2914 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);