2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2021 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"
46 #define MSG_NOSIGNAL 0
49 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 bool netnamegiven = false;
75 char *scriptinterpreter = NULL;
76 char *scriptextension = "";
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 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
95 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
103 #ifndef DISABLE_LEGACY
107 printf("Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
108 "See the AUTHORS file for a complete list.\n\n"
109 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
110 "and you are welcome to redistribute it under certain conditions;\n"
111 "see the file COPYING for details.\n");
114 static void usage(bool status) {
116 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
118 printf("Usage: %s [options] command\n\n", program_name);
119 printf("Valid options are:\n"
120 " -b, --batch Don't ask for anything (non-interactive mode).\n"
121 " -c, --config=DIR Read configuration options from DIR.\n"
122 " -n, --net=NETNAME Connect to net NETNAME.\n"
123 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
124 " --force Force some commands to work despite warnings.\n"
125 " --help Display this help and exit.\n"
126 " --version Output version information and exit.\n"
128 "Valid commands are:\n"
129 " init [name] Create initial configuration files.\n"
130 " get VARIABLE Print current value of VARIABLE\n"
131 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
132 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
133 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
134 " start [tincd options] Start tincd.\n"
135 " stop Stop tincd.\n"
136 " restart [tincd options] Restart tincd.\n"
137 " reload Partially reload configuration of running tincd.\n"
138 " pid Show PID of currently running tincd.\n"
139 #ifdef DISABLE_LEGACY
140 " generate-keys Generate a new Ed25519 public/private key pair.\n"
142 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
143 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
145 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
146 " dump Dump a list of one of the following things:\n"
147 " [reachable] nodes - all known nodes in the VPN\n"
148 " edges - all known connections in the VPN\n"
149 " subnets - all known subnets in the VPN\n"
150 " connections - all meta connections with ourself\n"
151 " [di]graph - graph of the VPN in dotty format\n"
152 " invitations - outstanding invitations\n"
153 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
154 " purge Purge unreachable nodes\n"
155 " debug N Set debug level\n"
156 " retry Retry all outgoing connections\n"
157 " disconnect NODE Close meta connection with NODE\n"
159 " top Show real-time statistics\n"
161 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
162 " log [level] Dump log output [up to the specified level]\n"
163 " export Export host configuration of local node to standard output\n"
164 " export-all Export all host configuration files to standard output\n"
165 " import Import host configuration file(s) from standard input\n"
166 " exchange Same as export followed by import\n"
167 " exchange-all Same as export-all followed by import\n"
168 " invite NODE [...] Generate an invitation for NODE\n"
169 " join INVITATION Join a VPN using an INVITATION\n"
170 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
171 " fsck Check the configuration files for problems.\n"
172 " sign [FILE] Generate a signed version of a file.\n"
173 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
175 printf("Report bugs to tinc@tinc-vpn.org.\n");
179 static bool parse_options(int argc, char **argv) {
181 int option_index = 0;
183 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
185 case 0: /* long option */
192 case 'c': /* config file */
193 confbase = xstrdup(optarg);
194 confbasegiven = true;
197 case 'n': /* net name given */
198 netname = xstrdup(optarg);
201 case 1: /* show help */
205 case 2: /* show version */
209 case 3: /* open control socket here */
210 pidfilename = xstrdup(optarg);
217 case '?': /* wrong options */
226 if(!netname && (netname = getenv("NETNAME"))) {
227 netname = xstrdup(netname);
230 /* netname "." is special: a "top-level name" */
232 if(netname && (!*netname || !strcmp(netname, "."))) {
237 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
238 fprintf(stderr, "Invalid character in netname!\n");
245 /* Open a file with the desired permissions, minus the umask.
246 Also, if we want to create an executable file, we call fchmod()
247 to set the executable bits. */
249 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
250 mode_t mask = umask(0);
252 umask(~perms & 0777);
253 FILE *f = fopen(filename, mode);
256 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
262 if((perms & 0444) && f) {
263 fchmod(fileno(f), perms);
271 static void disable_old_keys(const char *filename, const char *what) {
272 char tmpfile[PATH_MAX] = "";
274 bool disabled = false;
278 FILE *r = fopen(filename, "r");
285 int result = snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
287 if(result < sizeof(tmpfile)) {
288 struct stat st = {.st_mode = 0600};
289 fstat(fileno(r), &st);
290 w = fopenmask(tmpfile, "w", st.st_mode);
293 while(fgets(buf, sizeof(buf), r)) {
294 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
295 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
301 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
308 if(block || ed25519pubkey) {
312 if(fputs(buf, w) < 0) {
318 if(block && !strncmp(buf, "-----END ", 9)) {
328 if(ferror(r) || fclose(r) < 0) {
334 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
344 // We cannot atomically replace files on Windows.
345 char bakfile[PATH_MAX] = "";
346 snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
348 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
349 rename(bakfile, filename);
352 if(rename(tmpfile, filename)) {
354 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
359 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
366 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
368 char directory[PATH_MAX] = ".";
374 /* Check stdin and stdout */
376 /* Ask for a file and/or directory name. */
377 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
379 if(fgets(buf, sizeof(buf), stdin) == NULL) {
380 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
384 size_t len = strlen(buf);
397 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
400 if(filename[0] != '/') {
402 /* The directory is a relative path or a filename. */
403 getcwd(directory, sizeof(directory));
405 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
406 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
418 disable_old_keys(filename, what);
420 /* Open it first to keep the inode busy */
422 r = fopenmask(filename, mode, perms);
425 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
433 Generate a public/private Ed25519 key pair, and ask for a file to store
436 static bool ed25519_keygen(bool ask) {
439 char fname[PATH_MAX];
441 fprintf(stderr, "Generating Ed25519 key pair:\n");
443 if(!(key = ecdsa_generate())) {
444 fprintf(stderr, "Error during key generation!\n");
447 fprintf(stderr, "Done.\n");
450 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
451 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
457 if(!ecdsa_write_pem_private_key(key, f)) {
458 fprintf(stderr, "Error writing private key!\n");
465 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
467 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
470 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
476 char *pubkey = ecdsa_get_base64_public_key(key);
477 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
495 #ifndef DISABLE_LEGACY
497 Generate a public/private RSA key pair, and ask for a file to store
500 static bool rsa_keygen(int bits, bool ask) {
503 char fname[PATH_MAX];
505 // Make sure the key size is a multiple of 8 bits.
508 // Make sure that a valid key size is used.
509 if(bits < 1024 || bits > 8192) {
510 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
512 } else if(bits < 2048) {
513 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
516 fprintf(stderr, "Generating %d bits keys:\n", bits);
518 if(!(key = rsa_generate(bits, 0x10001))) {
519 fprintf(stderr, "Error during key generation!\n");
522 fprintf(stderr, "Done.\n");
525 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
526 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
532 if(!rsa_write_pem_private_key(key, f)) {
533 fprintf(stderr, "Error writing private key!\n");
540 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
542 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
545 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
551 if(!rsa_write_pem_public_key(key, f)) {
552 fprintf(stderr, "Error writing public key!\n");
575 bool recvline(int fd, char *line, size_t len) {
576 char *newline = NULL;
582 while(!(newline = memchr(buffer, '\n', blen))) {
583 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
585 if(result == -1 && sockerrno == EINTR) {
587 } else if(result <= 0) {
594 if((size_t)(newline - buffer) >= len) {
598 len = newline - buffer;
600 memcpy(line, buffer, len);
602 memmove(buffer, newline + 1, blen - len - 1);
608 static bool recvdata(int fd, char *data, size_t len) {
610 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
612 if(result == -1 && sockerrno == EINTR) {
614 } else if(result <= 0) {
621 memcpy(data, buffer, len);
622 memmove(buffer, buffer + len, blen - len);
628 bool sendline(int fd, char *format, ...) {
629 static char buffer[4096];
634 va_start(ap, format);
635 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
636 buffer[sizeof(buffer) - 1] = 0;
639 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
647 int result = send(fd, p, blen, MSG_NOSIGNAL);
649 if(result == -1 && sockerrno == EINTR) {
651 } else if(result <= 0) {
662 static void pcap(int fd, FILE *out, uint32_t snaplen) {
663 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
671 uint32_t tz_accuracy;
678 snaplen ? snaplen : sizeof(data),
691 fwrite(&header, sizeof(header), 1, out);
696 while(recvline(fd, line, sizeof(line))) {
698 int n = sscanf(line, "%d %d %d", &code, &req, &len);
699 gettimeofday(&tv, NULL);
701 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || (size_t)len > sizeof(data)) {
705 if(!recvdata(fd, data, len)) {
709 packet.tv_sec = tv.tv_sec;
710 packet.tv_usec = tv.tv_usec;
712 packet.origlen = len;
713 fwrite(&packet, sizeof(packet), 1, out);
714 fwrite(data, len, 1, out);
719 static void logcontrol(int fd, FILE *out, int level) {
720 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
724 while(recvline(fd, line, sizeof(line))) {
726 int n = sscanf(line, "%d %d %d", &code, &req, &len);
728 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
732 if(!recvdata(fd, data, len)) {
736 fwrite(data, len, 1, out);
742 static bool stop_tincd(void) {
743 if(!connect_tincd(true)) {
747 sendline(fd, "%d %d", CONTROL, REQ_STOP);
749 while(recvline(fd, line, sizeof(line))) {
750 // wait for tincd to close the connection...
761 static bool remove_service(void) {
762 SC_HANDLE manager = NULL;
763 SC_HANDLE service = NULL;
764 SERVICE_STATUS status = {0};
765 bool success = false;
767 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
770 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
774 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
777 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
778 success = stop_tincd();
780 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
786 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
787 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
789 fprintf(stderr, "%s service stopped\n", identname);
792 if(!DeleteService(service)) {
793 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
802 CloseServiceHandle(service);
806 CloseServiceHandle(manager);
810 fprintf(stderr, "%s service removed\n", identname);
817 bool connect_tincd(bool verbose) {
822 struct timeval tv = {0, 0};
824 if(select(fd + 1, &r, NULL, NULL, &tv)) {
825 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
833 FILE *f = fopen(pidfilename, "r");
837 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
846 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
848 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
859 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
860 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
861 /* clean up the stale socket and pid file */
863 unlink(unixsocketname);
867 struct sockaddr_un sa = {
868 .sun_family = AF_UNIX,
871 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
872 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
876 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
878 fd = socket(AF_UNIX, SOCK_STREAM, 0);
882 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
888 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
890 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
899 struct addrinfo hints = {
900 .ai_family = AF_UNSPEC,
901 .ai_socktype = SOCK_STREAM,
902 .ai_protocol = IPPROTO_TCP,
906 struct addrinfo *res = NULL;
908 if(getaddrinfo(host, port, &hints, &res) || !res) {
910 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
916 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
920 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
926 unsigned long arg = 0;
928 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
930 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
934 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
936 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
948 static const int one = 1;
949 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
952 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
957 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
959 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
967 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
969 fprintf(stderr, "Could not fully establish control socket connection\n");
981 static int cmd_start(int argc, char *argv[]) {
982 if(connect_tincd(false)) {
984 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
986 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
993 char *slash = strrchr(program_name, '/');
997 if((c = strrchr(program_name, '\\')) > slash) {
1004 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
1010 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
1015 Windows has no real concept of an "argv array". A command line is just one string.
1016 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
1017 it uses quotes to handle spaces in arguments.
1018 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
1019 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
1020 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
1022 xasprintf(&arg0, "\"%s\"", arg0);
1024 nargv[nargc++] = arg0;
1026 for(int i = 1; i < optind; i++) {
1027 nargv[nargc++] = orig_argv[i];
1030 for(int i = 1; i < argc; i++) {
1031 nargv[nargc++] = argv[i];
1035 int status = spawnvp(_P_WAIT, c, nargv);
1038 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
1044 int pfd[2] = {-1, -1};
1046 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
1047 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
1055 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1063 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1064 setenv("TINC_UMBILICAL", buf, true);
1065 exit(execvp(c, nargv));
1072 int status = -1, result;
1074 signal(SIGINT, SIG_IGN);
1077 // Pass all log messages from the umbilical to stderr.
1078 // A nul-byte right before closure means tincd started successfully.
1079 bool failure = true;
1083 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1084 failure = buf[len - 1];
1099 // Make sure the child process is really gone.
1100 result = waitpid(pid, &status, 0);
1103 signal(SIGINT, SIG_DFL);
1106 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1107 fprintf(stderr, "Error starting %s\n", c);
1115 static int cmd_stop(int argc, char *argv[]) {
1119 fprintf(stderr, "Too many arguments!\n");
1124 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1129 if(kill(pid, SIGTERM)) {
1130 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1134 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1135 waitpid(pid, NULL, 0);
1146 static int cmd_restart(int argc, char *argv[]) {
1148 return cmd_start(argc, argv);
1151 static int cmd_reload(int argc, char *argv[]) {
1155 fprintf(stderr, "Too many arguments!\n");
1159 if(!connect_tincd(true)) {
1163 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1165 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1166 fprintf(stderr, "Could not reload configuration.\n");
1174 static int dump_invitations(void) {
1175 char dname[PATH_MAX];
1176 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1177 DIR *dir = opendir(dname);
1180 if(errno == ENOENT) {
1181 fprintf(stderr, "No outstanding invitations.\n");
1185 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1193 while((ent = readdir(dir))) {
1194 char buf[MAX_STRING_SIZE];
1196 if(b64decode(ent->d_name, buf, 24) != 18) {
1200 char fname[PATH_MAX];
1202 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1203 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1207 FILE *f = fopen(fname, "r");
1210 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1216 if(!fgets(buf, sizeof(buf), f)) {
1217 fprintf(stderr, "Invalid invitation file %s\n", fname);
1224 char *eol = buf + strlen(buf);
1226 while(strchr("\t \r\n", *--eol)) {
1230 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1231 fprintf(stderr, "Invalid invitation file %s\n", fname);
1236 printf("%s %s\n", ent->d_name, buf + 7);
1242 fprintf(stderr, "No outstanding invitations.\n");
1248 static int cmd_dump(int argc, char *argv[]) {
1249 bool only_reachable = false;
1251 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1252 if(strcasecmp(argv[2], "nodes")) {
1253 fprintf(stderr, "`reachable' only supported for nodes.\n");
1258 only_reachable = true;
1264 fprintf(stderr, "Invalid number of arguments.\n");
1269 if(!strcasecmp(argv[1], "invitations")) {
1270 return dump_invitations();
1273 if(!connect_tincd(true)) {
1279 if(!strcasecmp(argv[1], "nodes")) {
1280 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1281 } else if(!strcasecmp(argv[1], "edges")) {
1282 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1283 } else if(!strcasecmp(argv[1], "subnets")) {
1284 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1285 } else if(!strcasecmp(argv[1], "connections")) {
1286 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1287 } else if(!strcasecmp(argv[1], "graph")) {
1288 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1289 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1291 } else if(!strcasecmp(argv[1], "digraph")) {
1292 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1293 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1296 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1302 printf("graph {\n");
1303 } else if(do_graph == 2) {
1304 printf("digraph {\n");
1307 while(recvline(fd, line, sizeof(line))) {
1308 char node1[4096], node2[4096];
1309 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1312 if(do_graph && req == REQ_DUMP_NODES) {
1334 char local_host[4096];
1335 char local_port[4096];
1338 int cipher, digest, maclength, compression, distance, socket, weight;
1339 short int pmtu, minmtu, maxmtu;
1340 unsigned int options, status_int;
1341 node_status_t status;
1342 long int last_state_change;
1344 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1347 case REQ_DUMP_NODES: {
1348 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %d %d %d %d %x %x %4095s %4095s %d %hd %hd %hd %ld %d %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change, &udp_ping_rtt, &in_packets, &in_bytes, &out_packets, &out_bytes);
1351 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1355 memcpy(&status, &status_int, sizeof(status));
1358 const char *color = "black";
1360 if(!strcmp(host, "MYSELF")) {
1362 } else if(!status.reachable) {
1364 } else if(strcmp(via, node)) {
1366 } else if(!status.validkey) {
1368 } else if(minmtu > 0) {
1372 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1374 if(only_reachable && !status.reachable) {
1378 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,
1379 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu, in_packets, in_bytes, out_packets, out_bytes);
1381 if(udp_ping_rtt != -1) {
1382 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1390 case REQ_DUMP_EDGES: {
1391 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);
1394 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1399 float w = 1 + 65536.0 / weight;
1401 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1402 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1403 } else if(do_graph == 2) {
1404 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1407 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);
1412 case REQ_DUMP_SUBNETS: {
1413 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1416 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1420 printf("%s owner %s\n", strip_weight(subnet), node);
1424 case REQ_DUMP_CONNECTIONS: {
1425 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1428 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1432 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1437 fprintf(stderr, "Unable to parse dump from tincd.\n");
1442 fprintf(stderr, "Error receiving dump.\n");
1446 static int cmd_purge(int argc, char *argv[]) {
1450 fprintf(stderr, "Too many arguments!\n");
1454 if(!connect_tincd(true)) {
1458 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1460 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1461 fprintf(stderr, "Could not purge old information.\n");
1468 static int cmd_debug(int argc, char *argv[]) {
1470 fprintf(stderr, "Invalid number of arguments.\n");
1474 if(!connect_tincd(true)) {
1478 int debuglevel = atoi(argv[1]);
1481 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1483 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1484 fprintf(stderr, "Could not set debug level.\n");
1488 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1492 static int cmd_retry(int argc, char *argv[]) {
1496 fprintf(stderr, "Too many arguments!\n");
1500 if(!connect_tincd(true)) {
1504 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1506 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1507 fprintf(stderr, "Could not retry outgoing connections.\n");
1514 static int cmd_connect(int argc, char *argv[]) {
1516 fprintf(stderr, "Invalid number of arguments.\n");
1520 if(!check_id(argv[1])) {
1521 fprintf(stderr, "Invalid name for node.\n");
1525 if(!connect_tincd(true)) {
1529 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1531 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1532 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1539 static int cmd_disconnect(int argc, char *argv[]) {
1541 fprintf(stderr, "Invalid number of arguments.\n");
1545 if(!check_id(argv[1])) {
1546 fprintf(stderr, "Invalid name for node.\n");
1550 if(!connect_tincd(true)) {
1554 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1556 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1557 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1564 static int cmd_top(int argc, char *argv[]) {
1568 fprintf(stderr, "Too many arguments!\n");
1574 if(!connect_tincd(true)) {
1581 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1586 static int cmd_pcap(int argc, char *argv[]) {
1588 fprintf(stderr, "Too many arguments!\n");
1592 if(!connect_tincd(true)) {
1596 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1601 static void sigint_handler(int sig) {
1604 fprintf(stderr, "\n");
1605 shutdown(fd, SHUT_RDWR);
1609 static int cmd_log(int argc, char *argv[]) {
1611 fprintf(stderr, "Too many arguments!\n");
1615 if(!connect_tincd(true)) {
1620 signal(SIGINT, sigint_handler);
1623 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1626 signal(SIGINT, SIG_DFL);
1634 static int cmd_pid(int argc, char *argv[]) {
1638 fprintf(stderr, "Too many arguments!\n");
1642 if(!connect_tincd(true) || !pid) {
1646 printf("%d\n", pid);
1650 int rstrip(char *value) {
1651 int len = strlen(value);
1653 while(len && strchr("\t\r\n ", value[len - 1])) {
1660 char *get_my_name(bool verbose) {
1661 FILE *f = fopen(tinc_conf, "r");
1665 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1674 while(fgets(buf, sizeof(buf), f)) {
1675 int len = strcspn(buf, "\t =");
1677 value += strspn(value, "\t ");
1681 value += strspn(value, "\t ");
1684 if(!rstrip(value)) {
1690 if(strcasecmp(buf, "Name")) {
1696 return replace_name(value);
1703 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1709 ecdsa_t *get_pubkey(FILE *f) {
1713 while(fgets(buf, sizeof(buf), f)) {
1714 int len = strcspn(buf, "\t =");
1716 value += strspn(value, "\t ");
1720 value += strspn(value, "\t ");
1723 if(!rstrip(value)) {
1729 if(strcasecmp(buf, "Ed25519PublicKey")) {
1734 return ecdsa_set_base64_public_key(value);
1741 const var_t variables[] = {
1742 /* Server configuration */
1743 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1744 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1745 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1746 {"BindToInterface", VAR_SERVER},
1747 {"Broadcast", VAR_SERVER | VAR_SAFE},
1748 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1749 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1750 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1751 {"Device", VAR_SERVER},
1752 {"DeviceStandby", VAR_SERVER},
1753 {"DeviceType", VAR_SERVER},
1754 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1755 {"Ed25519PrivateKeyFile", VAR_SERVER},
1756 {"ExperimentalProtocol", VAR_SERVER},
1757 {"Forwarding", VAR_SERVER},
1758 {"FWMark", VAR_SERVER},
1759 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1760 {"Hostnames", VAR_SERVER},
1761 {"IffOneQueue", VAR_SERVER},
1762 {"Interface", VAR_SERVER},
1763 {"InvitationExpire", VAR_SERVER},
1764 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1765 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1766 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1767 {"LogLevel", VAR_SERVER},
1768 {"MACExpire", VAR_SERVER | VAR_SAFE},
1769 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1770 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1771 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1772 {"Mode", VAR_SERVER | VAR_SAFE},
1773 {"Name", VAR_SERVER},
1774 {"PingInterval", VAR_SERVER | VAR_SAFE},
1775 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1776 {"PriorityInheritance", VAR_SERVER},
1777 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1778 {"PrivateKeyFile", VAR_SERVER},
1779 {"ProcessPriority", VAR_SERVER},
1780 {"Proxy", VAR_SERVER},
1781 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1782 {"ScriptsExtension", VAR_SERVER},
1783 {"ScriptsInterpreter", VAR_SERVER},
1784 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1785 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1786 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1787 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1788 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1789 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1790 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1791 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1792 {"UDPRcvBuf", VAR_SERVER},
1793 {"UDPSndBuf", VAR_SERVER},
1794 {"UPnP", VAR_SERVER},
1795 {"UPnPDiscoverWait", VAR_SERVER},
1796 {"UPnPRefreshPeriod", VAR_SERVER},
1797 {"VDEGroup", VAR_SERVER},
1798 {"VDEPort", VAR_SERVER},
1799 /* Host configuration */
1800 {"Address", VAR_HOST | VAR_MULTIPLE},
1801 {"Cipher", VAR_SERVER | VAR_HOST},
1802 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1803 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1804 {"Digest", VAR_SERVER | VAR_HOST},
1805 {"Ed25519PublicKey", VAR_HOST},
1806 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1807 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1808 {"MACLength", VAR_SERVER | VAR_HOST},
1809 {"PMTU", VAR_SERVER | VAR_HOST},
1810 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1812 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1813 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1814 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1815 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1816 {"Weight", VAR_HOST | VAR_SAFE},
1820 static int cmd_config(int argc, char *argv[]) {
1822 fprintf(stderr, "Invalid number of arguments.\n");
1826 if(strcasecmp(argv[0], "config")) {
1832 if(!strcasecmp(argv[1], "get")) {
1834 } else if(!strcasecmp(argv[1], "add")) {
1835 argv++, argc--, action = 1;
1836 } else if(!strcasecmp(argv[1], "del")) {
1837 argv++, argc--, action = -1;
1838 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1839 argv++, argc--, action = 0;
1843 fprintf(stderr, "Invalid number of arguments.\n");
1847 // Concatenate the rest of the command line
1848 strncpy(line, argv[1], sizeof(line) - 1);
1850 for(int i = 2; i < argc; i++) {
1851 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1852 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1855 // Liberal parsing into node name, variable name and value.
1861 len = strcspn(line, "\t =");
1863 value += strspn(value, "\t ");
1867 value += strspn(value, "\t ");
1871 variable = strchr(line, '.');
1881 fprintf(stderr, "No variable given.\n");
1885 if(action >= 0 && !*value) {
1886 fprintf(stderr, "No value for variable given.\n");
1890 if(action < -1 && *value) {
1894 /* Some simple checks. */
1896 bool warnonremove = false;
1898 for(int i = 0; variables[i].name; i++) {
1899 if(strcasecmp(variables[i].name, variable)) {
1904 variable = (char *)variables[i].name;
1906 if(!strcasecmp(variable, "Subnet")) {
1909 if(!str2net(&s, value)) {
1910 fprintf(stderr, "Malformed subnet definition %s\n", value);
1913 if(!subnetcheck(s)) {
1914 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1919 /* Discourage use of obsolete variables. */
1921 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1923 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1925 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1930 /* Don't put server variables in host config files */
1932 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1934 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1936 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1941 /* Should this go into our own host config file? */
1943 if(!node && !(variables[i].type & VAR_SERVER)) {
1944 node = get_my_name(true);
1951 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1952 Turn on warnings when it seems variables might be removed unintentionally. */
1954 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1955 warnonremove = true;
1957 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1958 warnonremove = true;
1964 if(node && !check_id(node)) {
1965 fprintf(stderr, "Invalid name for node.\n");
1970 if(force || action < 0) {
1971 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1973 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1978 // Open the right configuration file.
1979 char filename[PATH_MAX];
1982 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1984 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1987 FILE *f = fopen(filename, "r");
1990 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1994 char tmpfile[PATH_MAX];
1998 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1999 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
2003 tf = fopen(tmpfile, "w");
2006 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
2012 // Copy the file, making modifications on the fly, unless we are just getting a value.
2016 bool removed = false;
2019 while(fgets(buf1, sizeof(buf1), f)) {
2020 buf1[sizeof(buf1) - 1] = 0;
2021 strncpy(buf2, buf1, sizeof(buf2));
2023 // Parse line in a simple way
2027 len = strcspn(buf2, "\t =");
2028 bvalue = buf2 + len;
2029 bvalue += strspn(bvalue, "\t ");
2031 if(*bvalue == '=') {
2033 bvalue += strspn(bvalue, "\t ");
2040 if(!strcasecmp(buf2, variable)) {
2044 printf("%s\n", bvalue);
2046 } else if(action == -1) {
2047 if(!*value || !strcasecmp(bvalue, value)) {
2053 } else if(action == 0) {
2054 // Warn if "set" was used for variables that can occur multiple times
2055 if(warnonremove && strcasecmp(bvalue, value)) {
2056 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2059 // Already set? Delete the rest...
2064 // Otherwise, replace.
2065 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2066 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2073 } else if(action > 0) {
2074 // Check if we've already seen this variable with the same value
2075 if(!strcasecmp(bvalue, value)) {
2082 // Copy original line...
2083 if(fputs(buf1, tf) < 0) {
2084 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2088 // Add newline if it is missing...
2089 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2090 if(fputc('\n', tf) < 0) {
2091 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2098 // Make sure we read everything...
2099 if(ferror(f) || !feof(f)) {
2100 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2105 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2109 // Add new variable if necessary.
2110 if((action > 0 && !found) || (action == 0 && !set)) {
2111 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2112 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2121 fprintf(stderr, "No matching configuration variables found.\n");
2126 // Make sure we wrote everything...
2128 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2132 // Could we find what we had to remove?
2133 if(action < 0 && !removed) {
2135 fprintf(stderr, "No configuration variables deleted.\n");
2139 // Replace the configuration file with the new one
2142 if(remove(filename)) {
2143 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2149 if(rename(tmpfile, filename)) {
2150 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2154 // Silently try notifying a running tincd of changes.
2155 if(connect_tincd(false)) {
2156 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2162 static bool try_bind(int port) {
2163 struct addrinfo *ai = NULL, *aip;
2164 struct addrinfo hint = {
2165 .ai_flags = AI_PASSIVE,
2166 .ai_family = AF_UNSPEC,
2167 .ai_socktype = SOCK_STREAM,
2168 .ai_protocol = IPPROTO_TCP,
2171 bool success = true;
2173 snprintf(portstr, sizeof(portstr), "%d", port);
2175 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2179 for(aip = ai; aip; aip = aip->ai_next) {
2180 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2187 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2200 int check_port(const char *name) {
2205 fprintf(stderr, "Warning: could not bind to port 655. ");
2207 for(int i = 0; i < 100; i++) {
2208 int port = 0x1000 + (rand() & 0x7fff);
2210 if(try_bind(port)) {
2211 char filename[PATH_MAX];
2212 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2213 FILE *f = fopen(filename, "a");
2216 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2217 fprintf(stderr, "Please change tinc's Port manually.\n");
2221 fprintf(f, "Port = %d\n", port);
2223 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2228 fprintf(stderr, "Please change tinc's Port manually.\n");
2232 static int cmd_init(int argc, char *argv[]) {
2233 if(!access(tinc_conf, F_OK)) {
2234 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2239 fprintf(stderr, "Too many arguments!\n");
2241 } else if(argc < 2) {
2244 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2246 if(!fgets(buf, sizeof(buf), stdin)) {
2247 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2251 int len = rstrip(buf);
2254 fprintf(stderr, "No name given!\n");
2260 fprintf(stderr, "No Name given!\n");
2264 name = strdup(argv[1]);
2267 fprintf(stderr, "No Name given!\n");
2272 if(!check_id(name)) {
2273 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2277 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2278 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2282 if(mkdir(confbase, 0777) && errno != EEXIST) {
2283 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2287 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2288 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2292 FILE *f = fopen(tinc_conf, "w");
2295 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2299 fprintf(f, "Name = %s\n", name);
2302 #ifndef DISABLE_LEGACY
2304 if(!rsa_keygen(2048, false)) {
2310 if(!ed25519_keygen(false)) {
2317 char filename[PATH_MAX];
2318 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2320 if(access(filename, F_OK)) {
2321 FILE *f = fopenmask(filename, "w", 0777);
2324 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2328 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");
2338 static int cmd_generate_keys(int argc, char *argv[]) {
2339 #ifdef DISABLE_LEGACY
2347 fprintf(stderr, "Too many arguments!\n");
2352 name = get_my_name(false);
2355 #ifndef DISABLE_LEGACY
2357 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2363 if(!ed25519_keygen(true)) {
2370 #ifndef DISABLE_LEGACY
2371 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2373 fprintf(stderr, "Too many arguments!\n");
2378 name = get_my_name(false);
2381 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2385 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2389 fprintf(stderr, "Too many arguments!\n");
2394 name = get_my_name(false);
2397 return !ed25519_keygen(true);
2400 static int cmd_help(int argc, char *argv[]) {
2408 static int cmd_version(int argc, char *argv[]) {
2412 fprintf(stderr, "Too many arguments!\n");
2420 static int cmd_info(int argc, char *argv[]) {
2422 fprintf(stderr, "Invalid number of arguments.\n");
2426 if(!connect_tincd(true)) {
2430 return info(fd, argv[1]);
2433 static const char *conffiles[] = {
2444 static int cmd_edit(int argc, char *argv[]) {
2446 fprintf(stderr, "Invalid number of arguments.\n");
2450 char filename[PATH_MAX] = "";
2452 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2453 for(int i = 0; conffiles[i]; i++) {
2454 if(!strcmp(argv[1], conffiles[i])) {
2455 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2464 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2465 char *dash = strchr(argv[1], '-');
2470 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2471 fprintf(stderr, "Invalid configuration filename.\n");
2479 const char *editor = getenv("VISUAL");
2482 editor = getenv("EDITOR");
2489 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2491 xasprintf(&command, "edit \"%s\"", filename);
2493 int result = system(command);
2500 // Silently try notifying a running tincd of changes.
2501 if(connect_tincd(false)) {
2502 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2508 static int export(const char *name, FILE *out) {
2509 char filename[PATH_MAX];
2510 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2511 FILE *in = fopen(filename, "r");
2514 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2518 fprintf(out, "Name = %s\n", name);
2521 while(fgets(buf, sizeof(buf), in)) {
2522 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2528 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2537 static int cmd_export(int argc, char *argv[]) {
2541 fprintf(stderr, "Too many arguments!\n");
2545 char *name = get_my_name(true);
2551 int result = export(name, stdout);
2561 static int cmd_export_all(int argc, char *argv[]) {
2565 fprintf(stderr, "Too many arguments!\n");
2569 DIR *dir = opendir(hosts_dir);
2572 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2580 while((ent = readdir(dir))) {
2581 if(!check_id(ent->d_name)) {
2588 printf("#---------------------------------------------------------------#\n");
2591 result |= export(ent->d_name, stdout);
2603 static int cmd_import(int argc, char *argv[]) {
2607 fprintf(stderr, "Too many arguments!\n");
2616 char filename[PATH_MAX] = "";
2618 bool firstline = true;
2620 while(fgets(buf, sizeof(buf), in)) {
2621 if(sscanf(buf, "Name = %4095s", name) == 1) {
2624 if(!check_id(name)) {
2625 fprintf(stderr, "Invalid Name in input!\n");
2633 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2634 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2638 if(!force && !access(filename, F_OK)) {
2639 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2644 out = fopen(filename, "w");
2647 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2653 } else if(firstline) {
2654 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2659 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2664 if(fputs(buf, out) < 0) {
2665 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2676 fprintf(stderr, "Imported %d host configuration files.\n", count);
2679 fprintf(stderr, "No host configuration files imported.\n");
2684 static int cmd_exchange(int argc, char *argv[]) {
2685 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2688 static int cmd_exchange_all(int argc, char *argv[]) {
2689 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2692 static int switch_network(char *name) {
2693 if(strcmp(name, ".")) {
2694 if(!check_netname(name, false)) {
2695 fprintf(stderr, "Invalid character in netname!\n");
2699 if(!check_netname(name, true)) {
2700 fprintf(stderr, "Warning: unsafe character in netname!\n");
2710 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2717 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2718 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2719 xasprintf(&prompt, "%s> ", identname);
2724 static int cmd_network(int argc, char *argv[]) {
2726 fprintf(stderr, "Too many arguments!\n");
2731 return switch_network(argv[1]);
2734 DIR *dir = opendir(confdir);
2737 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2743 while((ent = readdir(dir))) {
2744 if(*ent->d_name == '.') {
2748 if(!strcmp(ent->d_name, "tinc.conf")) {
2753 char fname[PATH_MAX];
2754 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2756 if(!access(fname, R_OK)) {
2757 printf("%s\n", ent->d_name);
2766 static int cmd_fsck(int argc, char *argv[]) {
2770 fprintf(stderr, "Too many arguments!\n");
2774 return fsck(orig_argv[0]);
2777 static void *readfile(FILE *in, size_t *len) {
2779 size_t bufsize = 4096;
2780 char *buf = xmalloc(bufsize);
2783 size_t read = fread(buf + count, 1, bufsize - count, in);
2791 if(count >= bufsize) {
2793 buf = xrealloc(buf, bufsize);
2804 static int cmd_sign(int argc, char *argv[]) {
2806 fprintf(stderr, "Too many arguments!\n");
2811 name = get_my_name(true);
2818 char fname[PATH_MAX];
2819 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2820 FILE *fp = fopen(fname, "r");
2823 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2827 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2830 fprintf(stderr, "Could not read private key from %s\n", fname);
2840 in = fopen(argv[1], "rb");
2843 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2852 char *data = readfile(in, &len);
2859 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2864 // Ensure we sign our name and current time as well
2865 long t = time(NULL);
2867 xasprintf(&trailer, " %s %ld", name, t);
2868 int trailer_len = strlen(trailer);
2870 data = xrealloc(data, len + trailer_len);
2871 memcpy(data + len, trailer, trailer_len);
2876 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2877 fprintf(stderr, "Error generating signature\n");
2883 b64encode(sig, sig, 64);
2886 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2887 fwrite(data, len, 1, stdout);
2893 static int cmd_verify(int argc, char *argv[]) {
2895 fprintf(stderr, "Not enough arguments!\n");
2900 fprintf(stderr, "Too many arguments!\n");
2904 char *node = argv[1];
2906 if(!strcmp(node, ".")) {
2908 name = get_my_name(true);
2916 } else if(!strcmp(node, "*")) {
2919 if(!check_id(node)) {
2920 fprintf(stderr, "Invalid node name\n");
2928 in = fopen(argv[2], "rb");
2931 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2939 char *data = readfile(in, &len);
2946 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2950 char *newline = memchr(data, '\n', len);
2952 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2953 fprintf(stderr, "Invalid input\n");
2959 size_t skip = newline - data;
2961 char signer[MAX_STRING_SIZE] = "";
2962 char sig[MAX_STRING_SIZE] = "";
2965 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2966 fprintf(stderr, "Invalid input\n");
2971 if(node && strcmp(node, signer)) {
2972 fprintf(stderr, "Signature is not made by %s\n", node);
2982 xasprintf(&trailer, " %s %ld", signer, t);
2983 int trailer_len = strlen(trailer);
2985 data = xrealloc(data, len + trailer_len);
2986 memcpy(data + len, trailer, trailer_len);
2989 newline = data + skip;
2991 char fname[PATH_MAX];
2992 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2993 FILE *fp = fopen(fname, "r");
2996 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
3001 ecdsa_t *key = get_pubkey(fp);
3005 key = ecdsa_read_pem_public_key(fp);
3009 fprintf(stderr, "Could not read public key from %s\n", fname);
3017 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
3018 fprintf(stderr, "Invalid signature\n");
3026 fwrite(newline, len - (newline - data), 1, stdout);
3032 static const struct {
3033 const char *command;
3034 int (*function)(int argc, char *argv[]);
3037 {"start", cmd_start, false},
3038 {"stop", cmd_stop, false},
3039 {"restart", cmd_restart, false},
3040 {"reload", cmd_reload, false},
3041 {"dump", cmd_dump, false},
3042 {"list", cmd_dump, false},
3043 {"purge", cmd_purge, false},
3044 {"debug", cmd_debug, false},
3045 {"retry", cmd_retry, false},
3046 {"connect", cmd_connect, false},
3047 {"disconnect", cmd_disconnect, false},
3048 {"top", cmd_top, false},
3049 {"pcap", cmd_pcap, false},
3050 {"log", cmd_log, false},
3051 {"pid", cmd_pid, false},
3052 {"config", cmd_config, true},
3053 {"add", cmd_config, false},
3054 {"del", cmd_config, false},
3055 {"get", cmd_config, false},
3056 {"set", cmd_config, false},
3057 {"init", cmd_init, false},
3058 {"generate-keys", cmd_generate_keys, false},
3059 #ifndef DISABLE_LEGACY
3060 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3062 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3063 {"help", cmd_help, false},
3064 {"version", cmd_version, false},
3065 {"info", cmd_info, false},
3066 {"edit", cmd_edit, false},
3067 {"export", cmd_export, false},
3068 {"export-all", cmd_export_all, false},
3069 {"import", cmd_import, false},
3070 {"exchange", cmd_exchange, false},
3071 {"exchange-all", cmd_exchange_all, false},
3072 {"invite", cmd_invite, false},
3073 {"join", cmd_join, false},
3074 {"network", cmd_network, false},
3075 {"fsck", cmd_fsck, false},
3076 {"sign", cmd_sign, false},
3077 {"verify", cmd_verify, false},
3078 {NULL, NULL, false},
3081 #ifdef HAVE_READLINE
3082 static char *complete_command(const char *text, int state) {
3091 while(commands[i].command) {
3092 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3093 return xstrdup(commands[i].command);
3102 static char *complete_dump(const char *text, int state) {
3103 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3113 if(!strncasecmp(matches[i], text, strlen(text))) {
3114 return xstrdup(matches[i]);
3123 static char *complete_config(const char *text, int state) {
3132 while(variables[i].name) {
3133 char *dot = strchr(text, '.');
3136 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3138 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3142 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3143 return xstrdup(variables[i].name);
3153 static char *complete_info(const char *text, int state) {
3159 if(!connect_tincd(false)) {
3163 // Check the list of nodes
3164 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3165 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3168 while(recvline(fd, line, sizeof(line))) {
3170 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3183 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3187 if(!strncmp(item, text, strlen(text))) {
3188 return xstrdup(strip_weight(item));
3195 static char *complete_nothing(const char *text, int state) {
3201 static char **completion(const char *text, int start, int end) {
3203 char **matches = NULL;
3206 matches = rl_completion_matches(text, complete_command);
3207 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3208 matches = rl_completion_matches(text, complete_dump);
3209 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3210 matches = rl_completion_matches(text, complete_config);
3211 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3212 matches = rl_completion_matches(text, complete_config);
3213 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3214 matches = rl_completion_matches(text, complete_config);
3215 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3216 matches = rl_completion_matches(text, complete_config);
3217 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3218 matches = rl_completion_matches(text, complete_info);
3225 static int cmd_shell(int argc, char *argv[]) {
3226 xasprintf(&prompt, "%s> ", identname);
3230 int maxargs = argc + 16;
3231 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3233 for(int i = 0; i < argc; i++) {
3237 #ifdef HAVE_READLINE
3238 rl_readline_name = "tinc";
3239 rl_completion_entry_function = complete_nothing;
3240 rl_attempted_completion_function = completion;
3241 rl_filename_completion_desired = 0;
3246 #ifdef HAVE_READLINE
3251 rl_basic_word_break_characters = "\t\n ";
3252 line = readline(prompt);
3253 copy = line ? xstrdup(line) : NULL;
3255 line = fgets(buf, sizeof(buf), stdin);
3261 fputs(prompt, stdout);
3264 line = fgets(buf, sizeof(buf), stdin);
3271 /* Ignore comments */
3280 char *p = line + strspn(line, " \t\n");
3281 char *next = strtok(p, " \t\n");
3284 if(nargc >= maxargs) {
3286 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3291 next = strtok(NULL, " \t\n");
3298 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3299 #ifdef HAVE_READLINE
3308 for(int i = 0; commands[i].command; i++) {
3309 if(!strcasecmp(nargv[argc], commands[i].command)) {
3310 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3316 #ifdef HAVE_READLINE
3325 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3330 #ifdef HAVE_READLINE
3343 int main(int argc, char *argv[]) {
3344 program_name = argv[0];
3347 tty = isatty(0) && isatty(1);
3349 if(!parse_options(argc, argv)) {
3354 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3355 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3368 static struct WSAData wsa_state;
3370 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3371 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3377 gettimeofday(&now, NULL);
3378 srand(now.tv_sec + now.tv_usec);
3381 if(optind >= argc) {
3382 return cmd_shell(argc, argv);
3385 for(int i = 0; commands[i].command; i++) {
3386 if(!strcasecmp(argv[optind], commands[i].command)) {
3387 return commands[i].function(argc - optind, argv + optind);
3391 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);