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);
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);
721 if ((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
722 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
723 /* clean up the stale socket and pid file */
725 unlink(unixsocketname);
730 struct sockaddr_un sa;
731 sa.sun_family = AF_UNIX;
732 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
734 fd = socket(AF_UNIX, SOCK_STREAM, 0);
737 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
741 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
743 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
749 struct addrinfo hints = {
750 .ai_family = AF_UNSPEC,
751 .ai_socktype = SOCK_STREAM,
752 .ai_protocol = IPPROTO_TCP,
756 struct addrinfo *res = NULL;
758 if(getaddrinfo(host, port, &hints, &res) || !res) {
760 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
764 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
767 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
772 unsigned long arg = 0;
774 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
776 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
780 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
782 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
792 static const int one = 1;
793 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
799 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
801 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
807 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
809 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
811 fprintf(stderr, "Could not fully establish control socket connection\n");
821 static int cmd_start(int argc, char *argv[]) {
822 if(connect_tincd(false)) {
824 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
826 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
831 char *slash = strrchr(program_name, '/');
834 if ((c = strrchr(program_name, '\\')) > slash)
839 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
844 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
849 Windows has no real concept of an "argv array". A command line is just one string.
850 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
851 it uses quotes to handle spaces in arguments.
852 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
853 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
854 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
856 xasprintf(&arg0, "\"%s\"", arg0);
858 nargv[nargc++] = arg0;
859 for(int i = 1; i < optind; i++)
860 nargv[nargc++] = orig_argv[i];
861 for(int i = 1; i < argc; i++)
862 nargv[nargc++] = argv[i];
865 int status = spawnvp(_P_WAIT, c, nargv);
867 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
872 int pfd[2] = {-1, -1};
873 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
874 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
881 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
889 snprintf(buf, sizeof buf, "%d", pfd[1]);
890 setenv("TINC_UMBILICAL", buf, true);
891 exit(execvp(c, nargv));
898 int status = -1, result;
900 signal(SIGINT, SIG_IGN);
903 // Pass all log messages from the umbilical to stderr.
904 // A nul-byte right before closure means tincd started succesfully.
909 while((len = read(pfd[0], buf, sizeof buf)) > 0) {
910 failure = buf[len - 1];
921 // Make sure the child process is really gone.
922 result = waitpid(pid, &status, 0);
925 signal(SIGINT, SIG_DFL);
928 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
929 fprintf(stderr, "Error starting %s\n", c);
937 static int cmd_stop(int argc, char *argv[]) {
939 fprintf(stderr, "Too many arguments!\n");
944 if(!connect_tincd(true)) {
946 if(kill(pid, SIGTERM)) {
947 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
951 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
952 waitpid(pid, NULL, 0);
959 sendline(fd, "%d %d", CONTROL, REQ_STOP);
961 while(recvline(fd, line, sizeof line)) {
962 // Wait for tincd to close the connection...
965 if(!remove_service())
975 static int cmd_restart(int argc, char *argv[]) {
977 return cmd_start(argc, argv);
980 static int cmd_reload(int argc, char *argv[]) {
982 fprintf(stderr, "Too many arguments!\n");
986 if(!connect_tincd(true))
989 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
990 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
991 fprintf(stderr, "Could not reload configuration.\n");
999 static int dump_invitations(void) {
1000 char dname[PATH_MAX];
1001 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
1002 DIR *dir = opendir(dname);
1004 if(errno == ENOENT) {
1005 fprintf(stderr, "No outstanding invitations.\n");
1009 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1016 while((ent = readdir(dir))) {
1017 char buf[MAX_STRING_SIZE];
1018 if(b64decode(ent->d_name, buf, 24) != 18)
1021 char fname[PATH_MAX];
1022 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
1023 FILE *f = fopen(fname, "r");
1025 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1031 if(!fgets(buf, sizeof buf, f)) {
1032 fprintf(stderr, "Invalid invitation file %s", fname);
1038 char *eol = buf + strlen(buf);
1039 while(strchr("\t \r\n", *--eol))
1041 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1042 fprintf(stderr, "Invalid invitation file %s", fname);
1047 printf("%s %s\n", ent->d_name, buf + 7);
1053 fprintf(stderr, "No outstanding invitations.\n");
1058 static int cmd_dump(int argc, char *argv[]) {
1059 bool only_reachable = false;
1061 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1062 if(strcasecmp(argv[2], "nodes")) {
1063 fprintf(stderr, "`reachable' only supported for nodes.\n");
1067 only_reachable = true;
1073 fprintf(stderr, "Invalid number of arguments.\n");
1078 if(!strcasecmp(argv[1], "invitations"))
1079 return dump_invitations();
1081 if(!connect_tincd(true))
1086 if(!strcasecmp(argv[1], "nodes"))
1087 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1088 else if(!strcasecmp(argv[1], "edges"))
1089 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1090 else if(!strcasecmp(argv[1], "subnets"))
1091 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1092 else if(!strcasecmp(argv[1], "connections"))
1093 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1094 else if(!strcasecmp(argv[1], "graph")) {
1095 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1096 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1098 } else if(!strcasecmp(argv[1], "digraph")) {
1099 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1100 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1103 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1109 printf("graph {\n");
1110 else if(do_graph == 2)
1111 printf("digraph {\n");
1113 while(recvline(fd, line, sizeof line)) {
1114 char node1[4096], node2[4096];
1115 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1117 if(do_graph && req == REQ_DUMP_NODES)
1135 char local_host[4096];
1136 char local_port[4096];
1139 int cipher, digest, maclength, compression, distance, socket, weight;
1140 short int pmtu, minmtu, maxmtu;
1141 unsigned int options, status_int;
1142 node_status_t status;
1143 long int last_state_change;
1146 case REQ_DUMP_NODES: {
1147 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);
1149 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1153 memcpy(&status, &status_int, sizeof status);
1156 const char *color = "black";
1157 if(!strcmp(host, "MYSELF"))
1159 else if(!status.reachable)
1161 else if(strcmp(via, node))
1163 else if(!status.validkey)
1167 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1169 if(only_reachable && !status.reachable)
1171 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",
1172 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1176 case REQ_DUMP_EDGES: {
1177 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);
1179 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1184 float w = 1 + 65536.0 / weight;
1185 if(do_graph == 1 && strcmp(node1, node2) > 0)
1186 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1187 else if(do_graph == 2)
1188 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1190 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);
1194 case REQ_DUMP_SUBNETS: {
1195 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1197 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1200 printf("%s owner %s\n", strip_weight(subnet), node);
1203 case REQ_DUMP_CONNECTIONS: {
1204 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1206 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1209 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1213 fprintf(stderr, "Unable to parse dump from tincd.\n");
1218 fprintf(stderr, "Error receiving dump.\n");
1222 static int cmd_purge(int argc, char *argv[]) {
1224 fprintf(stderr, "Too many arguments!\n");
1228 if(!connect_tincd(true))
1231 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1232 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1233 fprintf(stderr, "Could not purge old information.\n");
1240 static int cmd_debug(int argc, char *argv[]) {
1242 fprintf(stderr, "Invalid number of arguments.\n");
1246 if(!connect_tincd(true))
1249 int debuglevel = atoi(argv[1]);
1252 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1253 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1254 fprintf(stderr, "Could not set debug level.\n");
1258 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1262 static int cmd_retry(int argc, char *argv[]) {
1264 fprintf(stderr, "Too many arguments!\n");
1268 if(!connect_tincd(true))
1271 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1272 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1273 fprintf(stderr, "Could not retry outgoing connections.\n");
1280 static int cmd_connect(int argc, char *argv[]) {
1282 fprintf(stderr, "Invalid number of arguments.\n");
1286 if(!check_id(argv[1])) {
1287 fprintf(stderr, "Invalid name for node.\n");
1291 if(!connect_tincd(true))
1294 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1295 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1296 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1303 static int cmd_disconnect(int argc, char *argv[]) {
1305 fprintf(stderr, "Invalid number of arguments.\n");
1309 if(!check_id(argv[1])) {
1310 fprintf(stderr, "Invalid name for node.\n");
1314 if(!connect_tincd(true))
1317 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1318 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1319 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1326 static int cmd_top(int argc, char *argv[]) {
1328 fprintf(stderr, "Too many arguments!\n");
1333 if(!connect_tincd(true))
1339 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1344 static int cmd_pcap(int argc, char *argv[]) {
1346 fprintf(stderr, "Too many arguments!\n");
1350 if(!connect_tincd(true))
1353 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1358 static void sigint_handler(int sig) {
1359 fprintf(stderr, "\n");
1360 shutdown(fd, SHUT_RDWR);
1364 static int cmd_log(int argc, char *argv[]) {
1366 fprintf(stderr, "Too many arguments!\n");
1370 if(!connect_tincd(true))
1374 signal(SIGINT, sigint_handler);
1377 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1380 signal(SIGINT, SIG_DFL);
1388 static int cmd_pid(int argc, char *argv[]) {
1390 fprintf(stderr, "Too many arguments!\n");
1394 if(!connect_tincd(true) || !pid)
1397 printf("%d\n", pid);
1401 int rstrip(char *value) {
1402 int len = strlen(value);
1403 while(len && strchr("\t\r\n ", value[len - 1]))
1408 char *get_my_name(bool verbose) {
1409 FILE *f = fopen(tinc_conf, "r");
1412 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1418 while(fgets(buf, sizeof buf, f)) {
1419 int len = strcspn(buf, "\t =");
1421 value += strspn(value, "\t ");
1424 value += strspn(value, "\t ");
1429 if(strcasecmp(buf, "Name"))
1433 return replace_name(value);
1439 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1443 ecdsa_t *get_pubkey(FILE *f) {
1446 while(fgets(buf, sizeof buf, f)) {
1447 int len = strcspn(buf, "\t =");
1449 value += strspn(value, "\t ");
1452 value += strspn(value, "\t ");
1457 if(strcasecmp(buf, "Ed25519PublicKey"))
1460 return ecdsa_set_base64_public_key(value);
1466 const var_t variables[] = {
1467 /* Server configuration */
1468 {"AddressFamily", VAR_SERVER},
1469 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1470 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1471 {"BindToInterface", VAR_SERVER},
1472 {"Broadcast", VAR_SERVER | VAR_SAFE},
1473 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1474 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1475 {"DecrementTTL", VAR_SERVER},
1476 {"Device", VAR_SERVER},
1477 {"DeviceStandby", VAR_SERVER},
1478 {"DeviceType", VAR_SERVER},
1479 {"DirectOnly", VAR_SERVER},
1480 {"Ed25519PrivateKeyFile", VAR_SERVER},
1481 {"ExperimentalProtocol", VAR_SERVER},
1482 {"Forwarding", VAR_SERVER},
1483 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1484 {"Hostnames", VAR_SERVER},
1485 {"IffOneQueue", VAR_SERVER},
1486 {"Interface", VAR_SERVER},
1487 {"KeyExpire", VAR_SERVER},
1488 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1489 {"LocalDiscovery", VAR_SERVER},
1490 {"MACExpire", VAR_SERVER},
1491 {"MaxConnectionBurst", VAR_SERVER},
1492 {"MaxOutputBufferSize", VAR_SERVER},
1493 {"MaxTimeout", VAR_SERVER},
1494 {"Mode", VAR_SERVER | VAR_SAFE},
1495 {"Name", VAR_SERVER},
1496 {"PingInterval", VAR_SERVER},
1497 {"PingTimeout", VAR_SERVER},
1498 {"PriorityInheritance", VAR_SERVER},
1499 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1500 {"PrivateKeyFile", VAR_SERVER},
1501 {"ProcessPriority", VAR_SERVER},
1502 {"Proxy", VAR_SERVER},
1503 {"ReplayWindow", VAR_SERVER},
1504 {"ScriptsExtension", VAR_SERVER},
1505 {"ScriptsInterpreter", VAR_SERVER},
1506 {"StrictSubnets", VAR_SERVER},
1507 {"TunnelServer", VAR_SERVER},
1508 {"UDPDiscovery", VAR_SERVER},
1509 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1510 {"UDPDiscoveryInterval", VAR_SERVER},
1511 {"UDPDiscoveryTimeout", VAR_SERVER},
1512 {"MTUInfoInterval", VAR_SERVER},
1513 {"UDPInfoInterval", VAR_SERVER},
1514 {"UDPRcvBuf", VAR_SERVER},
1515 {"UDPSndBuf", VAR_SERVER},
1516 {"UPnP", VAR_SERVER},
1517 {"UPnPDiscoverWait", VAR_SERVER},
1518 {"UPnPRefreshPeriod", VAR_SERVER},
1519 {"VDEGroup", VAR_SERVER},
1520 {"VDEPort", VAR_SERVER},
1521 /* Host configuration */
1522 {"Address", VAR_HOST | VAR_MULTIPLE},
1523 {"Cipher", VAR_SERVER | VAR_HOST},
1524 {"ClampMSS", VAR_SERVER | VAR_HOST},
1525 {"Compression", VAR_SERVER | VAR_HOST},
1526 {"Digest", VAR_SERVER | VAR_HOST},
1527 {"Ed25519PublicKey", VAR_HOST},
1528 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1529 {"IndirectData", VAR_SERVER | VAR_HOST},
1530 {"MACLength", VAR_SERVER | VAR_HOST},
1531 {"PMTU", VAR_SERVER | VAR_HOST},
1532 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1534 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1535 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1536 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1537 {"TCPOnly", VAR_SERVER | VAR_HOST},
1538 {"Weight", VAR_HOST | VAR_SAFE},
1542 static int cmd_config(int argc, char *argv[]) {
1544 fprintf(stderr, "Invalid number of arguments.\n");
1548 if(strcasecmp(argv[0], "config"))
1552 if(!strcasecmp(argv[1], "get")) {
1554 } else if(!strcasecmp(argv[1], "add")) {
1555 argv++, argc--, action = 1;
1556 } else if(!strcasecmp(argv[1], "del")) {
1557 argv++, argc--, action = -1;
1558 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1559 argv++, argc--, action = 0;
1563 fprintf(stderr, "Invalid number of arguments.\n");
1567 // Concatenate the rest of the command line
1568 strncpy(line, argv[1], sizeof line - 1);
1569 for(int i = 2; i < argc; i++) {
1570 strncat(line, " ", sizeof line - 1 - strlen(line));
1571 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1574 // Liberal parsing into node name, variable name and value.
1580 len = strcspn(line, "\t =");
1582 value += strspn(value, "\t ");
1585 value += strspn(value, "\t ");
1588 variable = strchr(line, '.');
1597 fprintf(stderr, "No variable given.\n");
1601 if(action >= 0 && !*value) {
1602 fprintf(stderr, "No value for variable given.\n");
1606 if(action < -1 && *value)
1609 /* Some simple checks. */
1611 bool warnonremove = false;
1613 for(int i = 0; variables[i].name; i++) {
1614 if(strcasecmp(variables[i].name, variable))
1618 variable = (char *)variables[i].name;
1620 /* Discourage use of obsolete variables. */
1622 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1624 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1626 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1631 /* Don't put server variables in host config files */
1633 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1635 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1637 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1642 /* Should this go into our own host config file? */
1644 if(!node && !(variables[i].type & VAR_SERVER)) {
1645 node = get_my_name(true);
1650 /* Change "add" into "set" for variables that do not allow multiple occurences.
1651 Turn on warnings when it seems variables might be removed unintentionally. */
1653 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1654 warnonremove = true;
1656 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1657 warnonremove = true;
1663 if(node && !check_id(node)) {
1664 fprintf(stderr, "Invalid name for node.\n");
1669 if(force || action < 0) {
1670 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1672 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1677 // Open the right configuration file.
1678 char filename[PATH_MAX];
1680 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1682 snprintf(filename, sizeof filename, "%s", tinc_conf);
1684 FILE *f = fopen(filename, "r");
1686 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1690 char tmpfile[PATH_MAX];
1694 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1695 tf = fopen(tmpfile, "w");
1697 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1703 // Copy the file, making modifications on the fly, unless we are just getting a value.
1707 bool removed = false;
1710 while(fgets(buf1, sizeof buf1, f)) {
1711 buf1[sizeof buf1 - 1] = 0;
1712 strncpy(buf2, buf1, sizeof buf2);
1714 // Parse line in a simple way
1718 len = strcspn(buf2, "\t =");
1719 bvalue = buf2 + len;
1720 bvalue += strspn(bvalue, "\t ");
1721 if(*bvalue == '=') {
1723 bvalue += strspn(bvalue, "\t ");
1729 if(!strcasecmp(buf2, variable)) {
1733 printf("%s\n", bvalue);
1735 } else if(action == -1) {
1736 if(!*value || !strcasecmp(bvalue, value)) {
1741 } else if(action == 0) {
1742 // Warn if "set" was used for variables that can occur multiple times
1743 if(warnonremove && strcasecmp(bvalue, value))
1744 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1746 // Already set? Delete the rest...
1750 // Otherwise, replace.
1751 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1752 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1758 } else if(action > 0) {
1759 // Check if we've already seen this variable with the same value
1760 if(!strcasecmp(bvalue, value))
1766 // Copy original line...
1767 if(fputs(buf1, tf) < 0) {
1768 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1772 // Add newline if it is missing...
1773 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1774 if(fputc('\n', tf) < 0) {
1775 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1782 // Make sure we read everything...
1783 if(ferror(f) || !feof(f)) {
1784 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1789 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1793 // Add new variable if necessary.
1794 if((action > 0 && !found)|| (action == 0 && !set)) {
1795 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1796 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1805 fprintf(stderr, "No matching configuration variables found.\n");
1810 // Make sure we wrote everything...
1812 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1816 // Could we find what we had to remove?
1817 if(action < 0 && !removed) {
1819 fprintf(stderr, "No configuration variables deleted.\n");
1823 // Replace the configuration file with the new one
1825 if(remove(filename)) {
1826 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1830 if(rename(tmpfile, filename)) {
1831 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1835 // Silently try notifying a running tincd of changes.
1836 if(connect_tincd(false))
1837 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1842 static bool try_bind(int port) {
1843 struct addrinfo *ai = NULL, *aip;
1844 struct addrinfo hint = {
1845 .ai_flags = AI_PASSIVE,
1846 .ai_family = AF_UNSPEC,
1847 .ai_socktype = SOCK_STREAM,
1848 .ai_protocol = IPPROTO_TCP,
1851 bool success = true;
1853 snprintf(portstr, sizeof portstr, "%d", port);
1855 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1858 for(aip = ai; aip; aip = aip->ai_next) {
1859 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1865 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1877 int check_port(char *name) {
1881 fprintf(stderr, "Warning: could not bind to port 655. ");
1883 for(int i = 0; i < 100; i++) {
1884 int port = 0x1000 + (rand() & 0x7fff);
1885 if(try_bind(port)) {
1886 char filename[PATH_MAX];
1887 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1888 FILE *f = fopen(filename, "a");
1890 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1891 fprintf(stderr, "Please change tinc's Port manually.\n");
1895 fprintf(f, "Port = %d\n", port);
1897 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1902 fprintf(stderr, "Please change tinc's Port manually.\n");
1906 static int cmd_init(int argc, char *argv[]) {
1907 if(!access(tinc_conf, F_OK)) {
1908 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1913 fprintf(stderr, "Too many arguments!\n");
1915 } else if(argc < 2) {
1918 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1919 if(!fgets(buf, sizeof buf, stdin)) {
1920 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1923 int len = rstrip(buf);
1925 fprintf(stderr, "No name given!\n");
1930 fprintf(stderr, "No Name given!\n");
1934 name = strdup(argv[1]);
1936 fprintf(stderr, "No Name given!\n");
1941 if(!check_id(name)) {
1942 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1946 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1947 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1951 if(mkdir(confbase, 0777) && errno != EEXIST) {
1952 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1956 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1957 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1961 FILE *f = fopen(tinc_conf, "w");
1963 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1967 fprintf(f, "Name = %s\n", name);
1970 #ifndef DISABLE_LEGACY
1971 if(!rsa_keygen(2048, false))
1975 if(!ed25519_keygen(false))
1981 char filename[PATH_MAX];
1982 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1983 if(access(filename, F_OK)) {
1984 FILE *f = fopenmask(filename, "w", 0777);
1986 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1989 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");
1998 static int cmd_generate_keys(int argc, char *argv[]) {
1999 #ifdef DISABLE_LEGACY
2004 fprintf(stderr, "Too many arguments!\n");
2009 name = get_my_name(false);
2011 #ifndef DISABLE_LEGACY
2012 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
2016 if(!ed25519_keygen(true))
2022 #ifndef DISABLE_LEGACY
2023 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2025 fprintf(stderr, "Too many arguments!\n");
2030 name = get_my_name(false);
2032 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2036 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2038 fprintf(stderr, "Too many arguments!\n");
2043 name = get_my_name(false);
2045 return !ed25519_keygen(true);
2048 static int cmd_help(int argc, char *argv[]) {
2053 static int cmd_version(int argc, char *argv[]) {
2055 fprintf(stderr, "Too many arguments!\n");
2063 static int cmd_info(int argc, char *argv[]) {
2065 fprintf(stderr, "Invalid number of arguments.\n");
2069 if(!connect_tincd(true))
2072 return info(fd, argv[1]);
2075 static const char *conffiles[] = {
2086 static int cmd_edit(int argc, char *argv[]) {
2088 fprintf(stderr, "Invalid number of arguments.\n");
2092 char filename[PATH_MAX] = "";
2094 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2095 for(int i = 0; conffiles[i]; i++) {
2096 if(!strcmp(argv[1], conffiles[i])) {
2097 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2106 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2107 char *dash = strchr(argv[1], '-');
2110 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2111 fprintf(stderr, "Invalid configuration filename.\n");
2119 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2121 xasprintf(&command, "edit \"%s\"", filename);
2123 int result = system(command);
2128 // Silently try notifying a running tincd of changes.
2129 if(connect_tincd(false))
2130 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2135 static int export(const char *name, FILE *out) {
2136 char filename[PATH_MAX];
2137 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2138 FILE *in = fopen(filename, "r");
2140 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2144 fprintf(out, "Name = %s\n", name);
2146 while(fgets(buf, sizeof buf, in)) {
2147 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2152 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2161 static int cmd_export(int argc, char *argv[]) {
2163 fprintf(stderr, "Too many arguments!\n");
2167 char *name = get_my_name(true);
2171 int result = export(name, stdout);
2179 static int cmd_export_all(int argc, char *argv[]) {
2181 fprintf(stderr, "Too many arguments!\n");
2185 DIR *dir = opendir(hosts_dir);
2187 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2195 while((ent = readdir(dir))) {
2196 if(!check_id(ent->d_name))
2202 printf("#---------------------------------------------------------------#\n");
2204 result |= export(ent->d_name, stdout);
2213 static int cmd_import(int argc, char *argv[]) {
2215 fprintf(stderr, "Too many arguments!\n");
2224 char filename[PATH_MAX] = "";
2226 bool firstline = true;
2228 while(fgets(buf, sizeof buf, in)) {
2229 if(sscanf(buf, "Name = %s", name) == 1) {
2232 if(!check_id(name)) {
2233 fprintf(stderr, "Invalid Name in input!\n");
2240 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2242 if(!force && !access(filename, F_OK)) {
2243 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2248 out = fopen(filename, "w");
2250 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2256 } else if(firstline) {
2257 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2262 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2266 if(fputs(buf, out) < 0) {
2267 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2277 fprintf(stderr, "Imported %d host configuration files.\n", count);
2280 fprintf(stderr, "No host configuration files imported.\n");
2285 static int cmd_exchange(int argc, char *argv[]) {
2286 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2289 static int cmd_exchange_all(int argc, char *argv[]) {
2290 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2293 static int switch_network(char *name) {
2294 if(strcmp(name, ".")) {
2295 if(!check_netname(name, false)) {
2296 fprintf(stderr, "Invalid character in netname!\n");
2300 if(!check_netname(name, true))
2301 fprintf(stderr, "Warning: unsafe character in netname!\n");
2310 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2317 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2318 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2319 xasprintf(&prompt, "%s> ", identname);
2324 static int cmd_network(int argc, char *argv[]) {
2326 fprintf(stderr, "Too many arguments!\n");
2331 return switch_network(argv[1]);
2333 DIR *dir = opendir(confdir);
2335 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2340 while((ent = readdir(dir))) {
2341 if(*ent->d_name == '.')
2344 if(!strcmp(ent->d_name, "tinc.conf")) {
2349 char fname[PATH_MAX];
2350 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2351 if(!access(fname, R_OK))
2352 printf("%s\n", ent->d_name);
2360 static int cmd_fsck(int argc, char *argv[]) {
2362 fprintf(stderr, "Too many arguments!\n");
2366 return fsck(orig_argv[0]);
2369 static void *readfile(FILE *in, size_t *len) {
2371 size_t alloced = 4096;
2372 char *buf = xmalloc(alloced);
2375 size_t read = fread(buf + count, 1, alloced - count, in);
2379 if(count >= alloced) {
2381 buf = xrealloc(buf, alloced);
2391 static int cmd_sign(int argc, char *argv[]) {
2393 fprintf(stderr, "Too many arguments!\n");
2398 name = get_my_name(true);
2403 char fname[PATH_MAX];
2404 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
2405 FILE *fp = fopen(fname, "r");
2407 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2411 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2414 fprintf(stderr, "Could not read private key from %s\n", fname);
2424 in = fopen(argv[1], "rb");
2426 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2435 char *data = readfile(in, &len);
2439 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2444 // Ensure we sign our name and current time as well
2445 long t = time(NULL);
2447 xasprintf(&trailer, " %s %ld", name, t);
2448 int trailer_len = strlen(trailer);
2450 data = xrealloc(data, len + trailer_len);
2451 memcpy(data + len, trailer, trailer_len);
2455 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2456 fprintf(stderr, "Error generating signature\n");
2461 b64encode(sig, sig, 64);
2464 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2465 fwrite(data, len, 1, stdout);
2471 static int cmd_verify(int argc, char *argv[]) {
2473 fprintf(stderr, "Not enough arguments!\n");
2478 fprintf(stderr, "Too many arguments!\n");
2482 char *node = argv[1];
2483 if(!strcmp(node, ".")) {
2485 name = get_my_name(true);
2490 } else if(!strcmp(node, "*")) {
2493 if(!check_id(node)) {
2494 fprintf(stderr, "Invalid node name\n");
2502 in = fopen(argv[2], "rb");
2504 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2512 char *data = readfile(in, &len);
2516 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2520 char *newline = memchr(data, '\n', len);
2521 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2522 fprintf(stderr, "Invalid input\n");
2527 size_t skip = newline - data;
2529 char signer[MAX_STRING_SIZE] = "";
2530 char sig[MAX_STRING_SIZE] = "";
2533 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2534 fprintf(stderr, "Invalid input\n");
2538 if(node && strcmp(node, signer)) {
2539 fprintf(stderr, "Signature is not made by %s\n", node);
2547 xasprintf(&trailer, " %s %ld", signer, t);
2548 int trailer_len = strlen(trailer);
2550 data = xrealloc(data, len + trailer_len);
2551 memcpy(data + len, trailer, trailer_len);
2554 newline = data + skip;
2556 char fname[PATH_MAX];
2557 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, node);
2558 FILE *fp = fopen(fname, "r");
2560 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2565 ecdsa_t *key = get_pubkey(fp);
2568 key = ecdsa_read_pem_public_key(fp);
2571 fprintf(stderr, "Could not read public key from %s\n", fname);
2579 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2580 fprintf(stderr, "Invalid signature\n");
2588 fwrite(newline, len - (newline - data), 1, stdout);
2594 static const struct {
2595 const char *command;
2596 int (*function)(int argc, char *argv[]);
2599 {"start", cmd_start},
2601 {"restart", cmd_restart},
2602 {"reload", cmd_reload},
2605 {"purge", cmd_purge},
2606 {"debug", cmd_debug},
2607 {"retry", cmd_retry},
2608 {"connect", cmd_connect},
2609 {"disconnect", cmd_disconnect},
2614 {"config", cmd_config, true},
2615 {"add", cmd_config},
2616 {"del", cmd_config},
2617 {"get", cmd_config},
2618 {"set", cmd_config},
2620 {"generate-keys", cmd_generate_keys},
2621 #ifndef DISABLE_LEGACY
2622 {"generate-rsa-keys", cmd_generate_rsa_keys},
2624 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2626 {"version", cmd_version},
2629 {"export", cmd_export},
2630 {"export-all", cmd_export_all},
2631 {"import", cmd_import},
2632 {"exchange", cmd_exchange},
2633 {"exchange-all", cmd_exchange_all},
2634 {"invite", cmd_invite},
2636 {"network", cmd_network},
2639 {"verify", cmd_verify},
2643 #ifdef HAVE_READLINE
2644 static char *complete_command(const char *text, int state) {
2652 while(commands[i].command) {
2653 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2654 return xstrdup(commands[i].command);
2661 static char *complete_dump(const char *text, int state) {
2662 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2671 if(!strncasecmp(matches[i], text, strlen(text)))
2672 return xstrdup(matches[i]);
2679 static char *complete_config(const char *text, int state) {
2687 while(variables[i].name) {
2688 char *dot = strchr(text, '.');
2690 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2692 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2696 if(!strncasecmp(variables[i].name, text, strlen(text)))
2697 return xstrdup(variables[i].name);
2705 static char *complete_info(const char *text, int state) {
2709 if(!connect_tincd(false))
2711 // Check the list of nodes
2712 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2713 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2716 while(recvline(fd, line, sizeof line)) {
2718 int n = sscanf(line, "%d %d %s", &code, &req, item);
2728 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2732 if(!strncmp(item, text, strlen(text)))
2733 return xstrdup(strip_weight(item));
2739 static char *complete_nothing(const char *text, int state) {
2743 static char **completion (const char *text, int start, int end) {
2744 char **matches = NULL;
2747 matches = rl_completion_matches(text, complete_command);
2748 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2749 matches = rl_completion_matches(text, complete_dump);
2750 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2751 matches = rl_completion_matches(text, complete_config);
2752 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2753 matches = rl_completion_matches(text, complete_config);
2754 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2755 matches = rl_completion_matches(text, complete_config);
2756 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2757 matches = rl_completion_matches(text, complete_config);
2758 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2759 matches = rl_completion_matches(text, complete_info);
2765 static int cmd_shell(int argc, char *argv[]) {
2766 xasprintf(&prompt, "%s> ", identname);
2770 int maxargs = argc + 16;
2771 char **nargv = xmalloc(maxargs * sizeof *nargv);
2773 for(int i = 0; i < argc; i++)
2776 #ifdef HAVE_READLINE
2777 rl_readline_name = "tinc";
2778 rl_completion_entry_function = complete_nothing;
2779 rl_attempted_completion_function = completion;
2780 rl_filename_completion_desired = 0;
2785 #ifdef HAVE_READLINE
2789 rl_basic_word_break_characters = "\t\n ";
2790 line = readline(prompt);
2792 copy = xstrdup(line);
2794 line = fgets(buf, sizeof buf, stdin);
2798 fputs(prompt, stdout);
2800 line = fgets(buf, sizeof buf, stdin);
2806 /* Ignore comments */
2814 char *p = line + strspn(line, " \t\n");
2815 char *next = strtok(p, " \t\n");
2818 if(nargc >= maxargs) {
2819 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2822 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2827 next = strtok(NULL, " \t\n");
2833 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2838 for(int i = 0; commands[i].command; i++) {
2839 if(!strcasecmp(nargv[argc], commands[i].command)) {
2840 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2846 #ifdef HAVE_READLINE
2852 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2865 int main(int argc, char *argv[]) {
2866 program_name = argv[0];
2869 tty = isatty(0) && isatty(1);
2871 if(!parse_options(argc, argv))
2875 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2876 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2889 static struct WSAData wsa_state;
2891 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2892 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2901 return cmd_shell(argc, argv);
2903 for(int i = 0; commands[i].command; i++) {
2904 if(!strcasecmp(argv[optind], commands[i].command))
2905 return commands[i].function(argc - optind, argv + optind);
2908 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);