2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2015 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-2015 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) {
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 directory = get_current_dir_name();
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);
565 if(blen < 1 || blen >= sizeof buffer)
572 int result = send(fd, p, blen, MSG_NOSIGNAL);
573 if(result == -1 && sockerrno == EINTR)
584 static void pcap(int fd, FILE *out, int snaplen) {
585 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
593 uint32_t tz_accuracy;
600 snaplen ?: sizeof data,
613 fwrite(&header, sizeof header, 1, out);
617 while(recvline(fd, line, sizeof line)) {
619 int n = sscanf(line, "%d %d %d", &code, &req, &len);
620 gettimeofday(&tv, NULL);
621 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
623 if(!recvdata(fd, data, len))
625 packet.tv_sec = tv.tv_sec;
626 packet.tv_usec = tv.tv_usec;
628 packet.origlen = len;
629 fwrite(&packet, sizeof packet, 1, out);
630 fwrite(data, len, 1, out);
635 static void logcontrol(int fd, FILE *out, int level) {
636 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
640 while(recvline(fd, line, sizeof line)) {
642 int n = sscanf(line, "%d %d %d", &code, &req, &len);
643 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
645 if(!recvdata(fd, data, len))
647 fwrite(data, len, 1, out);
654 static bool remove_service(void) {
655 SC_HANDLE manager = NULL;
656 SC_HANDLE service = NULL;
657 SERVICE_STATUS status = {0};
659 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
661 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
665 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
668 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
672 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
673 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
675 fprintf(stderr, "%s service stopped\n", identname);
677 if(!DeleteService(service)) {
678 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
682 fprintf(stderr, "%s service removed\n", identname);
688 bool connect_tincd(bool verbose) {
693 struct timeval tv = {0, 0};
694 if(select(fd + 1, &r, NULL, NULL, &tv)) {
695 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
703 FILE *f = fopen(pidfilename, "r");
706 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
713 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
715 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
723 struct sockaddr_un sa;
724 sa.sun_family = AF_UNIX;
725 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
727 fd = socket(AF_UNIX, SOCK_STREAM, 0);
730 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
734 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
736 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
742 struct addrinfo hints = {
743 .ai_family = AF_UNSPEC,
744 .ai_socktype = SOCK_STREAM,
745 .ai_protocol = IPPROTO_TCP,
749 struct addrinfo *res = NULL;
751 if(getaddrinfo(host, port, &hints, &res) || !res) {
753 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
757 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
760 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
765 unsigned long arg = 0;
767 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
769 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
773 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
775 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
785 static const int one = 1;
786 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
792 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
794 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
800 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
802 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
804 fprintf(stderr, "Could not fully establish control socket connection\n");
814 static int cmd_start(int argc, char *argv[]) {
815 if(connect_tincd(false)) {
817 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
819 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
824 char *slash = strrchr(program_name, '/');
827 if ((c = strrchr(program_name, '\\')) > slash)
832 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
837 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
842 Windows has no real concept of an "argv array". A command line is just one string.
843 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
844 it uses quotes to handle spaces in arguments.
845 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
846 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
847 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
849 xasprintf(&arg0, "\"%s\"", arg0);
851 nargv[nargc++] = arg0;
852 for(int i = 1; i < optind; i++)
853 nargv[nargc++] = orig_argv[i];
854 for(int i = 1; i < argc; i++)
855 nargv[nargc++] = argv[i];
858 int status = spawnvp(_P_WAIT, c, nargv);
860 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
865 int pfd[2] = {-1, -1};
866 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
867 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
874 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
882 snprintf(buf, sizeof buf, "%d", pfd[1]);
883 setenv("TINC_UMBILICAL", buf, true);
884 exit(execvp(c, nargv));
891 int status = -1, result;
893 signal(SIGINT, SIG_IGN);
896 // Pass all log messages from the umbilical to stderr.
897 // A nul-byte right before closure means tincd started succesfully.
902 while((len = read(pfd[0], buf, sizeof buf)) > 0) {
903 failure = buf[len - 1];
914 // Make sure the child process is really gone.
915 result = waitpid(pid, &status, 0);
918 signal(SIGINT, SIG_DFL);
921 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
922 fprintf(stderr, "Error starting %s\n", c);
930 static int cmd_stop(int argc, char *argv[]) {
932 fprintf(stderr, "Too many arguments!\n");
937 if(!connect_tincd(true)) {
939 if(kill(pid, SIGTERM)) {
940 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
944 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
945 waitpid(pid, NULL, 0);
952 sendline(fd, "%d %d", CONTROL, REQ_STOP);
954 while(recvline(fd, line, sizeof line)) {
955 // Wait for tincd to close the connection...
958 if(!remove_service())
968 static int cmd_restart(int argc, char *argv[]) {
970 return cmd_start(argc, argv);
973 static int cmd_reload(int argc, char *argv[]) {
975 fprintf(stderr, "Too many arguments!\n");
979 if(!connect_tincd(true))
982 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
983 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
984 fprintf(stderr, "Could not reload configuration.\n");
992 static int dump_invitations(void) {
993 char dname[PATH_MAX];
994 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
995 DIR *dir = opendir(dname);
997 if(errno == ENOENT) {
998 fprintf(stderr, "No outstanding invitations.\n");
1002 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1009 while((ent = readdir(dir))) {
1010 char buf[MAX_STRING_SIZE];
1011 if(b64decode(ent->d_name, buf, 24) != 18)
1014 char fname[PATH_MAX];
1015 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
1016 FILE *f = fopen(fname, "r");
1018 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1024 if(!fgets(buf, sizeof buf, f)) {
1025 fprintf(stderr, "Invalid invitation file %s", fname);
1031 char *eol = buf + strlen(buf);
1032 while(strchr("\t \r\n", *--eol))
1034 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1035 fprintf(stderr, "Invalid invitation file %s", fname);
1040 printf("%s %s\n", ent->d_name, buf + 7);
1046 fprintf(stderr, "No outstanding invitations.\n");
1051 static int cmd_dump(int argc, char *argv[]) {
1052 bool only_reachable = false;
1054 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1055 if(strcasecmp(argv[2], "nodes")) {
1056 fprintf(stderr, "`reachable' only supported for nodes.\n");
1060 only_reachable = true;
1066 fprintf(stderr, "Invalid number of arguments.\n");
1071 if(!strcasecmp(argv[1], "invitations"))
1072 return dump_invitations();
1074 if(!connect_tincd(true))
1079 if(!strcasecmp(argv[1], "nodes"))
1080 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1081 else if(!strcasecmp(argv[1], "edges"))
1082 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1083 else if(!strcasecmp(argv[1], "subnets"))
1084 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1085 else if(!strcasecmp(argv[1], "connections"))
1086 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1087 else if(!strcasecmp(argv[1], "graph")) {
1088 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1089 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1091 } else if(!strcasecmp(argv[1], "digraph")) {
1092 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1093 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1096 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1102 printf("graph {\n");
1103 else if(do_graph == 2)
1104 printf("digraph {\n");
1106 while(recvline(fd, line, sizeof line)) {
1107 char node1[4096], node2[4096];
1108 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1110 if(do_graph && req == REQ_DUMP_NODES)
1128 char local_host[4096];
1129 char local_port[4096];
1132 int cipher, digest, maclength, compression, distance, socket, weight;
1133 short int pmtu, minmtu, maxmtu;
1134 unsigned int options, status_int;
1135 node_status_t status;
1136 long int last_state_change;
1139 case REQ_DUMP_NODES: {
1140 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);
1142 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1146 memcpy(&status, &status_int, sizeof status);
1149 const char *color = "black";
1150 if(!strcmp(host, "MYSELF"))
1152 else if(!status.reachable)
1154 else if(strcmp(via, node))
1156 else if(!status.validkey)
1160 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1162 if(only_reachable && !status.reachable)
1164 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",
1165 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1169 case REQ_DUMP_EDGES: {
1170 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);
1172 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1177 float w = 1 + 65536.0 / weight;
1178 if(do_graph == 1 && strcmp(node1, node2) > 0)
1179 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1180 else if(do_graph == 2)
1181 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1183 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);
1187 case REQ_DUMP_SUBNETS: {
1188 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1190 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1193 printf("%s owner %s\n", strip_weight(subnet), node);
1196 case REQ_DUMP_CONNECTIONS: {
1197 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1199 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1202 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1206 fprintf(stderr, "Unable to parse dump from tincd.\n");
1211 fprintf(stderr, "Error receiving dump.\n");
1215 static int cmd_purge(int argc, char *argv[]) {
1217 fprintf(stderr, "Too many arguments!\n");
1221 if(!connect_tincd(true))
1224 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1225 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1226 fprintf(stderr, "Could not purge old information.\n");
1233 static int cmd_debug(int argc, char *argv[]) {
1235 fprintf(stderr, "Invalid number of arguments.\n");
1239 if(!connect_tincd(true))
1242 int debuglevel = atoi(argv[1]);
1245 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1246 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1247 fprintf(stderr, "Could not set debug level.\n");
1251 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1255 static int cmd_retry(int argc, char *argv[]) {
1257 fprintf(stderr, "Too many arguments!\n");
1261 if(!connect_tincd(true))
1264 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1265 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1266 fprintf(stderr, "Could not retry outgoing connections.\n");
1273 static int cmd_connect(int argc, char *argv[]) {
1275 fprintf(stderr, "Invalid number of arguments.\n");
1279 if(!check_id(argv[1])) {
1280 fprintf(stderr, "Invalid name for node.\n");
1284 if(!connect_tincd(true))
1287 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1288 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1289 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1296 static int cmd_disconnect(int argc, char *argv[]) {
1298 fprintf(stderr, "Invalid number of arguments.\n");
1302 if(!check_id(argv[1])) {
1303 fprintf(stderr, "Invalid name for node.\n");
1307 if(!connect_tincd(true))
1310 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1311 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1312 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1319 static int cmd_top(int argc, char *argv[]) {
1321 fprintf(stderr, "Too many arguments!\n");
1326 if(!connect_tincd(true))
1332 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1337 static int cmd_pcap(int argc, char *argv[]) {
1339 fprintf(stderr, "Too many arguments!\n");
1343 if(!connect_tincd(true))
1346 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1351 static void sigint_handler(int sig) {
1352 fprintf(stderr, "\n");
1353 shutdown(fd, SHUT_RDWR);
1357 static int cmd_log(int argc, char *argv[]) {
1359 fprintf(stderr, "Too many arguments!\n");
1363 if(!connect_tincd(true))
1367 signal(SIGINT, sigint_handler);
1370 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1373 signal(SIGINT, SIG_DFL);
1381 static int cmd_pid(int argc, char *argv[]) {
1383 fprintf(stderr, "Too many arguments!\n");
1387 if(!connect_tincd(true) && !pid)
1390 printf("%d\n", pid);
1394 int rstrip(char *value) {
1395 int len = strlen(value);
1396 while(len && strchr("\t\r\n ", value[len - 1]))
1401 char *get_my_name(bool verbose) {
1402 FILE *f = fopen(tinc_conf, "r");
1405 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1411 while(fgets(buf, sizeof buf, f)) {
1412 int len = strcspn(buf, "\t =");
1414 value += strspn(value, "\t ");
1417 value += strspn(value, "\t ");
1422 if(strcasecmp(buf, "Name"))
1426 return replace_name(value);
1432 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1436 static ecdsa_t *get_pubkey(FILE *f) {
1439 while(fgets(buf, sizeof buf, f)) {
1440 int len = strcspn(buf, "\t =");
1442 value += strspn(value, "\t ");
1445 value += strspn(value, "\t ");
1450 if(strcasecmp(buf, "Ed25519PublicKey"))
1453 return ecdsa_set_base64_public_key(value);
1459 const var_t variables[] = {
1460 /* Server configuration */
1461 {"AddressFamily", VAR_SERVER},
1462 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1463 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1464 {"BindToInterface", VAR_SERVER},
1465 {"Broadcast", VAR_SERVER | VAR_SAFE},
1466 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1467 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1468 {"DecrementTTL", VAR_SERVER},
1469 {"Device", VAR_SERVER},
1470 {"DeviceStandby", VAR_SERVER},
1471 {"DeviceType", VAR_SERVER},
1472 {"DirectOnly", VAR_SERVER},
1473 {"Ed25519PrivateKeyFile", VAR_SERVER},
1474 {"ExperimentalProtocol", VAR_SERVER},
1475 {"Forwarding", VAR_SERVER},
1476 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1477 {"Hostnames", VAR_SERVER},
1478 {"IffOneQueue", VAR_SERVER},
1479 {"Interface", VAR_SERVER},
1480 {"KeyExpire", VAR_SERVER},
1481 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1482 {"LocalDiscovery", VAR_SERVER},
1483 {"MACExpire", VAR_SERVER},
1484 {"MaxConnectionBurst", VAR_SERVER},
1485 {"MaxOutputBufferSize", VAR_SERVER},
1486 {"MaxTimeout", VAR_SERVER},
1487 {"Mode", VAR_SERVER | VAR_SAFE},
1488 {"Name", VAR_SERVER},
1489 {"PingInterval", VAR_SERVER},
1490 {"PingTimeout", VAR_SERVER},
1491 {"PriorityInheritance", VAR_SERVER},
1492 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1493 {"PrivateKeyFile", VAR_SERVER},
1494 {"ProcessPriority", VAR_SERVER},
1495 {"Proxy", VAR_SERVER},
1496 {"ReplayWindow", VAR_SERVER},
1497 {"ScriptsExtension", VAR_SERVER},
1498 {"ScriptsInterpreter", VAR_SERVER},
1499 {"StrictSubnets", VAR_SERVER},
1500 {"TunnelServer", VAR_SERVER},
1501 {"UDPDiscovery", VAR_SERVER},
1502 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1503 {"UDPDiscoveryInterval", VAR_SERVER},
1504 {"UDPDiscoveryTimeout", VAR_SERVER},
1505 {"MTUInfoInterval", VAR_SERVER},
1506 {"UDPInfoInterval", VAR_SERVER},
1507 {"UDPRcvBuf", VAR_SERVER},
1508 {"UDPSndBuf", VAR_SERVER},
1509 {"UPnP", VAR_SERVER},
1510 {"UPnPDiscoverWait", VAR_SERVER},
1511 {"UPnPRefreshPeriod", VAR_SERVER},
1512 {"VDEGroup", VAR_SERVER},
1513 {"VDEPort", VAR_SERVER},
1514 /* Host configuration */
1515 {"Address", VAR_HOST | VAR_MULTIPLE},
1516 {"Cipher", VAR_SERVER | VAR_HOST},
1517 {"ClampMSS", VAR_SERVER | VAR_HOST},
1518 {"Compression", VAR_SERVER | VAR_HOST},
1519 {"Digest", VAR_SERVER | VAR_HOST},
1520 {"Ed25519PublicKey", VAR_HOST},
1521 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1522 {"IndirectData", VAR_SERVER | VAR_HOST},
1523 {"MACLength", VAR_SERVER | VAR_HOST},
1524 {"PMTU", VAR_SERVER | VAR_HOST},
1525 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1527 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1528 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1529 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1530 {"TCPOnly", VAR_SERVER | VAR_HOST},
1531 {"Weight", VAR_HOST | VAR_SAFE},
1535 static int cmd_config(int argc, char *argv[]) {
1537 fprintf(stderr, "Invalid number of arguments.\n");
1541 if(strcasecmp(argv[0], "config"))
1545 if(!strcasecmp(argv[1], "get")) {
1547 } else if(!strcasecmp(argv[1], "add")) {
1548 argv++, argc--, action = 1;
1549 } else if(!strcasecmp(argv[1], "del")) {
1550 argv++, argc--, action = -1;
1551 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1552 argv++, argc--, action = 0;
1556 fprintf(stderr, "Invalid number of arguments.\n");
1560 // Concatenate the rest of the command line
1561 strncpy(line, argv[1], sizeof line - 1);
1562 for(int i = 2; i < argc; i++) {
1563 strncat(line, " ", sizeof line - 1 - strlen(line));
1564 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1567 // Liberal parsing into node name, variable name and value.
1573 len = strcspn(line, "\t =");
1575 value += strspn(value, "\t ");
1578 value += strspn(value, "\t ");
1581 variable = strchr(line, '.');
1590 fprintf(stderr, "No variable given.\n");
1594 if(action >= 0 && !*value) {
1595 fprintf(stderr, "No value for variable given.\n");
1599 if(action < -1 && *value)
1602 /* Some simple checks. */
1604 bool warnonremove = false;
1606 for(int i = 0; variables[i].name; i++) {
1607 if(strcasecmp(variables[i].name, variable))
1611 variable = (char *)variables[i].name;
1613 /* Discourage use of obsolete variables. */
1615 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1617 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1619 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1624 /* Don't put server variables in host config files */
1626 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1628 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1630 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1635 /* Should this go into our own host config file? */
1637 if(!node && !(variables[i].type & VAR_SERVER)) {
1638 node = get_my_name(true);
1643 /* Change "add" into "set" for variables that do not allow multiple occurences.
1644 Turn on warnings when it seems variables might be removed unintentionally. */
1646 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1647 warnonremove = true;
1649 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1650 warnonremove = true;
1656 if(node && !check_id(node)) {
1657 fprintf(stderr, "Invalid name for node.\n");
1662 if(force || action < 0) {
1663 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1665 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1670 // Open the right configuration file.
1671 char filename[PATH_MAX];
1673 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1675 snprintf(filename, sizeof filename, "%s", tinc_conf);
1677 FILE *f = fopen(filename, "r");
1679 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1683 char tmpfile[PATH_MAX];
1687 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1688 tf = fopen(tmpfile, "w");
1690 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1696 // Copy the file, making modifications on the fly, unless we are just getting a value.
1700 bool removed = false;
1703 while(fgets(buf1, sizeof buf1, f)) {
1704 buf1[sizeof buf1 - 1] = 0;
1705 strncpy(buf2, buf1, sizeof buf2);
1707 // Parse line in a simple way
1711 len = strcspn(buf2, "\t =");
1712 bvalue = buf2 + len;
1713 bvalue += strspn(bvalue, "\t ");
1714 if(*bvalue == '=') {
1716 bvalue += strspn(bvalue, "\t ");
1722 if(!strcasecmp(buf2, variable)) {
1726 printf("%s\n", bvalue);
1728 } else if(action == -1) {
1729 if(!*value || !strcasecmp(bvalue, value)) {
1734 } else if(action == 0) {
1735 // Warn if "set" was used for variables that can occur multiple times
1736 if(warnonremove && strcasecmp(bvalue, value))
1737 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1739 // Already set? Delete the rest...
1743 // Otherwise, replace.
1744 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1745 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1751 } else if(action > 0) {
1752 // Check if we've already seen this variable with the same value
1753 if(!strcasecmp(bvalue, value))
1759 // Copy original line...
1760 if(fputs(buf1, tf) < 0) {
1761 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1765 // Add newline if it is missing...
1766 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1767 if(fputc('\n', tf) < 0) {
1768 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1775 // Make sure we read everything...
1776 if(ferror(f) || !feof(f)) {
1777 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1782 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1786 // Add new variable if necessary.
1787 if((action > 0 && !found)|| (action == 0 && !set)) {
1788 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1789 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1798 fprintf(stderr, "No matching configuration variables found.\n");
1803 // Make sure we wrote everything...
1805 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1809 // Could we find what we had to remove?
1810 if(action < 0 && !removed) {
1812 fprintf(stderr, "No configuration variables deleted.\n");
1816 // Replace the configuration file with the new one
1818 if(remove(filename)) {
1819 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1823 if(rename(tmpfile, filename)) {
1824 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1828 // Silently try notifying a running tincd of changes.
1829 if(connect_tincd(false))
1830 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1835 static bool try_bind(int port) {
1836 struct addrinfo *ai = NULL, *aip;
1837 struct addrinfo hint = {
1838 .ai_flags = AI_PASSIVE,
1839 .ai_family = AF_UNSPEC,
1840 .ai_socktype = SOCK_STREAM,
1841 .ai_protocol = IPPROTO_TCP,
1844 bool success = true;
1846 snprintf(portstr, sizeof portstr, "%d", port);
1848 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1851 for(aip = ai; aip; aip = aip->ai_next) {
1852 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1858 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1870 int check_port(char *name) {
1874 fprintf(stderr, "Warning: could not bind to port 655. ");
1876 for(int i = 0; i < 100; i++) {
1877 int port = 0x1000 + (rand() & 0x7fff);
1878 if(try_bind(port)) {
1879 char filename[PATH_MAX];
1880 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1881 FILE *f = fopen(filename, "a");
1883 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1884 fprintf(stderr, "Please change tinc's Port manually.\n");
1888 fprintf(f, "Port = %d\n", port);
1890 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1895 fprintf(stderr, "Please change tinc's Port manually.\n");
1899 static int cmd_init(int argc, char *argv[]) {
1900 if(!access(tinc_conf, F_OK)) {
1901 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1906 fprintf(stderr, "Too many arguments!\n");
1908 } else if(argc < 2) {
1911 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1912 if(!fgets(buf, sizeof buf, stdin)) {
1913 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1916 int len = rstrip(buf);
1918 fprintf(stderr, "No name given!\n");
1923 fprintf(stderr, "No Name given!\n");
1927 name = strdup(argv[1]);
1929 fprintf(stderr, "No Name given!\n");
1934 if(!check_id(name)) {
1935 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1939 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1940 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1944 if(mkdir(confbase, 0777) && errno != EEXIST) {
1945 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1949 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1950 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1954 FILE *f = fopen(tinc_conf, "w");
1956 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1960 fprintf(f, "Name = %s\n", name);
1963 #ifndef DISABLE_LEGACY
1964 if(!rsa_keygen(2048, false))
1968 if(!ed25519_keygen(false))
1974 char filename[PATH_MAX];
1975 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1976 if(access(filename, F_OK)) {
1977 FILE *f = fopenmask(filename, "w", 0777);
1979 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1982 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");
1991 static int cmd_generate_keys(int argc, char *argv[]) {
1992 #ifdef DISABLE_LEGACY
1997 fprintf(stderr, "Too many arguments!\n");
2002 name = get_my_name(false);
2004 #ifndef DISABLE_LEGACY
2005 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
2009 if(!ed25519_keygen(true))
2015 #ifndef DISABLE_LEGACY
2016 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2018 fprintf(stderr, "Too many arguments!\n");
2023 name = get_my_name(false);
2025 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2029 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2031 fprintf(stderr, "Too many arguments!\n");
2036 name = get_my_name(false);
2038 return !ed25519_keygen(true);
2041 static int cmd_help(int argc, char *argv[]) {
2046 static int cmd_version(int argc, char *argv[]) {
2048 fprintf(stderr, "Too many arguments!\n");
2056 static int cmd_info(int argc, char *argv[]) {
2058 fprintf(stderr, "Invalid number of arguments.\n");
2062 if(!connect_tincd(true))
2065 return info(fd, argv[1]);
2068 static const char *conffiles[] = {
2079 static int cmd_edit(int argc, char *argv[]) {
2081 fprintf(stderr, "Invalid number of arguments.\n");
2085 char filename[PATH_MAX] = "";
2087 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2088 for(int i = 0; conffiles[i]; i++) {
2089 if(!strcmp(argv[1], conffiles[i])) {
2090 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2099 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2100 char *dash = strchr(argv[1], '-');
2103 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2104 fprintf(stderr, "Invalid configuration filename.\n");
2112 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2114 xasprintf(&command, "edit \"%s\"", filename);
2116 int result = system(command);
2121 // Silently try notifying a running tincd of changes.
2122 if(connect_tincd(false))
2123 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2128 static int export(const char *name, FILE *out) {
2129 char filename[PATH_MAX];
2130 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2131 FILE *in = fopen(filename, "r");
2133 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2137 fprintf(out, "Name = %s\n", name);
2139 while(fgets(buf, sizeof buf, in)) {
2140 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2145 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2154 static int cmd_export(int argc, char *argv[]) {
2156 fprintf(stderr, "Too many arguments!\n");
2160 char *name = get_my_name(true);
2164 int result = export(name, stdout);
2172 static int cmd_export_all(int argc, char *argv[]) {
2174 fprintf(stderr, "Too many arguments!\n");
2178 DIR *dir = opendir(hosts_dir);
2180 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2188 while((ent = readdir(dir))) {
2189 if(!check_id(ent->d_name))
2195 printf("#---------------------------------------------------------------#\n");
2197 result |= export(ent->d_name, stdout);
2206 static int cmd_import(int argc, char *argv[]) {
2208 fprintf(stderr, "Too many arguments!\n");
2217 char filename[PATH_MAX] = "";
2219 bool firstline = true;
2221 while(fgets(buf, sizeof buf, in)) {
2222 if(sscanf(buf, "Name = %s", name) == 1) {
2225 if(!check_id(name)) {
2226 fprintf(stderr, "Invalid Name in input!\n");
2233 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2235 if(!force && !access(filename, F_OK)) {
2236 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2241 out = fopen(filename, "w");
2243 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2249 } else if(firstline) {
2250 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2255 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2259 if(fputs(buf, out) < 0) {
2260 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2270 fprintf(stderr, "Imported %d host configuration files.\n", count);
2273 fprintf(stderr, "No host configuration files imported.\n");
2278 static int cmd_exchange(int argc, char *argv[]) {
2279 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2282 static int cmd_exchange_all(int argc, char *argv[]) {
2283 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2286 static int switch_network(char *name) {
2298 free(unixsocketname);
2299 unixsocketname = NULL;
2305 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2307 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2308 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2309 xasprintf(&prompt, "%s> ", identname);
2314 static int cmd_network(int argc, char *argv[]) {
2316 fprintf(stderr, "Too many arguments!\n");
2321 return switch_network(argv[1]);
2323 DIR *dir = opendir(confdir);
2325 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2330 while((ent = readdir(dir))) {
2331 if(*ent->d_name == '.')
2334 if(!strcmp(ent->d_name, "tinc.conf")) {
2339 char fname[PATH_MAX];
2340 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2341 if(!access(fname, R_OK))
2342 printf("%s\n", ent->d_name);
2350 static int cmd_fsck(int argc, char *argv[]) {
2352 fprintf(stderr, "Too many arguments!\n");
2356 return fsck(orig_argv[0]);
2359 static void *readfile(FILE *in, size_t *len) {
2361 size_t alloced = 4096;
2362 char *buf = xmalloc(alloced);
2365 size_t read = fread(buf + count, 1, alloced - count, in);
2369 if(count >= alloced) {
2371 buf = xrealloc(buf, alloced);
2381 static int cmd_sign(int argc, char *argv[]) {
2383 fprintf(stderr, "Too many arguments!\n");
2388 name = get_my_name(true);
2393 char fname[PATH_MAX];
2394 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
2395 FILE *fp = fopen(fname, "r");
2397 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2401 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2404 fprintf(stderr, "Could not read private key from %s\n", fname);
2414 in = fopen(argv[1], "rb");
2416 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2425 char *data = readfile(in, &len);
2429 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2434 // Ensure we sign our name and current time as well
2435 long t = time(NULL);
2437 xasprintf(&trailer, " %s %ld", name, t);
2438 int trailer_len = strlen(trailer);
2440 data = xrealloc(data, len + trailer_len);
2441 memcpy(data + len, trailer, trailer_len);
2445 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2446 fprintf(stderr, "Error generating signature\n");
2451 b64encode(sig, sig, 64);
2454 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2455 fwrite(data, len, 1, stdout);
2461 static int cmd_verify(int argc, char *argv[]) {
2463 fprintf(stderr, "Not enough arguments!\n");
2468 fprintf(stderr, "Too many arguments!\n");
2472 char *node = argv[1];
2473 if(!strcmp(node, ".")) {
2475 name = get_my_name(true);
2480 } else if(!strcmp(node, "*")) {
2483 if(!check_id(node)) {
2484 fprintf(stderr, "Invalid node name\n");
2492 in = fopen(argv[2], "rb");
2494 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2502 char *data = readfile(in, &len);
2506 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2510 char *newline = memchr(data, '\n', len);
2511 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2512 fprintf(stderr, "Invalid input\n");
2518 char signer[MAX_STRING_SIZE] = "";
2519 char sig[MAX_STRING_SIZE] = "";
2522 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2523 fprintf(stderr, "Invalid input\n");
2527 if(node && strcmp(node, signer)) {
2528 fprintf(stderr, "Signature is not made by %s\n", node);
2536 xasprintf(&trailer, " %s %ld", signer, t);
2537 int trailer_len = strlen(trailer);
2539 data = xrealloc(data, len + trailer_len);
2540 memcpy(data + len, trailer, trailer_len);
2543 char fname[PATH_MAX];
2544 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, node);
2545 FILE *fp = fopen(fname, "r");
2547 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2552 ecdsa_t *key = get_pubkey(fp);
2555 key = ecdsa_read_pem_public_key(fp);
2558 fprintf(stderr, "Could not read public key from %s\n", fname);
2566 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2567 fprintf(stderr, "Invalid signature\n");
2575 fwrite(newline, len - (newline - data), 1, stdout);
2581 static const struct {
2582 const char *command;
2583 int (*function)(int argc, char *argv[]);
2586 {"start", cmd_start},
2588 {"restart", cmd_restart},
2589 {"reload", cmd_reload},
2592 {"purge", cmd_purge},
2593 {"debug", cmd_debug},
2594 {"retry", cmd_retry},
2595 {"connect", cmd_connect},
2596 {"disconnect", cmd_disconnect},
2601 {"config", cmd_config, true},
2602 {"add", cmd_config},
2603 {"del", cmd_config},
2604 {"get", cmd_config},
2605 {"set", cmd_config},
2607 {"generate-keys", cmd_generate_keys},
2608 #ifndef DISABLE_LEGACY
2609 {"generate-rsa-keys", cmd_generate_rsa_keys},
2611 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2613 {"version", cmd_version},
2616 {"export", cmd_export},
2617 {"export-all", cmd_export_all},
2618 {"import", cmd_import},
2619 {"exchange", cmd_exchange},
2620 {"exchange-all", cmd_exchange_all},
2621 {"invite", cmd_invite},
2623 {"network", cmd_network},
2626 {"verify", cmd_verify},
2630 #ifdef HAVE_READLINE
2631 static char *complete_command(const char *text, int state) {
2639 while(commands[i].command) {
2640 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2641 return xstrdup(commands[i].command);
2648 static char *complete_dump(const char *text, int state) {
2649 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2658 if(!strncasecmp(matches[i], text, strlen(text)))
2659 return xstrdup(matches[i]);
2666 static char *complete_config(const char *text, int state) {
2674 while(variables[i].name) {
2675 char *dot = strchr(text, '.');
2677 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2679 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2683 if(!strncasecmp(variables[i].name, text, strlen(text)))
2684 return xstrdup(variables[i].name);
2692 static char *complete_info(const char *text, int state) {
2696 if(!connect_tincd(false))
2698 // Check the list of nodes
2699 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2700 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2703 while(recvline(fd, line, sizeof line)) {
2705 int n = sscanf(line, "%d %d %s", &code, &req, item);
2715 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2719 if(!strncmp(item, text, strlen(text)))
2720 return xstrdup(strip_weight(item));
2726 static char *complete_nothing(const char *text, int state) {
2730 static char **completion (const char *text, int start, int end) {
2731 char **matches = NULL;
2734 matches = rl_completion_matches(text, complete_command);
2735 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2736 matches = rl_completion_matches(text, complete_dump);
2737 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2738 matches = rl_completion_matches(text, complete_config);
2739 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2740 matches = rl_completion_matches(text, complete_config);
2741 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2742 matches = rl_completion_matches(text, complete_config);
2743 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2744 matches = rl_completion_matches(text, complete_config);
2745 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2746 matches = rl_completion_matches(text, complete_info);
2752 static int cmd_shell(int argc, char *argv[]) {
2753 xasprintf(&prompt, "%s> ", identname);
2757 int maxargs = argc + 16;
2758 char **nargv = xmalloc(maxargs * sizeof *nargv);
2760 for(int i = 0; i < argc; i++)
2763 #ifdef HAVE_READLINE
2764 rl_readline_name = "tinc";
2765 rl_completion_entry_function = complete_nothing;
2766 rl_attempted_completion_function = completion;
2767 rl_filename_completion_desired = 0;
2772 #ifdef HAVE_READLINE
2776 rl_basic_word_break_characters = "\t\n ";
2777 line = readline(prompt);
2779 copy = xstrdup(line);
2781 line = fgets(buf, sizeof buf, stdin);
2785 fputs(prompt, stdout);
2787 line = fgets(buf, sizeof buf, stdin);
2793 /* Ignore comments */
2801 char *p = line + strspn(line, " \t\n");
2802 char *next = strtok(p, " \t\n");
2805 if(nargc >= maxargs) {
2806 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2809 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2814 next = strtok(NULL, " \t\n");
2820 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2825 for(int i = 0; commands[i].command; i++) {
2826 if(!strcasecmp(nargv[argc], commands[i].command)) {
2827 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2833 #ifdef HAVE_READLINE
2839 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2852 int main(int argc, char *argv[]) {
2853 program_name = argv[0];
2856 tty = isatty(0) && isatty(1);
2858 if(!parse_options(argc, argv))
2862 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2863 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2876 static struct WSAData wsa_state;
2878 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2879 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2888 return cmd_shell(argc, argv);
2890 for(int i = 0; commands[i].command; i++) {
2891 if(!strcasecmp(argv[optind], commands[i].command))
2892 return commands[i].function(argc - optind, argv + optind);
2895 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);