2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2022 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.
23 #include "readline/readline.h"
24 #include "readline/history.h"
29 #include "control_common.h"
34 #include "invitation.h"
45 #define MSG_NOSIGNAL 0
48 static char **orig_argv;
50 /* If nonzero, display usage information and exit. */
51 static bool show_help = false;
53 /* If nonzero, print the version on standard output and exit. */
54 static bool show_version = false;
56 static char *name = NULL;
57 static char controlcookie[1025];
58 char *tinc_conf = NULL;
59 char *hosts_dir = NULL;
62 // Horrible global variables...
71 bool confbasegiven = false;
72 char *scriptinterpreter = NULL;
73 static char defaultextension[] = "";
74 char *scriptextension = defaultextension;
80 static struct option const long_options[] = {
81 {"batch", no_argument, NULL, 'b'},
82 {"config", required_argument, NULL, 'c'},
83 {"net", required_argument, NULL, 'n'},
84 {"help", no_argument, NULL, 1},
85 {"version", no_argument, NULL, 2},
86 {"pidfile", required_argument, NULL, 3},
87 {"force", no_argument, NULL, 4},
91 static void version(void) {
92 static const char *message =
93 "%s version %s (built %s %s, protocol %d.%d)\n"
101 #ifndef DISABLE_LEGACY
105 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
106 "See the AUTHORS file for a complete list.\n"
108 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
109 "and you are welcome to redistribute it under certain conditions;\n"
110 "see the file COPYING for details.\n";
112 printf(message, PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
115 static void usage(bool status) {
117 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
119 static const char *message =
120 "Usage: %s [options] command\n"
122 "Valid options are:\n"
123 " -b, --batch Don't ask for anything (non-interactive mode).\n"
124 " -c, --config=DIR Read configuration options from DIR.\n"
125 " -n, --net=NETNAME Connect to net NETNAME.\n"
126 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
127 " --force Force some commands to work despite warnings.\n"
128 " --help Display this help and exit.\n"
129 " --version Output version information and exit.\n"
131 "Valid commands are:\n"
132 " init [name] Create initial configuration files.\n"
133 " get VARIABLE Print current value of VARIABLE\n"
134 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
135 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
136 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
137 " start [tincd options] Start tincd.\n"
138 " stop Stop tincd.\n"
139 " restart [tincd options] Restart tincd.\n"
140 " reload Partially reload configuration of running tincd.\n"
141 " pid Show PID of currently running tincd.\n"
142 #ifdef DISABLE_LEGACY
143 " generate-keys Generate a new Ed25519 public/private key pair.\n"
145 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
146 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
148 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
149 " dump Dump a list of one of the following things:\n"
150 " [reachable] nodes - all known nodes in the VPN\n"
151 " edges - all known connections in the VPN\n"
152 " subnets - all known subnets in the VPN\n"
153 " connections - all meta connections with ourself\n"
154 " [di]graph - graph of the VPN in dotty format\n"
155 " invitations - outstanding invitations\n"
156 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
157 " purge Purge unreachable nodes\n"
158 " debug N Set debug level\n"
159 " retry Retry all outgoing connections\n"
160 " disconnect NODE Close meta connection with NODE\n"
162 " top Show real-time statistics\n"
164 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
165 " log [level] Dump log output [up to the specified level]\n"
166 " export Export host configuration of local node to standard output\n"
167 " export-all Export all host configuration files to standard output\n"
168 " import Import host configuration file(s) from standard input\n"
169 " exchange Same as export followed by import\n"
170 " exchange-all Same as export-all followed by import\n"
171 " invite NODE [...] Generate an invitation for NODE\n"
172 " join INVITATION Join a VPN using an INVITATION\n"
173 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
174 " fsck Check the configuration files for problems.\n"
175 " sign [FILE] Generate a signed version of a file.\n"
176 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
178 "Report bugs to tinc@tinc-vpn.org.\n";
180 printf(message, program_name);
184 static bool parse_options(int argc, char **argv) {
186 int option_index = 0;
188 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
190 case 0: /* long option */
197 case 'c': /* config file */
199 confbase = xstrdup(optarg);
200 confbasegiven = true;
203 case 'n': /* net name given */
205 netname = xstrdup(optarg);
208 case 1: /* show help */
212 case 2: /* show version */
216 case 3: /* open control socket here */
218 pidfilename = xstrdup(optarg);
225 case '?': /* wrong options */
235 if(!netname && (netname = getenv("NETNAME"))) {
236 netname = xstrdup(netname);
239 /* netname "." is special: a "top-level name" */
241 if(netname && (!*netname || !strcmp(netname, "."))) {
246 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
247 fprintf(stderr, "Invalid character in netname!\n");
255 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
257 char directory[PATH_MAX] = ".";
263 /* Check stdin and stdout */
265 /* Ask for a file and/or directory name. */
266 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
268 if(fgets(buf, sizeof(buf), stdin) == NULL) {
269 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
273 size_t len = strlen(buf);
286 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
289 if(filename[0] != '/') {
292 /* The directory is a relative path or a filename. */
293 if(!getcwd(directory, sizeof(directory))) {
294 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
298 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
299 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
311 disable_old_keys(filename, what);
313 /* Open it first to keep the inode busy */
315 r = fopenmask(filename, mode, perms);
318 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
326 Generate a public/private Ed25519 key pair, and ask for a file to store
329 static bool ed25519_keygen(bool ask) {
332 char fname[PATH_MAX];
334 fprintf(stderr, "Generating Ed25519 key pair:\n");
336 if(!(key = ecdsa_generate())) {
337 fprintf(stderr, "Error during key generation!\n");
340 fprintf(stderr, "Done.\n");
343 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
344 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
350 if(!ecdsa_write_pem_private_key(key, f)) {
351 fprintf(stderr, "Error writing private key!\n");
358 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
360 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
363 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
369 char *pubkey = ecdsa_get_base64_public_key(key);
370 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
388 #ifndef DISABLE_LEGACY
390 Generate a public/private RSA key pair, and ask for a file to store
393 static bool rsa_keygen(int bits, bool ask) {
396 char fname[PATH_MAX];
398 // Make sure the key size is a multiple of 8 bits.
401 // Make sure that a valid key size is used.
402 if(bits < 1024 || bits > 8192) {
403 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
405 } else if(bits < 2048) {
406 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
409 fprintf(stderr, "Generating %d bits keys:\n", bits);
411 if(!(key = rsa_generate(bits, 0x10001))) {
412 fprintf(stderr, "Error during key generation!\n");
415 fprintf(stderr, "Done.\n");
418 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
419 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
425 if(!rsa_write_pem_private_key(key, f)) {
426 fprintf(stderr, "Error writing private key!\n");
433 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
435 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
438 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
444 if(!rsa_write_pem_public_key(key, f)) {
445 fprintf(stderr, "Error writing public key!\n");
468 bool recvline(int fd, char *line, size_t len) {
469 char *newline = NULL;
475 while(!(newline = memchr(buffer, '\n', blen))) {
476 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
478 if(nrecv == -1 && sockerrno == EINTR) {
480 } else if(nrecv <= 0) {
487 if((size_t)(newline - buffer) >= len) {
491 len = newline - buffer;
493 memcpy(line, buffer, len);
495 memmove(buffer, newline + 1, blen - len - 1);
501 static bool recvdata(int fd, char *data, size_t len) {
503 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
505 if(nrecv == -1 && sockerrno == EINTR) {
507 } else if(nrecv <= 0) {
514 memcpy(data, buffer, len);
515 memmove(buffer, buffer + len, blen - len);
521 bool sendline(int fd, const char *format, ...) {
522 static char buffer[4096];
527 va_start(ap, format);
528 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
529 buffer[sizeof(buffer) - 1] = 0;
532 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
540 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
542 if(nsend == -1 && sockerrno == EINTR) {
544 } else if(nsend <= 0) {
555 static void pcap(int fd, FILE *out, uint32_t snaplen) {
556 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
564 uint32_t tz_accuracy;
571 snaplen ? snaplen : sizeof(data),
584 fwrite(&header, sizeof(header), 1, out);
589 while(recvline(fd, line, sizeof(line))) {
592 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
593 gettimeofday(&tv, NULL);
595 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
599 if(!recvdata(fd, data, len)) {
603 packet.tv_sec = tv.tv_sec;
604 packet.tv_usec = tv.tv_usec;
606 packet.origlen = len;
607 fwrite(&packet, sizeof(packet), 1, out);
608 fwrite(data, len, 1, out);
613 static void logcontrol(int fd, FILE *out, int level) {
614 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
618 while(recvline(fd, line, sizeof(line))) {
620 int n = sscanf(line, "%d %d %d", &code, &req, &len);
622 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
626 if(!recvdata(fd, data, len)) {
630 fwrite(data, len, 1, out);
636 static bool stop_tincd(void) {
637 if(!connect_tincd(true)) {
641 sendline(fd, "%d %d", CONTROL, REQ_STOP);
643 while(recvline(fd, line, sizeof(line))) {
644 // wait for tincd to close the connection...
655 static bool remove_service(void) {
656 SC_HANDLE manager = NULL;
657 SC_HANDLE service = NULL;
658 SERVICE_STATUS status = {0};
659 bool success = false;
661 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
664 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
668 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
671 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
672 success = stop_tincd();
674 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
680 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
681 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
683 fprintf(stderr, "%s service stopped\n", identname);
686 if(!DeleteService(service)) {
687 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
696 CloseServiceHandle(service);
700 CloseServiceHandle(manager);
704 fprintf(stderr, "%s service removed\n", identname);
711 bool connect_tincd(bool verbose) {
716 struct timeval tv = {0, 0};
718 if(select(fd + 1, &r, NULL, NULL, &tv)) {
719 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
727 FILE *f = fopen(pidfilename, "r");
731 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
740 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
742 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
753 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
754 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
755 /* clean up the stale socket and pid file */
757 unlink(unixsocketname);
761 struct sockaddr_un sa = {
762 .sun_family = AF_UNIX,
765 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
766 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
770 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
772 fd = socket(AF_UNIX, SOCK_STREAM, 0);
776 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
782 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
784 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
793 struct addrinfo hints = {
794 .ai_family = AF_UNSPEC,
795 .ai_socktype = SOCK_STREAM,
796 .ai_protocol = IPPROTO_TCP,
800 struct addrinfo *res = NULL;
802 if(getaddrinfo(host, port, &hints, &res) || !res) {
804 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
810 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
814 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
820 unsigned long arg = 0;
822 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
824 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
828 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
830 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
842 static const int one = 1;
843 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
846 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
851 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
853 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
861 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
863 fprintf(stderr, "Could not fully establish control socket connection\n");
875 static int cmd_start(int argc, char *argv[]) {
876 if(connect_tincd(false)) {
878 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
880 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
887 char *slash = strrchr(program_name, '/');
891 if((c = strrchr(program_name, '\\')) > slash) {
898 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
900 c = xstrdup("tincd");
904 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
909 Windows has no real concept of an "argv array". A command line is just one string.
910 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
911 it uses quotes to handle spaces in arguments.
912 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
913 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
914 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
916 xasprintf(&arg0, "\"%s\"", arg0);
918 nargv[nargc++] = arg0;
920 for(int i = 1; i < optind; i++) {
921 nargv[nargc++] = orig_argv[i];
924 for(int i = 1; i < argc; i++) {
925 nargv[nargc++] = argv[i];
929 int status = spawnvp(_P_WAIT, c, nargv);
935 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
941 int pfd[2] = {-1, -1};
943 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
944 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
953 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
962 snprintf(buf, sizeof(buf), "%d", pfd[1]);
963 setenv("TINC_UMBILICAL", buf, true);
964 exit(execvp(c, nargv));
972 signal(SIGINT, SIG_IGN);
975 // Pass all log messages from the umbilical to stderr.
976 // A nul-byte right before closure means tincd started successfully.
981 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
982 failure = buf[len - 1];
988 if(write(2, buf, len) != len) {
989 // Nothing we can do about it.
999 // Make sure the child process is really gone.
1001 pid_t result = waitpid(pid, &status, 0);
1004 signal(SIGINT, SIG_DFL);
1007 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1010 fprintf(stderr, "Error starting %s\n", c);
1015 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1019 static int cmd_stop(int argc, char *argv[]) {
1023 fprintf(stderr, "Too many arguments!\n");
1028 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1033 if(kill(pid, SIGTERM)) {
1034 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1038 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1039 waitpid(pid, NULL, 0);
1050 static int cmd_restart(int argc, char *argv[]) {
1052 return cmd_start(argc, argv);
1055 static int cmd_reload(int argc, char *argv[]) {
1059 fprintf(stderr, "Too many arguments!\n");
1063 if(!connect_tincd(true)) {
1067 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1069 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1070 fprintf(stderr, "Could not reload configuration.\n");
1078 static int dump_invitations(void) {
1079 char dname[PATH_MAX];
1080 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1081 DIR *dir = opendir(dname);
1084 if(errno == ENOENT) {
1085 fprintf(stderr, "No outstanding invitations.\n");
1089 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1097 while((ent = readdir(dir))) {
1098 char buf[MAX_STRING_SIZE];
1100 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1104 char fname[PATH_MAX];
1106 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1107 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1111 FILE *f = fopen(fname, "r");
1114 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1120 if(!fgets(buf, sizeof(buf), f)) {
1121 fprintf(stderr, "Invalid invitation file %s\n", fname);
1128 char *eol = buf + strlen(buf);
1130 while(strchr("\t \r\n", *--eol)) {
1134 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1135 fprintf(stderr, "Invalid invitation file %s\n", fname);
1140 printf("%s %s\n", ent->d_name, buf + 7);
1146 fprintf(stderr, "No outstanding invitations.\n");
1152 static int cmd_dump(int argc, char *argv[]) {
1153 bool only_reachable = false;
1155 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1156 if(strcasecmp(argv[2], "nodes")) {
1157 fprintf(stderr, "`reachable' only supported for nodes.\n");
1162 only_reachable = true;
1168 fprintf(stderr, "Invalid number of arguments.\n");
1173 if(!strcasecmp(argv[1], "invitations")) {
1174 return dump_invitations();
1177 if(!connect_tincd(true)) {
1183 if(!strcasecmp(argv[1], "nodes")) {
1184 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1185 } else if(!strcasecmp(argv[1], "edges")) {
1186 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1187 } else if(!strcasecmp(argv[1], "subnets")) {
1188 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1189 } else if(!strcasecmp(argv[1], "connections")) {
1190 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1191 } else if(!strcasecmp(argv[1], "graph")) {
1192 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1193 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1195 } else if(!strcasecmp(argv[1], "digraph")) {
1196 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1197 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1200 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1206 printf("graph {\n");
1207 } else if(do_graph == 2) {
1208 printf("digraph {\n");
1211 while(recvline(fd, line, sizeof(line))) {
1212 char node1[4096], node2[4096];
1213 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1216 if(do_graph && req == REQ_DUMP_NODES) {
1238 char local_host[4096];
1239 char local_port[4096];
1242 int cipher, digest, maclength, compression, distance, socket, weight;
1243 short int pmtu, minmtu, maxmtu;
1244 unsigned int options;
1245 node_status_t status;
1246 long int last_state_change;
1248 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1251 case REQ_DUMP_NODES: {
1252 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %d %d %d %d %x %"PRIx32" %4095s %4095s %d %hd %hd %hd %ld %d %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status.value, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change, &udp_ping_rtt, &in_packets, &in_bytes, &out_packets, &out_bytes);
1255 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1260 const char *color = "black";
1262 if(!strcmp(host, "MYSELF")) {
1264 } else if(!status.reachable) {
1266 } else if(strcmp(via, node)) {
1268 } else if(!status.validkey) {
1270 } else if(minmtu > 0) {
1274 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1276 if(only_reachable && !status.reachable) {
1280 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 %d (min %d max %d) rx %"PRIu64" %"PRIu64" tx %"PRIu64" %"PRIu64,
1281 node, id, host, port, cipher, digest, maclength, compression, options, status.value, nexthop, via, distance, pmtu, minmtu, maxmtu, in_packets, in_bytes, out_packets, out_bytes);
1283 if(udp_ping_rtt != -1) {
1284 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1292 case REQ_DUMP_EDGES: {
1293 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %4095s port %4095s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
1296 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1301 float w = 1.0f + 65536.0f / (float)weight;
1303 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1304 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1305 } else if(do_graph == 2) {
1306 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1309 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);
1314 case REQ_DUMP_SUBNETS: {
1315 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1318 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1322 printf("%s owner %s\n", strip_weight(subnet), node);
1326 case REQ_DUMP_CONNECTIONS: {
1327 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1330 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1334 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1339 fprintf(stderr, "Unable to parse dump from tincd.\n");
1344 fprintf(stderr, "Error receiving dump.\n");
1348 static int cmd_purge(int argc, char *argv[]) {
1352 fprintf(stderr, "Too many arguments!\n");
1356 if(!connect_tincd(true)) {
1360 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1362 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1363 fprintf(stderr, "Could not purge old information.\n");
1370 static int cmd_debug(int argc, char *argv[]) {
1372 fprintf(stderr, "Invalid number of arguments.\n");
1376 if(!connect_tincd(true)) {
1380 int debuglevel = atoi(argv[1]);
1383 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1385 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1386 fprintf(stderr, "Could not set debug level.\n");
1390 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1394 static int cmd_retry(int argc, char *argv[]) {
1398 fprintf(stderr, "Too many arguments!\n");
1402 if(!connect_tincd(true)) {
1406 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1408 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1409 fprintf(stderr, "Could not retry outgoing connections.\n");
1416 static int cmd_connect(int argc, char *argv[]) {
1418 fprintf(stderr, "Invalid number of arguments.\n");
1422 if(!check_id(argv[1])) {
1423 fprintf(stderr, "Invalid name for node.\n");
1427 if(!connect_tincd(true)) {
1431 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1433 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1434 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1441 static int cmd_disconnect(int argc, char *argv[]) {
1443 fprintf(stderr, "Invalid number of arguments.\n");
1447 if(!check_id(argv[1])) {
1448 fprintf(stderr, "Invalid name for node.\n");
1452 if(!connect_tincd(true)) {
1456 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1458 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1459 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1466 static int cmd_top(int argc, char *argv[]) {
1470 fprintf(stderr, "Too many arguments!\n");
1476 if(!connect_tincd(true)) {
1483 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1488 static int cmd_pcap(int argc, char *argv[]) {
1490 fprintf(stderr, "Too many arguments!\n");
1494 if(!connect_tincd(true)) {
1498 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1503 static void sigint_handler(int sig) {
1506 fprintf(stderr, "\n");
1507 shutdown(fd, SHUT_RDWR);
1511 static int cmd_log(int argc, char *argv[]) {
1513 fprintf(stderr, "Too many arguments!\n");
1517 if(!connect_tincd(true)) {
1522 signal(SIGINT, sigint_handler);
1525 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1528 signal(SIGINT, SIG_DFL);
1536 static int cmd_pid(int argc, char *argv[]) {
1540 fprintf(stderr, "Too many arguments!\n");
1544 if(!connect_tincd(true) || !pid) {
1548 printf("%d\n", pid);
1552 size_t rstrip(char *value) {
1553 size_t len = strlen(value);
1555 while(len && strchr("\t\r\n ", value[len - 1])) {
1562 char *get_my_name(bool verbose) {
1563 FILE *f = fopen(tinc_conf, "r");
1567 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1576 while(fgets(buf, sizeof(buf), f)) {
1577 size_t len = strcspn(buf, "\t =");
1579 value += strspn(value, "\t ");
1583 value += strspn(value, "\t ");
1586 if(!rstrip(value)) {
1592 if(strcasecmp(buf, "Name")) {
1598 return replace_name(value);
1605 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1611 ecdsa_t *get_pubkey(FILE *f) {
1615 while(fgets(buf, sizeof(buf), f)) {
1616 size_t len = strcspn(buf, "\t =");
1618 value += strspn(value, "\t ");
1622 value += strspn(value, "\t ");
1625 if(!rstrip(value)) {
1631 if(strcasecmp(buf, "Ed25519PublicKey")) {
1636 return ecdsa_set_base64_public_key(value);
1643 const var_t variables[] = {
1644 /* Server configuration */
1645 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1646 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1647 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1648 {"BindToInterface", VAR_SERVER},
1649 {"Broadcast", VAR_SERVER | VAR_SAFE},
1650 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1651 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1652 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1653 {"Device", VAR_SERVER},
1654 {"DeviceStandby", VAR_SERVER},
1655 {"DeviceType", VAR_SERVER},
1656 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1657 {"Ed25519PrivateKeyFile", VAR_SERVER},
1658 {"ExperimentalProtocol", VAR_SERVER},
1659 {"Forwarding", VAR_SERVER},
1660 {"FWMark", VAR_SERVER},
1661 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1662 {"Hostnames", VAR_SERVER},
1663 {"IffOneQueue", VAR_SERVER},
1664 {"Interface", VAR_SERVER},
1665 {"InvitationExpire", VAR_SERVER},
1666 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1667 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1668 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1669 {"LogLevel", VAR_SERVER},
1670 {"MACExpire", VAR_SERVER | VAR_SAFE},
1671 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1672 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1673 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1674 {"Mode", VAR_SERVER | VAR_SAFE},
1675 {"Name", VAR_SERVER},
1676 {"PingInterval", VAR_SERVER | VAR_SAFE},
1677 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1678 {"PriorityInheritance", VAR_SERVER},
1679 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1680 {"PrivateKeyFile", VAR_SERVER},
1681 {"ProcessPriority", VAR_SERVER},
1682 {"Proxy", VAR_SERVER},
1683 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1684 {"ScriptsExtension", VAR_SERVER},
1685 {"ScriptsInterpreter", VAR_SERVER},
1686 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1687 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1688 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1689 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1690 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1691 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1692 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1693 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1694 {"UDPRcvBuf", VAR_SERVER},
1695 {"UDPSndBuf", VAR_SERVER},
1696 {"UPnP", VAR_SERVER},
1697 {"UPnPDiscoverWait", VAR_SERVER},
1698 {"UPnPRefreshPeriod", VAR_SERVER},
1699 {"VDEGroup", VAR_SERVER},
1700 {"VDEPort", VAR_SERVER},
1701 /* Host configuration */
1702 {"Address", VAR_HOST | VAR_MULTIPLE},
1703 {"Cipher", VAR_SERVER | VAR_HOST},
1704 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1705 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1706 {"Digest", VAR_SERVER | VAR_HOST},
1707 {"Ed25519PublicKey", VAR_HOST},
1708 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1709 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1710 {"MACLength", VAR_SERVER | VAR_HOST},
1711 {"PMTU", VAR_SERVER | VAR_HOST},
1712 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1714 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1715 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1716 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1717 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1718 {"Weight", VAR_HOST | VAR_SAFE},
1722 static int cmd_config(int argc, char *argv[]) {
1724 fprintf(stderr, "Invalid number of arguments.\n");
1728 if(strcasecmp(argv[0], "config")) {
1734 if(!strcasecmp(argv[1], "get")) {
1736 } else if(!strcasecmp(argv[1], "add")) {
1737 argv++, argc--, action = 1;
1738 } else if(!strcasecmp(argv[1], "del")) {
1739 argv++, argc--, action = -1;
1740 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1741 argv++, argc--, action = 0;
1745 fprintf(stderr, "Invalid number of arguments.\n");
1749 // Concatenate the rest of the command line
1750 strncpy(line, argv[1], sizeof(line) - 1);
1752 for(int i = 2; i < argc; i++) {
1753 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1754 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1757 // Liberal parsing into node name, variable name and value.
1763 len = strcspn(line, "\t =");
1765 value += strspn(value, "\t ");
1769 value += strspn(value, "\t ");
1773 variable = strchr(line, '.');
1783 fprintf(stderr, "No variable given.\n");
1787 if(action >= 0 && !*value) {
1788 fprintf(stderr, "No value for variable given.\n");
1792 if(action < -1 && *value) {
1796 /* Some simple checks. */
1798 bool warnonremove = false;
1800 for(int i = 0; variables[i].name; i++) {
1801 if(strcasecmp(variables[i].name, variable)) {
1806 variable = (char *)variables[i].name;
1808 if(!strcasecmp(variable, "Subnet") && *value) {
1811 if(!str2net(&s, value)) {
1812 fprintf(stderr, "Malformed subnet definition %s\n", value);
1816 if(!subnetcheck(s)) {
1817 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1822 /* Discourage use of obsolete variables. */
1824 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1826 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1828 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1833 /* Don't put server variables in host config files */
1835 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1837 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1839 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1844 /* Should this go into our own host config file? */
1846 if(!node && !(variables[i].type & VAR_SERVER)) {
1847 node = get_my_name(true);
1854 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1855 Turn on warnings when it seems variables might be removed unintentionally. */
1857 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1858 warnonremove = true;
1860 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1861 warnonremove = true;
1867 if(node && !check_id(node)) {
1868 fprintf(stderr, "Invalid name for node.\n");
1878 if(force || action < 0) {
1879 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1881 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1883 if(node && node != line) {
1891 // Open the right configuration file.
1892 char filename[PATH_MAX];
1895 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node) >= sizeof(filename)) {
1896 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1906 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1909 FILE *f = fopen(filename, "r");
1912 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1916 char tmpfile[PATH_MAX];
1920 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1921 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1925 tf = fopen(tmpfile, "w");
1928 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1934 // Copy the file, making modifications on the fly, unless we are just getting a value.
1938 bool removed = false;
1941 while(fgets(buf1, sizeof(buf1), f)) {
1942 buf1[sizeof(buf1) - 1] = 0;
1943 strncpy(buf2, buf1, sizeof(buf2));
1945 // Parse line in a simple way
1948 size_t len = strcspn(buf2, "\t =");
1949 bvalue = buf2 + len;
1950 bvalue += strspn(bvalue, "\t ");
1952 if(*bvalue == '=') {
1954 bvalue += strspn(bvalue, "\t ");
1961 if(!strcasecmp(buf2, variable)) {
1965 printf("%s\n", bvalue);
1967 } else if(action == -1) {
1968 if(!*value || !strcasecmp(bvalue, value)) {
1974 } else if(action == 0) {
1975 // Warn if "set" was used for variables that can occur multiple times
1976 if(warnonremove && strcasecmp(bvalue, value)) {
1977 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1980 // Already set? Delete the rest...
1985 // Otherwise, replace.
1986 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1987 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1994 } else if(action > 0) {
1995 // Check if we've already seen this variable with the same value
1996 if(!strcasecmp(bvalue, value)) {
2003 // Copy original line...
2004 if(fputs(buf1, tf) < 0) {
2005 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2009 // Add newline if it is missing...
2010 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2011 if(fputc('\n', tf) < 0) {
2012 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2019 // Make sure we read everything...
2020 if(ferror(f) || !feof(f)) {
2021 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2026 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2030 // Add new variable if necessary.
2031 if((action > 0 && !found) || (action == 0 && !set)) {
2032 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2033 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2042 fprintf(stderr, "No matching configuration variables found.\n");
2047 // Make sure we wrote everything...
2049 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2053 // Could we find what we had to remove?
2054 if(action < 0 && !removed) {
2056 fprintf(stderr, "No configuration variables deleted.\n");
2060 // Replace the configuration file with the new one
2063 if(remove(filename)) {
2064 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2070 if(rename(tmpfile, filename)) {
2071 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2075 // Silently try notifying a running tincd of changes.
2076 if(connect_tincd(false)) {
2077 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2083 static bool try_bind(int port) {
2084 struct addrinfo *ai = NULL, *aip;
2085 struct addrinfo hint = {
2086 .ai_flags = AI_PASSIVE,
2087 .ai_family = AF_UNSPEC,
2088 .ai_socktype = SOCK_STREAM,
2089 .ai_protocol = IPPROTO_TCP,
2092 bool success = true;
2094 snprintf(portstr, sizeof(portstr), "%d", port);
2096 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2100 for(aip = ai; aip; aip = aip->ai_next) {
2101 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2108 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2121 int check_port(const char *name) {
2126 fprintf(stderr, "Warning: could not bind to port 655. ");
2128 for(int i = 0; i < 100; i++) {
2129 uint16_t port = 0x1000 + prng(0x8000);
2131 if(try_bind(port)) {
2132 char filename[PATH_MAX];
2133 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2134 FILE *f = fopen(filename, "a");
2137 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2138 fprintf(stderr, "Please change tinc's Port manually.\n");
2142 fprintf(f, "Port = %d\n", port);
2144 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2149 fprintf(stderr, "Please change tinc's Port manually.\n");
2153 static int cmd_init(int argc, char *argv[]) {
2154 if(!access(tinc_conf, F_OK)) {
2155 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2160 fprintf(stderr, "Too many arguments!\n");
2162 } else if(argc < 2) {
2165 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2167 if(!fgets(buf, sizeof(buf), stdin)) {
2168 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2172 size_t len = rstrip(buf);
2175 fprintf(stderr, "No name given!\n");
2181 fprintf(stderr, "No Name given!\n");
2185 name = strdup(argv[1]);
2188 fprintf(stderr, "No Name given!\n");
2193 if(!check_id(name)) {
2194 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2198 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2199 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2203 if(mkdir(confbase, 0777) && errno != EEXIST) {
2204 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2208 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2209 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2213 FILE *f = fopen(tinc_conf, "w");
2216 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2220 fprintf(f, "Name = %s\n", name);
2223 #ifndef DISABLE_LEGACY
2225 if(!rsa_keygen(2048, false)) {
2231 if(!ed25519_keygen(false)) {
2238 char filename[PATH_MAX];
2239 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2241 if(access(filename, F_OK)) {
2242 FILE *f = fopenmask(filename, "w", 0777);
2245 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2249 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");
2259 static int cmd_generate_keys(int argc, char *argv[]) {
2260 #ifdef DISABLE_LEGACY
2268 fprintf(stderr, "Too many arguments!\n");
2273 name = get_my_name(false);
2276 #ifndef DISABLE_LEGACY
2278 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2284 if(!ed25519_keygen(true)) {
2291 #ifndef DISABLE_LEGACY
2292 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2294 fprintf(stderr, "Too many arguments!\n");
2299 name = get_my_name(false);
2302 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2306 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2310 fprintf(stderr, "Too many arguments!\n");
2315 name = get_my_name(false);
2318 return !ed25519_keygen(true);
2321 static int cmd_help(int argc, char *argv[]) {
2329 static int cmd_version(int argc, char *argv[]) {
2333 fprintf(stderr, "Too many arguments!\n");
2341 static int cmd_info(int argc, char *argv[]) {
2343 fprintf(stderr, "Invalid number of arguments.\n");
2347 if(!connect_tincd(true)) {
2351 return info(fd, argv[1]);
2354 static const char *conffiles[] = {
2365 static int cmd_edit(int argc, char *argv[]) {
2367 fprintf(stderr, "Invalid number of arguments.\n");
2371 char filename[PATH_MAX] = "";
2373 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2374 for(int i = 0; conffiles[i]; i++) {
2375 if(!strcmp(argv[1], conffiles[i])) {
2376 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2385 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2386 char *dash = strchr(argv[1], '-');
2391 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2392 fprintf(stderr, "Invalid configuration filename.\n");
2400 const char *editor = getenv("VISUAL");
2403 editor = getenv("EDITOR");
2410 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2412 xasprintf(&command, "edit \"%s\"", filename);
2414 int result = system(command);
2421 // Silently try notifying a running tincd of changes.
2422 if(connect_tincd(false)) {
2423 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2429 static int export(const char *name, FILE *out) {
2430 char filename[PATH_MAX];
2431 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2432 FILE *in = fopen(filename, "r");
2435 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2439 fprintf(out, "Name = %s\n", name);
2442 while(fgets(buf, sizeof(buf), in)) {
2443 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2449 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2458 static int cmd_export(int argc, char *argv[]) {
2462 fprintf(stderr, "Too many arguments!\n");
2466 char *name = get_my_name(true);
2472 int result = export(name, stdout);
2482 static int cmd_export_all(int argc, char *argv[]) {
2486 fprintf(stderr, "Too many arguments!\n");
2490 DIR *dir = opendir(hosts_dir);
2493 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2501 while((ent = readdir(dir))) {
2502 if(!check_id(ent->d_name)) {
2509 printf("#---------------------------------------------------------------#\n");
2512 result |= export(ent->d_name, stdout);
2524 static int cmd_import(int argc, char *argv[]) {
2528 fprintf(stderr, "Too many arguments!\n");
2537 char filename[PATH_MAX] = "";
2539 bool firstline = true;
2541 while(fgets(buf, sizeof(buf), in)) {
2542 if(sscanf(buf, "Name = %4095s", name) == 1) {
2545 if(!check_id(name)) {
2546 fprintf(stderr, "Invalid Name in input!\n");
2554 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2555 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2559 if(!force && !access(filename, F_OK)) {
2560 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2565 out = fopen(filename, "w");
2568 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2574 } else if(firstline) {
2575 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2580 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2585 if(fputs(buf, out) < 0) {
2586 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2597 fprintf(stderr, "Imported %d host configuration files.\n", count);
2600 fprintf(stderr, "No host configuration files imported.\n");
2605 static int cmd_exchange(int argc, char *argv[]) {
2606 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2609 static int cmd_exchange_all(int argc, char *argv[]) {
2610 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2613 static int switch_network(char *name) {
2614 if(strcmp(name, ".")) {
2615 if(!check_netname(name, false)) {
2616 fprintf(stderr, "Invalid character in netname!\n");
2620 if(!check_netname(name, true)) {
2621 fprintf(stderr, "Warning: unsafe character in netname!\n");
2631 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2638 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2639 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2640 xasprintf(&prompt, "%s> ", identname);
2645 static int cmd_network(int argc, char *argv[]) {
2647 fprintf(stderr, "Too many arguments!\n");
2652 return switch_network(argv[1]);
2655 DIR *dir = opendir(confdir);
2658 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2664 while((ent = readdir(dir))) {
2665 if(*ent->d_name == '.') {
2669 if(!strcmp(ent->d_name, "tinc.conf")) {
2674 char fname[PATH_MAX];
2675 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2677 if(!access(fname, R_OK)) {
2678 printf("%s\n", ent->d_name);
2687 static int cmd_fsck(int argc, char *argv[]) {
2691 fprintf(stderr, "Too many arguments!\n");
2695 return fsck(orig_argv[0]);
2698 static void *readfile(FILE *in, size_t *len) {
2700 size_t bufsize = 4096;
2701 char *buf = xmalloc(bufsize);
2704 size_t read = fread(buf + count, 1, bufsize - count, in);
2712 if(count >= bufsize) {
2714 buf = xrealloc(buf, bufsize);
2725 static int cmd_sign(int argc, char *argv[]) {
2727 fprintf(stderr, "Too many arguments!\n");
2732 name = get_my_name(true);
2739 char fname[PATH_MAX];
2740 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2741 FILE *fp = fopen(fname, "r");
2744 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2748 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2751 fprintf(stderr, "Could not read private key from %s\n", fname);
2761 in = fopen(argv[1], "rb");
2764 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2773 char *data = readfile(in, &len);
2780 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2785 // Ensure we sign our name and current time as well
2786 long t = time(NULL);
2788 xasprintf(&trailer, " %s %ld", name, t);
2789 size_t trailer_len = strlen(trailer);
2791 data = xrealloc(data, len + trailer_len);
2792 memcpy(data + len, trailer, trailer_len);
2797 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2798 fprintf(stderr, "Error generating signature\n");
2804 b64encode_tinc(sig, sig, 64);
2807 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2808 fwrite(data, len, 1, stdout);
2814 static int cmd_verify(int argc, char *argv[]) {
2816 fprintf(stderr, "Not enough arguments!\n");
2821 fprintf(stderr, "Too many arguments!\n");
2825 char *node = argv[1];
2827 if(!strcmp(node, ".")) {
2829 name = get_my_name(true);
2837 } else if(!strcmp(node, "*")) {
2840 if(!check_id(node)) {
2841 fprintf(stderr, "Invalid node name\n");
2849 in = fopen(argv[2], "rb");
2852 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2860 char *data = readfile(in, &len);
2867 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2871 char *newline = memchr(data, '\n', len);
2873 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2874 fprintf(stderr, "Invalid input\n");
2880 size_t skip = newline - data;
2882 char signer[MAX_STRING_SIZE] = "";
2883 char sig[MAX_STRING_SIZE] = "";
2886 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2887 fprintf(stderr, "Invalid input\n");
2892 if(node && strcmp(node, signer)) {
2893 fprintf(stderr, "Signature is not made by %s\n", node);
2903 xasprintf(&trailer, " %s %ld", signer, t);
2904 size_t trailer_len = strlen(trailer);
2906 data = xrealloc(data, len + trailer_len);
2907 memcpy(data + len, trailer, trailer_len);
2910 newline = data + skip;
2912 char fname[PATH_MAX];
2913 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2914 FILE *fp = fopen(fname, "r");
2917 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2922 ecdsa_t *key = get_pubkey(fp);
2926 key = ecdsa_read_pem_public_key(fp);
2930 fprintf(stderr, "Could not read public key from %s\n", fname);
2938 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2939 fprintf(stderr, "Invalid signature\n");
2947 fwrite(newline, len - (newline - data), 1, stdout);
2953 static const struct {
2954 const char *command;
2955 int (*function)(int argc, char *argv[]);
2958 {"start", cmd_start, false},
2959 {"stop", cmd_stop, false},
2960 {"restart", cmd_restart, false},
2961 {"reload", cmd_reload, false},
2962 {"dump", cmd_dump, false},
2963 {"list", cmd_dump, false},
2964 {"purge", cmd_purge, false},
2965 {"debug", cmd_debug, false},
2966 {"retry", cmd_retry, false},
2967 {"connect", cmd_connect, false},
2968 {"disconnect", cmd_disconnect, false},
2969 {"top", cmd_top, false},
2970 {"pcap", cmd_pcap, false},
2971 {"log", cmd_log, false},
2972 {"pid", cmd_pid, false},
2973 {"config", cmd_config, true},
2974 {"add", cmd_config, false},
2975 {"del", cmd_config, false},
2976 {"get", cmd_config, false},
2977 {"set", cmd_config, false},
2978 {"init", cmd_init, false},
2979 {"generate-keys", cmd_generate_keys, false},
2980 #ifndef DISABLE_LEGACY
2981 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
2983 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
2984 {"help", cmd_help, false},
2985 {"version", cmd_version, false},
2986 {"info", cmd_info, false},
2987 {"edit", cmd_edit, false},
2988 {"export", cmd_export, false},
2989 {"export-all", cmd_export_all, false},
2990 {"import", cmd_import, false},
2991 {"exchange", cmd_exchange, false},
2992 {"exchange-all", cmd_exchange_all, false},
2993 {"invite", cmd_invite, false},
2994 {"join", cmd_join, false},
2995 {"network", cmd_network, false},
2996 {"fsck", cmd_fsck, false},
2997 {"sign", cmd_sign, false},
2998 {"verify", cmd_verify, false},
2999 {NULL, NULL, false},
3002 #ifdef HAVE_READLINE
3003 static char *complete_command(const char *text, int state) {
3012 while(commands[i].command) {
3013 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3014 return xstrdup(commands[i].command);
3023 static char *complete_dump(const char *text, int state) {
3024 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3034 if(!strncasecmp(matches[i], text, strlen(text))) {
3035 return xstrdup(matches[i]);
3044 static char *complete_config(const char *text, int state) {
3053 while(variables[i].name) {
3054 char *dot = strchr(text, '.');
3057 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3059 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3063 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3064 return xstrdup(variables[i].name);
3074 static char *complete_info(const char *text, int state) {
3080 if(!connect_tincd(false)) {
3084 // Check the list of nodes
3085 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3086 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3089 while(recvline(fd, line, sizeof(line))) {
3091 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3104 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3108 if(!strncmp(item, text, strlen(text))) {
3109 return xstrdup(strip_weight(item));
3116 static char *complete_nothing(const char *text, int state) {
3122 static char **completion(const char *text, int start, int end) {
3124 char **matches = NULL;
3127 matches = rl_completion_matches(text, complete_command);
3128 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3129 matches = rl_completion_matches(text, complete_dump);
3130 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3131 matches = rl_completion_matches(text, complete_config);
3132 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3133 matches = rl_completion_matches(text, complete_config);
3134 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3135 matches = rl_completion_matches(text, complete_config);
3136 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3137 matches = rl_completion_matches(text, complete_config);
3138 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3139 matches = rl_completion_matches(text, complete_info);
3146 static int cmd_shell(int argc, char *argv[]) {
3147 xasprintf(&prompt, "%s> ", identname);
3151 int maxargs = argc + 16;
3152 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3154 for(int i = 0; i < argc; i++) {
3158 #ifdef HAVE_READLINE
3159 rl_readline_name = "tinc";
3160 rl_basic_word_break_characters = "\t\n ";
3161 rl_completion_entry_function = complete_nothing;
3162 rl_attempted_completion_function = completion;
3163 rl_filename_completion_desired = 0;
3168 #ifdef HAVE_READLINE
3173 line = readline(prompt);
3174 copy = line ? xstrdup(line) : NULL;
3176 line = fgets(buf, sizeof(buf), stdin);
3182 fputs(prompt, stdout);
3185 line = fgets(buf, sizeof(buf), stdin);
3192 /* Ignore comments */
3201 char *p = line + strspn(line, " \t\n");
3202 char *next = strtok(p, " \t\n");
3205 if(nargc >= maxargs) {
3207 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3212 next = strtok(NULL, " \t\n");
3219 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3220 #ifdef HAVE_READLINE
3229 for(int i = 0; commands[i].command; i++) {
3230 if(!strcasecmp(nargv[argc], commands[i].command)) {
3231 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3237 #ifdef HAVE_READLINE
3246 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3251 #ifdef HAVE_READLINE
3263 static void cleanup(void) {
3269 int main(int argc, char *argv[]) {
3270 program_name = argv[0];
3272 tty = isatty(0) && isatty(1);
3274 if(!parse_options(argc, argv)) {
3279 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3280 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3294 static struct WSAData wsa_state;
3296 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3297 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3303 gettimeofday(&now, NULL);
3307 if(optind >= argc) {
3308 return cmd_shell(argc, argv);
3311 for(int i = 0; commands[i].command; i++) {
3312 if(!strcasecmp(argv[optind], commands[i].command)) {
3313 return commands[i].function(argc - optind, argv + optind);
3317 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);