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.
25 #include "readline/readline.h"
26 #include "readline/history.h"
31 #include "control_common.h"
36 #include "invitation.h"
47 #define MSG_NOSIGNAL 0
50 static char **orig_argv;
52 /* If nonzero, display usage information and exit. */
53 static bool show_help = false;
55 /* If nonzero, print the version on standard output and exit. */
56 static bool show_version = false;
58 static char *name = NULL;
59 static char controlcookie[1025];
60 char *tinc_conf = NULL;
61 char *hosts_dir = NULL;
64 // Horrible global variables...
73 bool confbasegiven = false;
74 char *scriptinterpreter = NULL;
75 static char defaultextension[] = "";
76 char *scriptextension = defaultextension;
82 static struct option const long_options[] = {
83 {"batch", no_argument, NULL, 'b'},
84 {"config", required_argument, NULL, 'c'},
85 {"net", required_argument, NULL, 'n'},
86 {"help", no_argument, NULL, 1},
87 {"version", no_argument, NULL, 2},
88 {"pidfile", required_argument, NULL, 3},
89 {"force", no_argument, NULL, 4},
93 static void version(void) {
94 static const char *message =
95 "%s version %s (built %s %s, protocol %d.%d)\n"
103 #ifndef DISABLE_LEGACY
107 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
108 "See the AUTHORS file for a complete list.\n"
110 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
111 "and you are welcome to redistribute it under certain conditions;\n"
112 "see the file COPYING for details.\n";
114 printf(message, PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
117 static void usage(bool status) {
119 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
121 static const char *message =
122 "Usage: %s [options] command\n"
124 "Valid options are:\n"
125 " -b, --batch Don't ask for anything (non-interactive mode).\n"
126 " -c, --config=DIR Read configuration options from DIR.\n"
127 " -n, --net=NETNAME Connect to net NETNAME.\n"
128 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
129 " --force Force some commands to work despite warnings.\n"
130 " --help Display this help and exit.\n"
131 " --version Output version information and exit.\n"
133 "Valid commands are:\n"
134 " init [name] Create initial configuration files.\n"
135 " get VARIABLE Print current value of VARIABLE\n"
136 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
137 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
138 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
139 " start [tincd options] Start tincd.\n"
140 " stop Stop tincd.\n"
141 " restart [tincd options] Restart tincd.\n"
142 " reload Partially reload configuration of running tincd.\n"
143 " pid Show PID of currently running tincd.\n"
144 #ifdef DISABLE_LEGACY
145 " generate-keys Generate a new Ed25519 public/private key pair.\n"
147 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
148 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
150 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
151 " dump Dump a list of one of the following things:\n"
152 " [reachable] nodes - all known nodes in the VPN\n"
153 " edges - all known connections in the VPN\n"
154 " subnets - all known subnets in the VPN\n"
155 " connections - all meta connections with ourself\n"
156 " [di]graph - graph of the VPN in dotty format\n"
157 " invitations - outstanding invitations\n"
158 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
159 " purge Purge unreachable nodes\n"
160 " debug N Set debug level\n"
161 " retry Retry all outgoing connections\n"
162 " disconnect NODE Close meta connection with NODE\n"
164 " top Show real-time statistics\n"
166 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
167 " log [level] Dump log output [up to the specified level]\n"
168 " export Export host configuration of local node to standard output\n"
169 " export-all Export all host configuration files to standard output\n"
170 " import Import host configuration file(s) from standard input\n"
171 " exchange Same as export followed by import\n"
172 " exchange-all Same as export-all followed by import\n"
173 " invite NODE [...] Generate an invitation for NODE\n"
174 " join INVITATION Join a VPN using an INVITATION\n"
175 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
176 " fsck Check the configuration files for problems.\n"
177 " sign [FILE] Generate a signed version of a file.\n"
178 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
180 "Report bugs to tinc@tinc-vpn.org.\n";
182 printf(message, program_name);
186 static bool parse_options(int argc, char **argv) {
188 int option_index = 0;
190 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
192 case 0: /* long option */
199 case 'c': /* config file */
201 confbase = xstrdup(optarg);
202 confbasegiven = true;
205 case 'n': /* net name given */
207 netname = xstrdup(optarg);
210 case 1: /* show help */
214 case 2: /* show version */
218 case 3: /* open control socket here */
220 pidfilename = xstrdup(optarg);
227 case '?': /* wrong options */
237 if(!netname && (netname = getenv("NETNAME"))) {
238 netname = xstrdup(netname);
241 /* netname "." is special: a "top-level name" */
243 if(netname && (!*netname || !strcmp(netname, "."))) {
248 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
249 fprintf(stderr, "Invalid character in netname!\n");
257 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
259 char directory[PATH_MAX] = ".";
265 /* Check stdin and stdout */
267 /* Ask for a file and/or directory name. */
268 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
270 if(fgets(buf, sizeof(buf), stdin) == NULL) {
271 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
275 size_t len = strlen(buf);
288 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
291 if(filename[0] != '/') {
294 /* The directory is a relative path or a filename. */
295 if(!getcwd(directory, sizeof(directory))) {
296 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
300 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
301 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
313 disable_old_keys(filename, what);
315 /* Open it first to keep the inode busy */
317 r = fopenmask(filename, mode, perms);
320 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
328 Generate a public/private Ed25519 key pair, and ask for a file to store
331 static bool ed25519_keygen(bool ask) {
334 char fname[PATH_MAX];
336 fprintf(stderr, "Generating Ed25519 key pair:\n");
338 if(!(key = ecdsa_generate())) {
339 fprintf(stderr, "Error during key generation!\n");
342 fprintf(stderr, "Done.\n");
345 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
346 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
352 if(!ecdsa_write_pem_private_key(key, f)) {
353 fprintf(stderr, "Error writing private key!\n");
360 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
362 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
365 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
371 char *pubkey = ecdsa_get_base64_public_key(key);
372 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
390 #ifndef DISABLE_LEGACY
392 Generate a public/private RSA key pair, and ask for a file to store
395 static bool rsa_keygen(int bits, bool ask) {
398 char fname[PATH_MAX];
400 // Make sure the key size is a multiple of 8 bits.
403 // Make sure that a valid key size is used.
404 if(bits < 1024 || bits > 8192) {
405 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
407 } else if(bits < 2048) {
408 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
411 fprintf(stderr, "Generating %d bits keys:\n", bits);
413 if(!(key = rsa_generate(bits, 0x10001))) {
414 fprintf(stderr, "Error during key generation!\n");
417 fprintf(stderr, "Done.\n");
420 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
421 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
427 if(!rsa_write_pem_private_key(key, f)) {
428 fprintf(stderr, "Error writing private key!\n");
435 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
437 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
440 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
446 if(!rsa_write_pem_public_key(key, f)) {
447 fprintf(stderr, "Error writing public key!\n");
470 bool recvline(int fd, char *line, size_t len) {
471 char *newline = NULL;
477 while(!(newline = memchr(buffer, '\n', blen))) {
478 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
480 if(nrecv == -1 && sockerrno == EINTR) {
482 } else if(nrecv <= 0) {
489 if((size_t)(newline - buffer) >= len) {
493 len = newline - buffer;
495 memcpy(line, buffer, len);
497 memmove(buffer, newline + 1, blen - len - 1);
503 static bool recvdata(int fd, char *data, size_t len) {
505 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
507 if(nrecv == -1 && sockerrno == EINTR) {
509 } else if(nrecv <= 0) {
516 memcpy(data, buffer, len);
517 memmove(buffer, buffer + len, blen - len);
523 bool sendline(int fd, const char *format, ...) {
524 static char buffer[4096];
529 va_start(ap, format);
530 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
531 buffer[sizeof(buffer) - 1] = 0;
534 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
542 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
544 if(nsend == -1 && sockerrno == EINTR) {
546 } else if(nsend <= 0) {
557 static void pcap(int fd, FILE *out, uint32_t snaplen) {
558 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
566 uint32_t tz_accuracy;
573 snaplen ? snaplen : sizeof(data),
586 fwrite(&header, sizeof(header), 1, out);
591 while(recvline(fd, line, sizeof(line))) {
594 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
595 gettimeofday(&tv, NULL);
597 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
601 if(!recvdata(fd, data, len)) {
605 packet.tv_sec = tv.tv_sec;
606 packet.tv_usec = tv.tv_usec;
608 packet.origlen = len;
609 fwrite(&packet, sizeof(packet), 1, out);
610 fwrite(data, len, 1, out);
615 static void logcontrol(int fd, FILE *out, int level) {
616 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
620 while(recvline(fd, line, sizeof(line))) {
622 int n = sscanf(line, "%d %d %d", &code, &req, &len);
624 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
628 if(!recvdata(fd, data, len)) {
632 fwrite(data, len, 1, out);
638 static bool stop_tincd(void) {
639 if(!connect_tincd(true)) {
643 sendline(fd, "%d %d", CONTROL, REQ_STOP);
645 while(recvline(fd, line, sizeof(line))) {
646 // wait for tincd to close the connection...
657 static bool remove_service(void) {
658 SC_HANDLE manager = NULL;
659 SC_HANDLE service = NULL;
660 SERVICE_STATUS status = {0};
661 bool success = false;
663 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
666 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
670 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
673 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
674 success = stop_tincd();
676 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
682 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
683 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
685 fprintf(stderr, "%s service stopped\n", identname);
688 if(!DeleteService(service)) {
689 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
698 CloseServiceHandle(service);
702 CloseServiceHandle(manager);
706 fprintf(stderr, "%s service removed\n", identname);
713 bool connect_tincd(bool verbose) {
718 struct timeval tv = {0, 0};
720 if(select(fd + 1, &r, NULL, NULL, &tv)) {
721 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
729 FILE *f = fopen(pidfilename, "r");
733 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
742 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
744 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
755 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
756 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
757 /* clean up the stale socket and pid file */
759 unlink(unixsocketname);
763 struct sockaddr_un sa = {
764 .sun_family = AF_UNIX,
767 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
768 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
772 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
774 fd = socket(AF_UNIX, SOCK_STREAM, 0);
778 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
784 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
786 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
795 struct addrinfo hints = {
796 .ai_family = AF_UNSPEC,
797 .ai_socktype = SOCK_STREAM,
798 .ai_protocol = IPPROTO_TCP,
802 struct addrinfo *res = NULL;
804 if(getaddrinfo(host, port, &hints, &res) || !res) {
806 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
812 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
816 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
822 unsigned long arg = 0;
824 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
826 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
830 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
832 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
844 static const int one = 1;
845 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
848 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
853 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
855 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
863 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
865 fprintf(stderr, "Could not fully establish control socket connection\n");
877 static int cmd_start(int argc, char *argv[]) {
878 if(connect_tincd(false)) {
880 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
882 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
889 char *slash = strrchr(program_name, '/');
893 if((c = strrchr(program_name, '\\')) > slash) {
900 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
902 c = xstrdup("tincd");
906 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
911 Windows has no real concept of an "argv array". A command line is just one string.
912 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
913 it uses quotes to handle spaces in arguments.
914 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
915 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
916 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
918 xasprintf(&arg0, "\"%s\"", arg0);
920 nargv[nargc++] = arg0;
922 for(int i = 1; i < optind; i++) {
923 nargv[nargc++] = orig_argv[i];
926 for(int i = 1; i < argc; i++) {
927 nargv[nargc++] = argv[i];
931 int status = spawnvp(_P_WAIT, c, nargv);
937 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
943 int pfd[2] = {-1, -1};
945 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
946 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
955 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
964 snprintf(buf, sizeof(buf), "%d", pfd[1]);
965 setenv("TINC_UMBILICAL", buf, true);
966 exit(execvp(c, nargv));
974 signal(SIGINT, SIG_IGN);
977 // Pass all log messages from the umbilical to stderr.
978 // A nul-byte right before closure means tincd started successfully.
983 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
984 failure = buf[len - 1];
990 if(write(2, buf, len) != len) {
991 // Nothing we can do about it.
1001 // Make sure the child process is really gone.
1003 pid_t result = waitpid(pid, &status, 0);
1006 signal(SIGINT, SIG_DFL);
1009 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1012 fprintf(stderr, "Error starting %s\n", c);
1017 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1021 static int cmd_stop(int argc, char *argv[]) {
1025 fprintf(stderr, "Too many arguments!\n");
1030 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1035 if(kill(pid, SIGTERM)) {
1036 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1040 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1041 waitpid(pid, NULL, 0);
1052 static int cmd_restart(int argc, char *argv[]) {
1054 return cmd_start(argc, argv);
1057 static int cmd_reload(int argc, char *argv[]) {
1061 fprintf(stderr, "Too many arguments!\n");
1065 if(!connect_tincd(true)) {
1069 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1071 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1072 fprintf(stderr, "Could not reload configuration.\n");
1080 static int dump_invitations(void) {
1081 char dname[PATH_MAX];
1082 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1083 DIR *dir = opendir(dname);
1086 if(errno == ENOENT) {
1087 fprintf(stderr, "No outstanding invitations.\n");
1091 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1099 while((ent = readdir(dir))) {
1100 char buf[MAX_STRING_SIZE];
1102 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1106 char fname[PATH_MAX];
1108 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1109 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1113 FILE *f = fopen(fname, "r");
1116 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1122 if(!fgets(buf, sizeof(buf), f)) {
1123 fprintf(stderr, "Invalid invitation file %s\n", fname);
1130 char *eol = buf + strlen(buf);
1132 while(strchr("\t \r\n", *--eol)) {
1136 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1137 fprintf(stderr, "Invalid invitation file %s\n", fname);
1142 printf("%s %s\n", ent->d_name, buf + 7);
1148 fprintf(stderr, "No outstanding invitations.\n");
1154 static int cmd_dump(int argc, char *argv[]) {
1155 bool only_reachable = false;
1157 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1158 if(strcasecmp(argv[2], "nodes")) {
1159 fprintf(stderr, "`reachable' only supported for nodes.\n");
1164 only_reachable = true;
1170 fprintf(stderr, "Invalid number of arguments.\n");
1175 if(!strcasecmp(argv[1], "invitations")) {
1176 return dump_invitations();
1179 if(!connect_tincd(true)) {
1185 if(!strcasecmp(argv[1], "nodes")) {
1186 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1187 } else if(!strcasecmp(argv[1], "edges")) {
1188 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1189 } else if(!strcasecmp(argv[1], "subnets")) {
1190 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1191 } else if(!strcasecmp(argv[1], "connections")) {
1192 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1193 } else if(!strcasecmp(argv[1], "graph")) {
1194 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1195 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1197 } else if(!strcasecmp(argv[1], "digraph")) {
1198 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1199 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1202 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1208 printf("graph {\n");
1209 } else if(do_graph == 2) {
1210 printf("digraph {\n");
1213 while(recvline(fd, line, sizeof(line))) {
1214 char node1[4096], node2[4096];
1215 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1218 if(do_graph && req == REQ_DUMP_NODES) {
1240 char local_host[4096];
1241 char local_port[4096];
1244 int cipher, digest, maclength, compression, distance, socket, weight;
1245 short int pmtu, minmtu, maxmtu;
1246 unsigned int options;
1247 node_status_t status;
1248 long int last_state_change;
1250 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1253 case REQ_DUMP_NODES: {
1254 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);
1257 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1262 const char *color = "black";
1264 if(!strcmp(host, "MYSELF")) {
1266 } else if(!status.reachable) {
1268 } else if(strcmp(via, node)) {
1270 } else if(!status.validkey) {
1272 } else if(minmtu > 0) {
1276 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1278 if(only_reachable && !status.reachable) {
1282 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,
1283 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);
1285 if(udp_ping_rtt != -1) {
1286 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1294 case REQ_DUMP_EDGES: {
1295 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);
1298 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1303 float w = 1.0f + 65536.0f / (float)weight;
1305 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1306 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1307 } else if(do_graph == 2) {
1308 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1311 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);
1316 case REQ_DUMP_SUBNETS: {
1317 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1320 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1324 printf("%s owner %s\n", strip_weight(subnet), node);
1328 case REQ_DUMP_CONNECTIONS: {
1329 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1332 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1336 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1341 fprintf(stderr, "Unable to parse dump from tincd.\n");
1346 fprintf(stderr, "Error receiving dump.\n");
1350 static int cmd_purge(int argc, char *argv[]) {
1354 fprintf(stderr, "Too many arguments!\n");
1358 if(!connect_tincd(true)) {
1362 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1364 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1365 fprintf(stderr, "Could not purge old information.\n");
1372 static int cmd_debug(int argc, char *argv[]) {
1374 fprintf(stderr, "Invalid number of arguments.\n");
1378 if(!connect_tincd(true)) {
1382 int debuglevel = atoi(argv[1]);
1385 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1387 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1388 fprintf(stderr, "Could not set debug level.\n");
1392 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1396 static int cmd_retry(int argc, char *argv[]) {
1400 fprintf(stderr, "Too many arguments!\n");
1404 if(!connect_tincd(true)) {
1408 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1410 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1411 fprintf(stderr, "Could not retry outgoing connections.\n");
1418 static int cmd_connect(int argc, char *argv[]) {
1420 fprintf(stderr, "Invalid number of arguments.\n");
1424 if(!check_id(argv[1])) {
1425 fprintf(stderr, "Invalid name for node.\n");
1429 if(!connect_tincd(true)) {
1433 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1435 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1436 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1443 static int cmd_disconnect(int argc, char *argv[]) {
1445 fprintf(stderr, "Invalid number of arguments.\n");
1449 if(!check_id(argv[1])) {
1450 fprintf(stderr, "Invalid name for node.\n");
1454 if(!connect_tincd(true)) {
1458 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1460 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1461 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1468 static int cmd_top(int argc, char *argv[]) {
1472 fprintf(stderr, "Too many arguments!\n");
1478 if(!connect_tincd(true)) {
1485 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1490 static int cmd_pcap(int argc, char *argv[]) {
1492 fprintf(stderr, "Too many arguments!\n");
1496 if(!connect_tincd(true)) {
1500 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1505 static void sigint_handler(int sig) {
1508 fprintf(stderr, "\n");
1509 shutdown(fd, SHUT_RDWR);
1513 static int cmd_log(int argc, char *argv[]) {
1515 fprintf(stderr, "Too many arguments!\n");
1519 if(!connect_tincd(true)) {
1524 signal(SIGINT, sigint_handler);
1527 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1530 signal(SIGINT, SIG_DFL);
1538 static int cmd_pid(int argc, char *argv[]) {
1542 fprintf(stderr, "Too many arguments!\n");
1546 if(!connect_tincd(true) || !pid) {
1550 printf("%d\n", pid);
1554 size_t rstrip(char *value) {
1555 size_t len = strlen(value);
1557 while(len && strchr("\t\r\n ", value[len - 1])) {
1564 char *get_my_name(bool verbose) {
1565 FILE *f = fopen(tinc_conf, "r");
1569 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1578 while(fgets(buf, sizeof(buf), f)) {
1579 size_t len = strcspn(buf, "\t =");
1581 value += strspn(value, "\t ");
1585 value += strspn(value, "\t ");
1588 if(!rstrip(value)) {
1594 if(strcasecmp(buf, "Name")) {
1600 return replace_name(value);
1607 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1613 ecdsa_t *get_pubkey(FILE *f) {
1617 while(fgets(buf, sizeof(buf), f)) {
1618 size_t len = strcspn(buf, "\t =");
1620 value += strspn(value, "\t ");
1624 value += strspn(value, "\t ");
1627 if(!rstrip(value)) {
1633 if(strcasecmp(buf, "Ed25519PublicKey")) {
1638 return ecdsa_set_base64_public_key(value);
1645 const var_t variables[] = {
1646 /* Server configuration */
1647 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1648 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1649 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1650 {"BindToInterface", VAR_SERVER},
1651 {"Broadcast", VAR_SERVER | VAR_SAFE},
1652 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1653 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1654 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1655 {"Device", VAR_SERVER},
1656 {"DeviceStandby", VAR_SERVER},
1657 {"DeviceType", VAR_SERVER},
1658 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1659 {"Ed25519PrivateKeyFile", VAR_SERVER},
1660 {"ExperimentalProtocol", VAR_SERVER},
1661 {"Forwarding", VAR_SERVER},
1662 {"FWMark", VAR_SERVER},
1663 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1664 {"Hostnames", VAR_SERVER},
1665 {"IffOneQueue", VAR_SERVER},
1666 {"Interface", VAR_SERVER},
1667 {"InvitationExpire", VAR_SERVER},
1668 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1669 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1670 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1671 {"LogLevel", VAR_SERVER},
1672 {"MACExpire", VAR_SERVER | VAR_SAFE},
1673 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1674 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1675 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1676 {"Mode", VAR_SERVER | VAR_SAFE},
1677 {"Name", VAR_SERVER},
1678 {"PingInterval", VAR_SERVER | VAR_SAFE},
1679 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1680 {"PriorityInheritance", VAR_SERVER},
1681 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1682 {"PrivateKeyFile", VAR_SERVER},
1683 {"ProcessPriority", VAR_SERVER},
1684 {"Proxy", VAR_SERVER},
1685 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1686 {"ScriptsExtension", VAR_SERVER},
1687 {"ScriptsInterpreter", VAR_SERVER},
1688 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1689 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1690 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1691 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1692 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1693 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1694 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1695 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1696 {"UDPRcvBuf", VAR_SERVER},
1697 {"UDPSndBuf", VAR_SERVER},
1698 {"UPnP", VAR_SERVER},
1699 {"UPnPDiscoverWait", VAR_SERVER},
1700 {"UPnPRefreshPeriod", VAR_SERVER},
1701 {"VDEGroup", VAR_SERVER},
1702 {"VDEPort", VAR_SERVER},
1703 /* Host configuration */
1704 {"Address", VAR_HOST | VAR_MULTIPLE},
1705 {"Cipher", VAR_SERVER | VAR_HOST},
1706 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1707 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1708 {"Digest", VAR_SERVER | VAR_HOST},
1709 {"Ed25519PublicKey", VAR_HOST},
1710 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1711 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1712 {"MACLength", VAR_SERVER | VAR_HOST},
1713 {"PMTU", VAR_SERVER | VAR_HOST},
1714 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1716 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1717 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1718 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1719 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1720 {"Weight", VAR_HOST | VAR_SAFE},
1724 static int cmd_config(int argc, char *argv[]) {
1726 fprintf(stderr, "Invalid number of arguments.\n");
1730 if(strcasecmp(argv[0], "config")) {
1736 if(!strcasecmp(argv[1], "get")) {
1738 } else if(!strcasecmp(argv[1], "add")) {
1739 argv++, argc--, action = 1;
1740 } else if(!strcasecmp(argv[1], "del")) {
1741 argv++, argc--, action = -1;
1742 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1743 argv++, argc--, action = 0;
1747 fprintf(stderr, "Invalid number of arguments.\n");
1751 // Concatenate the rest of the command line
1752 strncpy(line, argv[1], sizeof(line) - 1);
1754 for(int i = 2; i < argc; i++) {
1755 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1756 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1759 // Liberal parsing into node name, variable name and value.
1765 len = strcspn(line, "\t =");
1767 value += strspn(value, "\t ");
1771 value += strspn(value, "\t ");
1775 variable = strchr(line, '.');
1785 fprintf(stderr, "No variable given.\n");
1789 if(action >= 0 && !*value) {
1790 fprintf(stderr, "No value for variable given.\n");
1794 if(action < -1 && *value) {
1798 /* Some simple checks. */
1800 bool warnonremove = false;
1802 for(int i = 0; variables[i].name; i++) {
1803 if(strcasecmp(variables[i].name, variable)) {
1808 variable = (char *)variables[i].name;
1810 if(!strcasecmp(variable, "Subnet") && *value) {
1813 if(!str2net(&s, value)) {
1814 fprintf(stderr, "Malformed subnet definition %s\n", value);
1818 if(!subnetcheck(s)) {
1819 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1824 /* Discourage use of obsolete variables. */
1826 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1828 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1830 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1835 /* Don't put server variables in host config files */
1837 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1839 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1841 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1846 /* Should this go into our own host config file? */
1848 if(!node && !(variables[i].type & VAR_SERVER)) {
1849 node = get_my_name(true);
1856 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1857 Turn on warnings when it seems variables might be removed unintentionally. */
1859 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1860 warnonremove = true;
1862 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1863 warnonremove = true;
1869 if(node && !check_id(node)) {
1870 fprintf(stderr, "Invalid name for node.\n");
1880 if(force || action < 0) {
1881 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1883 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1885 if(node && node != line) {
1893 // Open the right configuration file.
1894 char filename[PATH_MAX];
1897 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1904 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1907 FILE *f = fopen(filename, "r");
1910 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1914 char tmpfile[PATH_MAX];
1918 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1919 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1923 tf = fopen(tmpfile, "w");
1926 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1932 // Copy the file, making modifications on the fly, unless we are just getting a value.
1936 bool removed = false;
1939 while(fgets(buf1, sizeof(buf1), f)) {
1940 buf1[sizeof(buf1) - 1] = 0;
1941 strncpy(buf2, buf1, sizeof(buf2));
1943 // Parse line in a simple way
1946 size_t len = strcspn(buf2, "\t =");
1947 bvalue = buf2 + len;
1948 bvalue += strspn(bvalue, "\t ");
1950 if(*bvalue == '=') {
1952 bvalue += strspn(bvalue, "\t ");
1959 if(!strcasecmp(buf2, variable)) {
1963 printf("%s\n", bvalue);
1965 } else if(action == -1) {
1966 if(!*value || !strcasecmp(bvalue, value)) {
1972 } else if(action == 0) {
1973 // Warn if "set" was used for variables that can occur multiple times
1974 if(warnonremove && strcasecmp(bvalue, value)) {
1975 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1978 // Already set? Delete the rest...
1983 // Otherwise, replace.
1984 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1985 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1992 } else if(action > 0) {
1993 // Check if we've already seen this variable with the same value
1994 if(!strcasecmp(bvalue, value)) {
2001 // Copy original line...
2002 if(fputs(buf1, tf) < 0) {
2003 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2007 // Add newline if it is missing...
2008 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2009 if(fputc('\n', tf) < 0) {
2010 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2017 // Make sure we read everything...
2018 if(ferror(f) || !feof(f)) {
2019 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2024 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2028 // Add new variable if necessary.
2029 if((action > 0 && !found) || (action == 0 && !set)) {
2030 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2031 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2040 fprintf(stderr, "No matching configuration variables found.\n");
2045 // Make sure we wrote everything...
2047 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2051 // Could we find what we had to remove?
2052 if(action < 0 && !removed) {
2054 fprintf(stderr, "No configuration variables deleted.\n");
2058 // Replace the configuration file with the new one
2061 if(remove(filename)) {
2062 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2068 if(rename(tmpfile, filename)) {
2069 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2073 // Silently try notifying a running tincd of changes.
2074 if(connect_tincd(false)) {
2075 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2081 static bool try_bind(int port) {
2082 struct addrinfo *ai = NULL, *aip;
2083 struct addrinfo hint = {
2084 .ai_flags = AI_PASSIVE,
2085 .ai_family = AF_UNSPEC,
2086 .ai_socktype = SOCK_STREAM,
2087 .ai_protocol = IPPROTO_TCP,
2090 bool success = true;
2092 snprintf(portstr, sizeof(portstr), "%d", port);
2094 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2098 for(aip = ai; aip; aip = aip->ai_next) {
2099 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2106 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2119 int check_port(const char *name) {
2124 fprintf(stderr, "Warning: could not bind to port 655. ");
2126 for(int i = 0; i < 100; i++) {
2127 uint16_t port = 0x1000 + prng(0x8000);
2129 if(try_bind(port)) {
2130 char filename[PATH_MAX];
2131 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2132 FILE *f = fopen(filename, "a");
2135 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2136 fprintf(stderr, "Please change tinc's Port manually.\n");
2140 fprintf(f, "Port = %d\n", port);
2142 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2147 fprintf(stderr, "Please change tinc's Port manually.\n");
2151 static int cmd_init(int argc, char *argv[]) {
2152 if(!access(tinc_conf, F_OK)) {
2153 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2158 fprintf(stderr, "Too many arguments!\n");
2160 } else if(argc < 2) {
2163 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2165 if(!fgets(buf, sizeof(buf), stdin)) {
2166 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2170 size_t len = rstrip(buf);
2173 fprintf(stderr, "No name given!\n");
2179 fprintf(stderr, "No Name given!\n");
2183 name = strdup(argv[1]);
2186 fprintf(stderr, "No Name given!\n");
2191 if(!check_id(name)) {
2192 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2196 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2197 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2201 if(mkdir(confbase, 0777) && errno != EEXIST) {
2202 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2206 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2207 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2211 FILE *f = fopen(tinc_conf, "w");
2214 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2218 fprintf(f, "Name = %s\n", name);
2221 #ifndef DISABLE_LEGACY
2223 if(!rsa_keygen(2048, false)) {
2229 if(!ed25519_keygen(false)) {
2236 char filename[PATH_MAX];
2237 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2239 if(access(filename, F_OK)) {
2240 FILE *f = fopenmask(filename, "w", 0777);
2243 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2247 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");
2257 static int cmd_generate_keys(int argc, char *argv[]) {
2258 #ifdef DISABLE_LEGACY
2266 fprintf(stderr, "Too many arguments!\n");
2271 name = get_my_name(false);
2274 #ifndef DISABLE_LEGACY
2276 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2282 if(!ed25519_keygen(true)) {
2289 #ifndef DISABLE_LEGACY
2290 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2292 fprintf(stderr, "Too many arguments!\n");
2297 name = get_my_name(false);
2300 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2304 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2308 fprintf(stderr, "Too many arguments!\n");
2313 name = get_my_name(false);
2316 return !ed25519_keygen(true);
2319 static int cmd_help(int argc, char *argv[]) {
2327 static int cmd_version(int argc, char *argv[]) {
2331 fprintf(stderr, "Too many arguments!\n");
2339 static int cmd_info(int argc, char *argv[]) {
2341 fprintf(stderr, "Invalid number of arguments.\n");
2345 if(!connect_tincd(true)) {
2349 return info(fd, argv[1]);
2352 static const char *conffiles[] = {
2363 static int cmd_edit(int argc, char *argv[]) {
2365 fprintf(stderr, "Invalid number of arguments.\n");
2369 char filename[PATH_MAX] = "";
2371 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2372 for(int i = 0; conffiles[i]; i++) {
2373 if(!strcmp(argv[1], conffiles[i])) {
2374 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2383 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2384 char *dash = strchr(argv[1], '-');
2389 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2390 fprintf(stderr, "Invalid configuration filename.\n");
2398 const char *editor = getenv("VISUAL");
2401 editor = getenv("EDITOR");
2408 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2410 xasprintf(&command, "edit \"%s\"", filename);
2412 int result = system(command);
2419 // Silently try notifying a running tincd of changes.
2420 if(connect_tincd(false)) {
2421 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2427 static int export(const char *name, FILE *out) {
2428 char filename[PATH_MAX];
2429 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2430 FILE *in = fopen(filename, "r");
2433 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2437 fprintf(out, "Name = %s\n", name);
2440 while(fgets(buf, sizeof(buf), in)) {
2441 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2447 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2456 static int cmd_export(int argc, char *argv[]) {
2460 fprintf(stderr, "Too many arguments!\n");
2464 char *name = get_my_name(true);
2470 int result = export(name, stdout);
2480 static int cmd_export_all(int argc, char *argv[]) {
2484 fprintf(stderr, "Too many arguments!\n");
2488 DIR *dir = opendir(hosts_dir);
2491 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2499 while((ent = readdir(dir))) {
2500 if(!check_id(ent->d_name)) {
2507 printf("#---------------------------------------------------------------#\n");
2510 result |= export(ent->d_name, stdout);
2522 static int cmd_import(int argc, char *argv[]) {
2526 fprintf(stderr, "Too many arguments!\n");
2535 char filename[PATH_MAX] = "";
2537 bool firstline = true;
2539 while(fgets(buf, sizeof(buf), in)) {
2540 if(sscanf(buf, "Name = %4095s", name) == 1) {
2543 if(!check_id(name)) {
2544 fprintf(stderr, "Invalid Name in input!\n");
2552 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2553 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2557 if(!force && !access(filename, F_OK)) {
2558 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2563 out = fopen(filename, "w");
2566 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2572 } else if(firstline) {
2573 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2578 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2583 if(fputs(buf, out) < 0) {
2584 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2595 fprintf(stderr, "Imported %d host configuration files.\n", count);
2598 fprintf(stderr, "No host configuration files imported.\n");
2603 static int cmd_exchange(int argc, char *argv[]) {
2604 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2607 static int cmd_exchange_all(int argc, char *argv[]) {
2608 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2611 static int switch_network(char *name) {
2612 if(strcmp(name, ".")) {
2613 if(!check_netname(name, false)) {
2614 fprintf(stderr, "Invalid character in netname!\n");
2618 if(!check_netname(name, true)) {
2619 fprintf(stderr, "Warning: unsafe character in netname!\n");
2629 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2636 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2637 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2638 xasprintf(&prompt, "%s> ", identname);
2643 static int cmd_network(int argc, char *argv[]) {
2645 fprintf(stderr, "Too many arguments!\n");
2650 return switch_network(argv[1]);
2653 DIR *dir = opendir(confdir);
2656 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2662 while((ent = readdir(dir))) {
2663 if(*ent->d_name == '.') {
2667 if(!strcmp(ent->d_name, "tinc.conf")) {
2672 char fname[PATH_MAX];
2673 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2675 if(!access(fname, R_OK)) {
2676 printf("%s\n", ent->d_name);
2685 static int cmd_fsck(int argc, char *argv[]) {
2689 fprintf(stderr, "Too many arguments!\n");
2693 return fsck(orig_argv[0]);
2696 static void *readfile(FILE *in, size_t *len) {
2698 size_t bufsize = 4096;
2699 char *buf = xmalloc(bufsize);
2702 size_t read = fread(buf + count, 1, bufsize - count, in);
2710 if(count >= bufsize) {
2712 buf = xrealloc(buf, bufsize);
2723 static int cmd_sign(int argc, char *argv[]) {
2725 fprintf(stderr, "Too many arguments!\n");
2730 name = get_my_name(true);
2737 char fname[PATH_MAX];
2738 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2739 FILE *fp = fopen(fname, "r");
2742 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2746 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2749 fprintf(stderr, "Could not read private key from %s\n", fname);
2759 in = fopen(argv[1], "rb");
2762 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2771 char *data = readfile(in, &len);
2778 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2783 // Ensure we sign our name and current time as well
2784 long t = time(NULL);
2786 xasprintf(&trailer, " %s %ld", name, t);
2787 size_t trailer_len = strlen(trailer);
2789 data = xrealloc(data, len + trailer_len);
2790 memcpy(data + len, trailer, trailer_len);
2795 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2796 fprintf(stderr, "Error generating signature\n");
2802 b64encode_tinc(sig, sig, 64);
2805 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2806 fwrite(data, len, 1, stdout);
2812 static int cmd_verify(int argc, char *argv[]) {
2814 fprintf(stderr, "Not enough arguments!\n");
2819 fprintf(stderr, "Too many arguments!\n");
2823 char *node = argv[1];
2825 if(!strcmp(node, ".")) {
2827 name = get_my_name(true);
2835 } else if(!strcmp(node, "*")) {
2838 if(!check_id(node)) {
2839 fprintf(stderr, "Invalid node name\n");
2847 in = fopen(argv[2], "rb");
2850 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2858 char *data = readfile(in, &len);
2865 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2869 char *newline = memchr(data, '\n', len);
2871 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2872 fprintf(stderr, "Invalid input\n");
2878 size_t skip = newline - data;
2880 char signer[MAX_STRING_SIZE] = "";
2881 char sig[MAX_STRING_SIZE] = "";
2884 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2885 fprintf(stderr, "Invalid input\n");
2890 if(node && strcmp(node, signer)) {
2891 fprintf(stderr, "Signature is not made by %s\n", node);
2901 xasprintf(&trailer, " %s %ld", signer, t);
2902 size_t trailer_len = strlen(trailer);
2904 data = xrealloc(data, len + trailer_len);
2905 memcpy(data + len, trailer, trailer_len);
2908 newline = data + skip;
2910 char fname[PATH_MAX];
2911 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2912 FILE *fp = fopen(fname, "r");
2915 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2920 ecdsa_t *key = get_pubkey(fp);
2924 key = ecdsa_read_pem_public_key(fp);
2928 fprintf(stderr, "Could not read public key from %s\n", fname);
2936 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2937 fprintf(stderr, "Invalid signature\n");
2945 fwrite(newline, len - (newline - data), 1, stdout);
2951 static const struct {
2952 const char *command;
2953 int (*function)(int argc, char *argv[]);
2956 {"start", cmd_start, false},
2957 {"stop", cmd_stop, false},
2958 {"restart", cmd_restart, false},
2959 {"reload", cmd_reload, false},
2960 {"dump", cmd_dump, false},
2961 {"list", cmd_dump, false},
2962 {"purge", cmd_purge, false},
2963 {"debug", cmd_debug, false},
2964 {"retry", cmd_retry, false},
2965 {"connect", cmd_connect, false},
2966 {"disconnect", cmd_disconnect, false},
2967 {"top", cmd_top, false},
2968 {"pcap", cmd_pcap, false},
2969 {"log", cmd_log, false},
2970 {"pid", cmd_pid, false},
2971 {"config", cmd_config, true},
2972 {"add", cmd_config, false},
2973 {"del", cmd_config, false},
2974 {"get", cmd_config, false},
2975 {"set", cmd_config, false},
2976 {"init", cmd_init, false},
2977 {"generate-keys", cmd_generate_keys, false},
2978 #ifndef DISABLE_LEGACY
2979 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
2981 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
2982 {"help", cmd_help, false},
2983 {"version", cmd_version, false},
2984 {"info", cmd_info, false},
2985 {"edit", cmd_edit, false},
2986 {"export", cmd_export, false},
2987 {"export-all", cmd_export_all, false},
2988 {"import", cmd_import, false},
2989 {"exchange", cmd_exchange, false},
2990 {"exchange-all", cmd_exchange_all, false},
2991 {"invite", cmd_invite, false},
2992 {"join", cmd_join, false},
2993 {"network", cmd_network, false},
2994 {"fsck", cmd_fsck, false},
2995 {"sign", cmd_sign, false},
2996 {"verify", cmd_verify, false},
2997 {NULL, NULL, false},
3000 #ifdef HAVE_READLINE
3001 static char *complete_command(const char *text, int state) {
3010 while(commands[i].command) {
3011 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3012 return xstrdup(commands[i].command);
3021 static char *complete_dump(const char *text, int state) {
3022 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3032 if(!strncasecmp(matches[i], text, strlen(text))) {
3033 return xstrdup(matches[i]);
3042 static char *complete_config(const char *text, int state) {
3051 while(variables[i].name) {
3052 char *dot = strchr(text, '.');
3055 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3057 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3061 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3062 return xstrdup(variables[i].name);
3072 static char *complete_info(const char *text, int state) {
3078 if(!connect_tincd(false)) {
3082 // Check the list of nodes
3083 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3084 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3087 while(recvline(fd, line, sizeof(line))) {
3089 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3102 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3106 if(!strncmp(item, text, strlen(text))) {
3107 return xstrdup(strip_weight(item));
3114 static char *complete_nothing(const char *text, int state) {
3120 static char **completion(const char *text, int start, int end) {
3122 char **matches = NULL;
3125 matches = rl_completion_matches(text, complete_command);
3126 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3127 matches = rl_completion_matches(text, complete_dump);
3128 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3129 matches = rl_completion_matches(text, complete_config);
3130 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3131 matches = rl_completion_matches(text, complete_config);
3132 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3133 matches = rl_completion_matches(text, complete_config);
3134 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3135 matches = rl_completion_matches(text, complete_config);
3136 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3137 matches = rl_completion_matches(text, complete_info);
3144 static int cmd_shell(int argc, char *argv[]) {
3145 xasprintf(&prompt, "%s> ", identname);
3149 int maxargs = argc + 16;
3150 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3152 for(int i = 0; i < argc; i++) {
3156 #ifdef HAVE_READLINE
3157 rl_readline_name = "tinc";
3158 rl_basic_word_break_characters = "\t\n ";
3159 rl_completion_entry_function = complete_nothing;
3160 rl_attempted_completion_function = completion;
3161 rl_filename_completion_desired = 0;
3166 #ifdef HAVE_READLINE
3171 line = readline(prompt);
3172 copy = line ? xstrdup(line) : NULL;
3174 line = fgets(buf, sizeof(buf), stdin);
3180 fputs(prompt, stdout);
3183 line = fgets(buf, sizeof(buf), stdin);
3190 /* Ignore comments */
3199 char *p = line + strspn(line, " \t\n");
3200 char *next = strtok(p, " \t\n");
3203 if(nargc >= maxargs) {
3205 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3210 next = strtok(NULL, " \t\n");
3217 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3218 #ifdef HAVE_READLINE
3227 for(int i = 0; commands[i].command; i++) {
3228 if(!strcasecmp(nargv[argc], commands[i].command)) {
3229 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3235 #ifdef HAVE_READLINE
3244 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3249 #ifdef HAVE_READLINE
3261 static void cleanup(void) {
3267 int main(int argc, char *argv[]) {
3268 program_name = argv[0];
3270 tty = isatty(0) && isatty(1);
3272 if(!parse_options(argc, argv)) {
3277 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3278 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3292 static struct WSAData wsa_state;
3294 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3295 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3301 gettimeofday(&now, NULL);
3305 if(optind >= argc) {
3306 return cmd_shell(argc, argv);
3309 for(int i = 0; commands[i].command; i++) {
3310 if(!strcasecmp(argv[optind], commands[i].command)) {
3311 return commands[i].function(argc - optind, argv + optind);
3315 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);