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 */
194 confbase = xstrdup(optarg);
195 confbasegiven = true;
198 case 'n': /* net name given */
200 netname = xstrdup(optarg);
203 case 1: /* show help */
207 case 2: /* show version */
211 case 3: /* open control socket here */
213 pidfilename = xstrdup(optarg);
220 case '?': /* wrong options */
230 if(!netname && (netname = getenv("NETNAME"))) {
231 netname = xstrdup(netname);
234 /* netname "." is special: a "top-level name" */
236 if(netname && (!*netname || !strcmp(netname, "."))) {
241 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
242 fprintf(stderr, "Invalid character in netname!\n");
250 /* Open a file with the desired permissions, minus the umask.
251 Also, if we want to create an executable file, we call fchmod()
252 to set the executable bits. */
254 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
255 mode_t mask = umask(0);
257 umask(~perms & 0777);
258 FILE *f = fopen(filename, mode);
261 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
267 if((perms & 0444) && f) {
268 fchmod(fileno(f), perms);
276 static void disable_old_keys(const char *filename, const char *what) {
277 char tmpfile[PATH_MAX] = "";
279 bool disabled = false;
283 FILE *r = fopen(filename, "r");
290 int result = snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
292 if(result < sizeof(tmpfile)) {
293 struct stat st = {.st_mode = 0600};
294 fstat(fileno(r), &st);
295 w = fopenmask(tmpfile, "w", st.st_mode);
298 while(fgets(buf, sizeof(buf), r)) {
299 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
300 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
306 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
313 if(block || ed25519pubkey) {
317 if(fputs(buf, w) < 0) {
323 if(block && !strncmp(buf, "-----END ", 9)) {
333 if(ferror(r) || fclose(r) < 0) {
339 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
349 // We cannot atomically replace files on Windows.
350 char bakfile[PATH_MAX] = "";
351 snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
353 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
354 rename(bakfile, filename);
357 if(rename(tmpfile, filename)) {
359 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
364 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
371 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
373 char directory[PATH_MAX] = ".";
379 /* Check stdin and stdout */
381 /* Ask for a file and/or directory name. */
382 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
384 if(fgets(buf, sizeof(buf), stdin) == NULL) {
385 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
389 size_t len = strlen(buf);
402 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
405 if(filename[0] != '/') {
407 /* The directory is a relative path or a filename. */
408 getcwd(directory, sizeof(directory));
410 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
411 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
423 disable_old_keys(filename, what);
425 /* Open it first to keep the inode busy */
427 r = fopenmask(filename, mode, perms);
430 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
438 Generate a public/private Ed25519 key pair, and ask for a file to store
441 static bool ed25519_keygen(bool ask) {
444 char fname[PATH_MAX];
446 fprintf(stderr, "Generating Ed25519 key pair:\n");
448 if(!(key = ecdsa_generate())) {
449 fprintf(stderr, "Error during key generation!\n");
452 fprintf(stderr, "Done.\n");
455 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
456 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
462 if(!ecdsa_write_pem_private_key(key, f)) {
463 fprintf(stderr, "Error writing private key!\n");
470 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
472 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
475 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
481 char *pubkey = ecdsa_get_base64_public_key(key);
482 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
500 #ifndef DISABLE_LEGACY
502 Generate a public/private RSA key pair, and ask for a file to store
505 static bool rsa_keygen(int bits, bool ask) {
508 char fname[PATH_MAX];
510 // Make sure the key size is a multiple of 8 bits.
513 // Make sure that a valid key size is used.
514 if(bits < 1024 || bits > 8192) {
515 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
517 } else if(bits < 2048) {
518 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
521 fprintf(stderr, "Generating %d bits keys:\n", bits);
523 if(!(key = rsa_generate(bits, 0x10001))) {
524 fprintf(stderr, "Error during key generation!\n");
527 fprintf(stderr, "Done.\n");
530 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
531 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
537 if(!rsa_write_pem_private_key(key, f)) {
538 fprintf(stderr, "Error writing private key!\n");
545 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
547 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
550 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
556 if(!rsa_write_pem_public_key(key, f)) {
557 fprintf(stderr, "Error writing public key!\n");
580 bool recvline(int fd, char *line, size_t len) {
581 char *newline = NULL;
587 while(!(newline = memchr(buffer, '\n', blen))) {
588 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
590 if(result == -1 && sockerrno == EINTR) {
592 } else if(result <= 0) {
599 if((size_t)(newline - buffer) >= len) {
603 len = newline - buffer;
605 memcpy(line, buffer, len);
607 memmove(buffer, newline + 1, blen - len - 1);
613 static bool recvdata(int fd, char *data, size_t len) {
615 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
617 if(result == -1 && sockerrno == EINTR) {
619 } else if(result <= 0) {
626 memcpy(data, buffer, len);
627 memmove(buffer, buffer + len, blen - len);
633 bool sendline(int fd, char *format, ...) {
634 static char buffer[4096];
639 va_start(ap, format);
640 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
641 buffer[sizeof(buffer) - 1] = 0;
644 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
652 int result = send(fd, p, blen, MSG_NOSIGNAL);
654 if(result == -1 && sockerrno == EINTR) {
656 } else if(result <= 0) {
667 static void pcap(int fd, FILE *out, uint32_t snaplen) {
668 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
676 uint32_t tz_accuracy;
683 snaplen ? snaplen : sizeof(data),
696 fwrite(&header, sizeof(header), 1, out);
701 while(recvline(fd, line, sizeof(line))) {
703 int n = sscanf(line, "%d %d %d", &code, &req, &len);
704 gettimeofday(&tv, NULL);
706 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || (size_t)len > sizeof(data)) {
710 if(!recvdata(fd, data, len)) {
714 packet.tv_sec = tv.tv_sec;
715 packet.tv_usec = tv.tv_usec;
717 packet.origlen = len;
718 fwrite(&packet, sizeof(packet), 1, out);
719 fwrite(data, len, 1, out);
724 static void logcontrol(int fd, FILE *out, int level) {
725 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
729 while(recvline(fd, line, sizeof(line))) {
731 int n = sscanf(line, "%d %d %d", &code, &req, &len);
733 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
737 if(!recvdata(fd, data, len)) {
741 fwrite(data, len, 1, out);
747 static bool stop_tincd(void) {
748 if(!connect_tincd(true)) {
752 sendline(fd, "%d %d", CONTROL, REQ_STOP);
754 while(recvline(fd, line, sizeof(line))) {
755 // wait for tincd to close the connection...
766 static bool remove_service(void) {
767 SC_HANDLE manager = NULL;
768 SC_HANDLE service = NULL;
769 SERVICE_STATUS status = {0};
770 bool success = false;
772 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
775 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
779 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
782 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
783 success = stop_tincd();
785 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
791 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
792 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
794 fprintf(stderr, "%s service stopped\n", identname);
797 if(!DeleteService(service)) {
798 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
807 CloseServiceHandle(service);
811 CloseServiceHandle(manager);
815 fprintf(stderr, "%s service removed\n", identname);
822 bool connect_tincd(bool verbose) {
827 struct timeval tv = {0, 0};
829 if(select(fd + 1, &r, NULL, NULL, &tv)) {
830 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
838 FILE *f = fopen(pidfilename, "r");
842 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
851 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
853 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
864 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
865 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
866 /* clean up the stale socket and pid file */
868 unlink(unixsocketname);
872 struct sockaddr_un sa = {
873 .sun_family = AF_UNIX,
876 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
877 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
881 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
883 fd = socket(AF_UNIX, SOCK_STREAM, 0);
887 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
893 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
895 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
904 struct addrinfo hints = {
905 .ai_family = AF_UNSPEC,
906 .ai_socktype = SOCK_STREAM,
907 .ai_protocol = IPPROTO_TCP,
911 struct addrinfo *res = NULL;
913 if(getaddrinfo(host, port, &hints, &res) || !res) {
915 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
921 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
925 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
931 unsigned long arg = 0;
933 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
935 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
939 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
941 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
953 static const int one = 1;
954 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
957 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
962 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
964 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
972 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
974 fprintf(stderr, "Could not fully establish control socket connection\n");
986 static int cmd_start(int argc, char *argv[]) {
987 if(connect_tincd(false)) {
989 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
991 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
998 char *slash = strrchr(program_name, '/');
1002 if((c = strrchr(program_name, '\\')) > slash) {
1008 char *default_c = "tincd";
1011 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
1017 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
1022 Windows has no real concept of an "argv array". A command line is just one string.
1023 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
1024 it uses quotes to handle spaces in arguments.
1025 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
1026 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
1027 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
1029 xasprintf(&arg0, "\"%s\"", arg0);
1031 nargv[nargc++] = arg0;
1033 for(int i = 1; i < optind; i++) {
1034 nargv[nargc++] = orig_argv[i];
1037 for(int i = 1; i < argc; i++) {
1038 nargv[nargc++] = argv[i];
1042 int status = spawnvp(_P_WAIT, c, nargv);
1046 if(c != default_c) {
1051 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
1057 int pfd[2] = {-1, -1};
1059 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
1060 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
1063 if(c != default_c) {
1073 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1076 if(c != default_c) {
1086 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1087 setenv("TINC_UMBILICAL", buf, true);
1088 exit(execvp(c, nargv));
1095 int status = -1, result;
1097 signal(SIGINT, SIG_IGN);
1100 // Pass all log messages from the umbilical to stderr.
1101 // A nul-byte right before closure means tincd started successfully.
1102 bool failure = true;
1106 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1107 failure = buf[len - 1];
1122 // Make sure the child process is really gone.
1123 result = waitpid(pid, &status, 0);
1126 signal(SIGINT, SIG_DFL);
1129 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1132 fprintf(stderr, "Error starting %s\n", c);
1135 if(c != default_c) {
1139 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1143 static int cmd_stop(int argc, char *argv[]) {
1147 fprintf(stderr, "Too many arguments!\n");
1152 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1157 if(kill(pid, SIGTERM)) {
1158 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1162 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1163 waitpid(pid, NULL, 0);
1174 static int cmd_restart(int argc, char *argv[]) {
1176 return cmd_start(argc, argv);
1179 static int cmd_reload(int argc, char *argv[]) {
1183 fprintf(stderr, "Too many arguments!\n");
1187 if(!connect_tincd(true)) {
1191 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1193 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1194 fprintf(stderr, "Could not reload configuration.\n");
1202 static int dump_invitations(void) {
1203 char dname[PATH_MAX];
1204 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1205 DIR *dir = opendir(dname);
1208 if(errno == ENOENT) {
1209 fprintf(stderr, "No outstanding invitations.\n");
1213 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1221 while((ent = readdir(dir))) {
1222 char buf[MAX_STRING_SIZE];
1224 if(b64decode(ent->d_name, buf, 24) != 18) {
1228 char fname[PATH_MAX];
1230 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1231 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1235 FILE *f = fopen(fname, "r");
1238 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1244 if(!fgets(buf, sizeof(buf), f)) {
1245 fprintf(stderr, "Invalid invitation file %s\n", fname);
1252 char *eol = buf + strlen(buf);
1254 while(strchr("\t \r\n", *--eol)) {
1258 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1259 fprintf(stderr, "Invalid invitation file %s\n", fname);
1264 printf("%s %s\n", ent->d_name, buf + 7);
1270 fprintf(stderr, "No outstanding invitations.\n");
1276 static int cmd_dump(int argc, char *argv[]) {
1277 bool only_reachable = false;
1279 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1280 if(strcasecmp(argv[2], "nodes")) {
1281 fprintf(stderr, "`reachable' only supported for nodes.\n");
1286 only_reachable = true;
1292 fprintf(stderr, "Invalid number of arguments.\n");
1297 if(!strcasecmp(argv[1], "invitations")) {
1298 return dump_invitations();
1301 if(!connect_tincd(true)) {
1307 if(!strcasecmp(argv[1], "nodes")) {
1308 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1309 } else if(!strcasecmp(argv[1], "edges")) {
1310 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1311 } else if(!strcasecmp(argv[1], "subnets")) {
1312 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1313 } else if(!strcasecmp(argv[1], "connections")) {
1314 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1315 } else if(!strcasecmp(argv[1], "graph")) {
1316 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1317 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1319 } else if(!strcasecmp(argv[1], "digraph")) {
1320 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1321 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1324 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1330 printf("graph {\n");
1331 } else if(do_graph == 2) {
1332 printf("digraph {\n");
1335 while(recvline(fd, line, sizeof(line))) {
1336 char node1[4096], node2[4096];
1337 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1340 if(do_graph && req == REQ_DUMP_NODES) {
1362 char local_host[4096];
1363 char local_port[4096];
1366 int cipher, digest, maclength, compression, distance, socket, weight;
1367 short int pmtu, minmtu, maxmtu;
1368 unsigned int options, status_int;
1369 node_status_t status;
1370 long int last_state_change;
1372 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1375 case REQ_DUMP_NODES: {
1376 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);
1379 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1383 memcpy(&status, &status_int, sizeof(status));
1386 const char *color = "black";
1388 if(!strcmp(host, "MYSELF")) {
1390 } else if(!status.reachable) {
1392 } else if(strcmp(via, node)) {
1394 } else if(!status.validkey) {
1396 } else if(minmtu > 0) {
1400 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1402 if(only_reachable && !status.reachable) {
1406 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,
1407 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);
1409 if(udp_ping_rtt != -1) {
1410 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1418 case REQ_DUMP_EDGES: {
1419 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);
1422 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1427 float w = 1 + 65536.0 / weight;
1429 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1430 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1431 } else if(do_graph == 2) {
1432 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1435 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);
1440 case REQ_DUMP_SUBNETS: {
1441 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1444 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1448 printf("%s owner %s\n", strip_weight(subnet), node);
1452 case REQ_DUMP_CONNECTIONS: {
1453 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1456 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1460 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1465 fprintf(stderr, "Unable to parse dump from tincd.\n");
1470 fprintf(stderr, "Error receiving dump.\n");
1474 static int cmd_purge(int argc, char *argv[]) {
1478 fprintf(stderr, "Too many arguments!\n");
1482 if(!connect_tincd(true)) {
1486 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1488 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1489 fprintf(stderr, "Could not purge old information.\n");
1496 static int cmd_debug(int argc, char *argv[]) {
1498 fprintf(stderr, "Invalid number of arguments.\n");
1502 if(!connect_tincd(true)) {
1506 int debuglevel = atoi(argv[1]);
1509 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1511 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1512 fprintf(stderr, "Could not set debug level.\n");
1516 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1520 static int cmd_retry(int argc, char *argv[]) {
1524 fprintf(stderr, "Too many arguments!\n");
1528 if(!connect_tincd(true)) {
1532 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1534 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1535 fprintf(stderr, "Could not retry outgoing connections.\n");
1542 static int cmd_connect(int argc, char *argv[]) {
1544 fprintf(stderr, "Invalid number of arguments.\n");
1548 if(!check_id(argv[1])) {
1549 fprintf(stderr, "Invalid name for node.\n");
1553 if(!connect_tincd(true)) {
1557 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1559 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1560 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1567 static int cmd_disconnect(int argc, char *argv[]) {
1569 fprintf(stderr, "Invalid number of arguments.\n");
1573 if(!check_id(argv[1])) {
1574 fprintf(stderr, "Invalid name for node.\n");
1578 if(!connect_tincd(true)) {
1582 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1584 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1585 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1592 static int cmd_top(int argc, char *argv[]) {
1596 fprintf(stderr, "Too many arguments!\n");
1602 if(!connect_tincd(true)) {
1609 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1614 static int cmd_pcap(int argc, char *argv[]) {
1616 fprintf(stderr, "Too many arguments!\n");
1620 if(!connect_tincd(true)) {
1624 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1629 static void sigint_handler(int sig) {
1632 fprintf(stderr, "\n");
1633 shutdown(fd, SHUT_RDWR);
1637 static int cmd_log(int argc, char *argv[]) {
1639 fprintf(stderr, "Too many arguments!\n");
1643 if(!connect_tincd(true)) {
1648 signal(SIGINT, sigint_handler);
1651 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1654 signal(SIGINT, SIG_DFL);
1662 static int cmd_pid(int argc, char *argv[]) {
1666 fprintf(stderr, "Too many arguments!\n");
1670 if(!connect_tincd(true) || !pid) {
1674 printf("%d\n", pid);
1678 int rstrip(char *value) {
1679 int len = strlen(value);
1681 while(len && strchr("\t\r\n ", value[len - 1])) {
1688 char *get_my_name(bool verbose) {
1689 FILE *f = fopen(tinc_conf, "r");
1693 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1702 while(fgets(buf, sizeof(buf), f)) {
1703 int len = strcspn(buf, "\t =");
1705 value += strspn(value, "\t ");
1709 value += strspn(value, "\t ");
1712 if(!rstrip(value)) {
1718 if(strcasecmp(buf, "Name")) {
1724 return replace_name(value);
1731 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1737 ecdsa_t *get_pubkey(FILE *f) {
1741 while(fgets(buf, sizeof(buf), f)) {
1742 int len = strcspn(buf, "\t =");
1744 value += strspn(value, "\t ");
1748 value += strspn(value, "\t ");
1751 if(!rstrip(value)) {
1757 if(strcasecmp(buf, "Ed25519PublicKey")) {
1762 return ecdsa_set_base64_public_key(value);
1769 const var_t variables[] = {
1770 /* Server configuration */
1771 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1772 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1773 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1774 {"BindToInterface", VAR_SERVER},
1775 {"Broadcast", VAR_SERVER | VAR_SAFE},
1776 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1777 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1778 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1779 {"Device", VAR_SERVER},
1780 {"DeviceStandby", VAR_SERVER},
1781 {"DeviceType", VAR_SERVER},
1782 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1783 {"Ed25519PrivateKeyFile", VAR_SERVER},
1784 {"ExperimentalProtocol", VAR_SERVER},
1785 {"Forwarding", VAR_SERVER},
1786 {"FWMark", VAR_SERVER},
1787 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1788 {"Hostnames", VAR_SERVER},
1789 {"IffOneQueue", VAR_SERVER},
1790 {"Interface", VAR_SERVER},
1791 {"InvitationExpire", VAR_SERVER},
1792 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1793 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1794 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1795 {"LogLevel", VAR_SERVER},
1796 {"MACExpire", VAR_SERVER | VAR_SAFE},
1797 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1798 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1799 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1800 {"Mode", VAR_SERVER | VAR_SAFE},
1801 {"Name", VAR_SERVER},
1802 {"PingInterval", VAR_SERVER | VAR_SAFE},
1803 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1804 {"PriorityInheritance", VAR_SERVER},
1805 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1806 {"PrivateKeyFile", VAR_SERVER},
1807 {"ProcessPriority", VAR_SERVER},
1808 {"Proxy", VAR_SERVER},
1809 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1810 {"ScriptsExtension", VAR_SERVER},
1811 {"ScriptsInterpreter", VAR_SERVER},
1812 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1813 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1814 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1815 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1816 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1817 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1818 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1819 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1820 {"UDPRcvBuf", VAR_SERVER},
1821 {"UDPSndBuf", VAR_SERVER},
1822 {"UPnP", VAR_SERVER},
1823 {"UPnPDiscoverWait", VAR_SERVER},
1824 {"UPnPRefreshPeriod", VAR_SERVER},
1825 {"VDEGroup", VAR_SERVER},
1826 {"VDEPort", VAR_SERVER},
1827 /* Host configuration */
1828 {"Address", VAR_HOST | VAR_MULTIPLE},
1829 {"Cipher", VAR_SERVER | VAR_HOST},
1830 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1831 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1832 {"Digest", VAR_SERVER | VAR_HOST},
1833 {"Ed25519PublicKey", VAR_HOST},
1834 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1835 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1836 {"MACLength", VAR_SERVER | VAR_HOST},
1837 {"PMTU", VAR_SERVER | VAR_HOST},
1838 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1840 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1841 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1842 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1843 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1844 {"Weight", VAR_HOST | VAR_SAFE},
1848 static int cmd_config(int argc, char *argv[]) {
1850 fprintf(stderr, "Invalid number of arguments.\n");
1854 if(strcasecmp(argv[0], "config")) {
1860 if(!strcasecmp(argv[1], "get")) {
1862 } else if(!strcasecmp(argv[1], "add")) {
1863 argv++, argc--, action = 1;
1864 } else if(!strcasecmp(argv[1], "del")) {
1865 argv++, argc--, action = -1;
1866 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1867 argv++, argc--, action = 0;
1871 fprintf(stderr, "Invalid number of arguments.\n");
1875 // Concatenate the rest of the command line
1876 strncpy(line, argv[1], sizeof(line) - 1);
1878 for(int i = 2; i < argc; i++) {
1879 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1880 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1883 // Liberal parsing into node name, variable name and value.
1889 len = strcspn(line, "\t =");
1891 value += strspn(value, "\t ");
1895 value += strspn(value, "\t ");
1899 variable = strchr(line, '.');
1909 fprintf(stderr, "No variable given.\n");
1913 if(action >= 0 && !*value) {
1914 fprintf(stderr, "No value for variable given.\n");
1918 if(action < -1 && *value) {
1922 /* Some simple checks. */
1924 bool warnonremove = false;
1926 for(int i = 0; variables[i].name; i++) {
1927 if(strcasecmp(variables[i].name, variable)) {
1932 variable = (char *)variables[i].name;
1934 if(!strcasecmp(variable, "Subnet") && *value) {
1937 if(!str2net(&s, value)) {
1938 fprintf(stderr, "Malformed subnet definition %s\n", value);
1942 if(!subnetcheck(s)) {
1943 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1948 /* Discourage use of obsolete variables. */
1950 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1952 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1954 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1959 /* Don't put server variables in host config files */
1961 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1963 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1965 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1970 /* Should this go into our own host config file? */
1972 if(!node && !(variables[i].type & VAR_SERVER)) {
1973 node = get_my_name(true);
1980 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1981 Turn on warnings when it seems variables might be removed unintentionally. */
1983 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1984 warnonremove = true;
1986 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1987 warnonremove = true;
1993 if(node && !check_id(node)) {
1994 fprintf(stderr, "Invalid name for node.\n");
2004 if(force || action < 0) {
2005 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
2007 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
2009 if(node && node != line) {
2017 // Open the right configuration file.
2018 char filename[PATH_MAX];
2021 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
2028 snprintf(filename, sizeof(filename), "%s", tinc_conf);
2031 FILE *f = fopen(filename, "r");
2034 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2038 char tmpfile[PATH_MAX];
2042 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
2043 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
2047 tf = fopen(tmpfile, "w");
2050 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
2056 // Copy the file, making modifications on the fly, unless we are just getting a value.
2060 bool removed = false;
2063 while(fgets(buf1, sizeof(buf1), f)) {
2064 buf1[sizeof(buf1) - 1] = 0;
2065 strncpy(buf2, buf1, sizeof(buf2));
2067 // Parse line in a simple way
2071 len = strcspn(buf2, "\t =");
2072 bvalue = buf2 + len;
2073 bvalue += strspn(bvalue, "\t ");
2075 if(*bvalue == '=') {
2077 bvalue += strspn(bvalue, "\t ");
2084 if(!strcasecmp(buf2, variable)) {
2088 printf("%s\n", bvalue);
2090 } else if(action == -1) {
2091 if(!*value || !strcasecmp(bvalue, value)) {
2097 } else if(action == 0) {
2098 // Warn if "set" was used for variables that can occur multiple times
2099 if(warnonremove && strcasecmp(bvalue, value)) {
2100 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2103 // Already set? Delete the rest...
2108 // Otherwise, replace.
2109 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2110 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2117 } else if(action > 0) {
2118 // Check if we've already seen this variable with the same value
2119 if(!strcasecmp(bvalue, value)) {
2126 // Copy original line...
2127 if(fputs(buf1, tf) < 0) {
2128 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2132 // Add newline if it is missing...
2133 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2134 if(fputc('\n', tf) < 0) {
2135 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2142 // Make sure we read everything...
2143 if(ferror(f) || !feof(f)) {
2144 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2149 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2153 // Add new variable if necessary.
2154 if((action > 0 && !found) || (action == 0 && !set)) {
2155 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2156 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2165 fprintf(stderr, "No matching configuration variables found.\n");
2170 // Make sure we wrote everything...
2172 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2176 // Could we find what we had to remove?
2177 if(action < 0 && !removed) {
2179 fprintf(stderr, "No configuration variables deleted.\n");
2183 // Replace the configuration file with the new one
2186 if(remove(filename)) {
2187 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2193 if(rename(tmpfile, filename)) {
2194 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2198 // Silently try notifying a running tincd of changes.
2199 if(connect_tincd(false)) {
2200 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2206 static bool try_bind(int port) {
2207 struct addrinfo *ai = NULL, *aip;
2208 struct addrinfo hint = {
2209 .ai_flags = AI_PASSIVE,
2210 .ai_family = AF_UNSPEC,
2211 .ai_socktype = SOCK_STREAM,
2212 .ai_protocol = IPPROTO_TCP,
2215 bool success = true;
2217 snprintf(portstr, sizeof(portstr), "%d", port);
2219 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2223 for(aip = ai; aip; aip = aip->ai_next) {
2224 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2231 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2244 int check_port(const char *name) {
2249 fprintf(stderr, "Warning: could not bind to port 655. ");
2251 for(int i = 0; i < 100; i++) {
2252 int port = 0x1000 + (rand() & 0x7fff);
2254 if(try_bind(port)) {
2255 char filename[PATH_MAX];
2256 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2257 FILE *f = fopen(filename, "a");
2260 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2261 fprintf(stderr, "Please change tinc's Port manually.\n");
2265 fprintf(f, "Port = %d\n", port);
2267 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2272 fprintf(stderr, "Please change tinc's Port manually.\n");
2276 static int cmd_init(int argc, char *argv[]) {
2277 if(!access(tinc_conf, F_OK)) {
2278 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2283 fprintf(stderr, "Too many arguments!\n");
2285 } else if(argc < 2) {
2288 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2290 if(!fgets(buf, sizeof(buf), stdin)) {
2291 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2295 int len = rstrip(buf);
2298 fprintf(stderr, "No name given!\n");
2304 fprintf(stderr, "No Name given!\n");
2308 name = strdup(argv[1]);
2311 fprintf(stderr, "No Name given!\n");
2316 if(!check_id(name)) {
2317 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2321 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2322 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2326 if(mkdir(confbase, 0777) && errno != EEXIST) {
2327 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2331 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2332 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2336 FILE *f = fopen(tinc_conf, "w");
2339 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2343 fprintf(f, "Name = %s\n", name);
2346 #ifndef DISABLE_LEGACY
2348 if(!rsa_keygen(2048, false)) {
2354 if(!ed25519_keygen(false)) {
2361 char filename[PATH_MAX];
2362 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2364 if(access(filename, F_OK)) {
2365 FILE *f = fopenmask(filename, "w", 0777);
2368 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2372 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");
2382 static int cmd_generate_keys(int argc, char *argv[]) {
2383 #ifdef DISABLE_LEGACY
2391 fprintf(stderr, "Too many arguments!\n");
2396 name = get_my_name(false);
2399 #ifndef DISABLE_LEGACY
2401 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2407 if(!ed25519_keygen(true)) {
2414 #ifndef DISABLE_LEGACY
2415 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2417 fprintf(stderr, "Too many arguments!\n");
2422 name = get_my_name(false);
2425 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2429 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2433 fprintf(stderr, "Too many arguments!\n");
2438 name = get_my_name(false);
2441 return !ed25519_keygen(true);
2444 static int cmd_help(int argc, char *argv[]) {
2452 static int cmd_version(int argc, char *argv[]) {
2456 fprintf(stderr, "Too many arguments!\n");
2464 static int cmd_info(int argc, char *argv[]) {
2466 fprintf(stderr, "Invalid number of arguments.\n");
2470 if(!connect_tincd(true)) {
2474 return info(fd, argv[1]);
2477 static const char *conffiles[] = {
2488 static int cmd_edit(int argc, char *argv[]) {
2490 fprintf(stderr, "Invalid number of arguments.\n");
2494 char filename[PATH_MAX] = "";
2496 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2497 for(int i = 0; conffiles[i]; i++) {
2498 if(!strcmp(argv[1], conffiles[i])) {
2499 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2508 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2509 char *dash = strchr(argv[1], '-');
2514 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2515 fprintf(stderr, "Invalid configuration filename.\n");
2523 const char *editor = getenv("VISUAL");
2526 editor = getenv("EDITOR");
2533 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2535 xasprintf(&command, "edit \"%s\"", filename);
2537 int result = system(command);
2544 // Silently try notifying a running tincd of changes.
2545 if(connect_tincd(false)) {
2546 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2552 static int export(const char *name, FILE *out) {
2553 char filename[PATH_MAX];
2554 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2555 FILE *in = fopen(filename, "r");
2558 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2562 fprintf(out, "Name = %s\n", name);
2565 while(fgets(buf, sizeof(buf), in)) {
2566 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2572 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2581 static int cmd_export(int argc, char *argv[]) {
2585 fprintf(stderr, "Too many arguments!\n");
2589 char *name = get_my_name(true);
2595 int result = export(name, stdout);
2605 static int cmd_export_all(int argc, char *argv[]) {
2609 fprintf(stderr, "Too many arguments!\n");
2613 DIR *dir = opendir(hosts_dir);
2616 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2624 while((ent = readdir(dir))) {
2625 if(!check_id(ent->d_name)) {
2632 printf("#---------------------------------------------------------------#\n");
2635 result |= export(ent->d_name, stdout);
2647 static int cmd_import(int argc, char *argv[]) {
2651 fprintf(stderr, "Too many arguments!\n");
2660 char filename[PATH_MAX] = "";
2662 bool firstline = true;
2664 while(fgets(buf, sizeof(buf), in)) {
2665 if(sscanf(buf, "Name = %4095s", name) == 1) {
2668 if(!check_id(name)) {
2669 fprintf(stderr, "Invalid Name in input!\n");
2677 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2678 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2682 if(!force && !access(filename, F_OK)) {
2683 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2688 out = fopen(filename, "w");
2691 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2697 } else if(firstline) {
2698 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2703 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2708 if(fputs(buf, out) < 0) {
2709 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2720 fprintf(stderr, "Imported %d host configuration files.\n", count);
2723 fprintf(stderr, "No host configuration files imported.\n");
2728 static int cmd_exchange(int argc, char *argv[]) {
2729 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2732 static int cmd_exchange_all(int argc, char *argv[]) {
2733 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2736 static int switch_network(char *name) {
2737 if(strcmp(name, ".")) {
2738 if(!check_netname(name, false)) {
2739 fprintf(stderr, "Invalid character in netname!\n");
2743 if(!check_netname(name, true)) {
2744 fprintf(stderr, "Warning: unsafe character in netname!\n");
2754 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2761 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2762 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2763 xasprintf(&prompt, "%s> ", identname);
2768 static int cmd_network(int argc, char *argv[]) {
2770 fprintf(stderr, "Too many arguments!\n");
2775 return switch_network(argv[1]);
2778 DIR *dir = opendir(confdir);
2781 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2787 while((ent = readdir(dir))) {
2788 if(*ent->d_name == '.') {
2792 if(!strcmp(ent->d_name, "tinc.conf")) {
2797 char fname[PATH_MAX];
2798 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2800 if(!access(fname, R_OK)) {
2801 printf("%s\n", ent->d_name);
2810 static int cmd_fsck(int argc, char *argv[]) {
2814 fprintf(stderr, "Too many arguments!\n");
2818 return fsck(orig_argv[0]);
2821 static void *readfile(FILE *in, size_t *len) {
2823 size_t bufsize = 4096;
2824 char *buf = xmalloc(bufsize);
2827 size_t read = fread(buf + count, 1, bufsize - count, in);
2835 if(count >= bufsize) {
2837 buf = xrealloc(buf, bufsize);
2848 static int cmd_sign(int argc, char *argv[]) {
2850 fprintf(stderr, "Too many arguments!\n");
2855 name = get_my_name(true);
2862 char fname[PATH_MAX];
2863 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2864 FILE *fp = fopen(fname, "r");
2867 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2871 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2874 fprintf(stderr, "Could not read private key from %s\n", fname);
2884 in = fopen(argv[1], "rb");
2887 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2896 char *data = readfile(in, &len);
2903 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2908 // Ensure we sign our name and current time as well
2909 long t = time(NULL);
2911 xasprintf(&trailer, " %s %ld", name, t);
2912 int trailer_len = strlen(trailer);
2914 data = xrealloc(data, len + trailer_len);
2915 memcpy(data + len, trailer, trailer_len);
2920 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2921 fprintf(stderr, "Error generating signature\n");
2927 b64encode(sig, sig, 64);
2930 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2931 fwrite(data, len, 1, stdout);
2937 static int cmd_verify(int argc, char *argv[]) {
2939 fprintf(stderr, "Not enough arguments!\n");
2944 fprintf(stderr, "Too many arguments!\n");
2948 char *node = argv[1];
2950 if(!strcmp(node, ".")) {
2952 name = get_my_name(true);
2960 } else if(!strcmp(node, "*")) {
2963 if(!check_id(node)) {
2964 fprintf(stderr, "Invalid node name\n");
2972 in = fopen(argv[2], "rb");
2975 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2983 char *data = readfile(in, &len);
2990 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2994 char *newline = memchr(data, '\n', len);
2996 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2997 fprintf(stderr, "Invalid input\n");
3003 size_t skip = newline - data;
3005 char signer[MAX_STRING_SIZE] = "";
3006 char sig[MAX_STRING_SIZE] = "";
3009 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
3010 fprintf(stderr, "Invalid input\n");
3015 if(node && strcmp(node, signer)) {
3016 fprintf(stderr, "Signature is not made by %s\n", node);
3026 xasprintf(&trailer, " %s %ld", signer, t);
3027 int trailer_len = strlen(trailer);
3029 data = xrealloc(data, len + trailer_len);
3030 memcpy(data + len, trailer, trailer_len);
3033 newline = data + skip;
3035 char fname[PATH_MAX];
3036 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
3037 FILE *fp = fopen(fname, "r");
3040 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
3045 ecdsa_t *key = get_pubkey(fp);
3049 key = ecdsa_read_pem_public_key(fp);
3053 fprintf(stderr, "Could not read public key from %s\n", fname);
3061 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
3062 fprintf(stderr, "Invalid signature\n");
3070 fwrite(newline, len - (newline - data), 1, stdout);
3076 static const struct {
3077 const char *command;
3078 int (*function)(int argc, char *argv[]);
3081 {"start", cmd_start, false},
3082 {"stop", cmd_stop, false},
3083 {"restart", cmd_restart, false},
3084 {"reload", cmd_reload, false},
3085 {"dump", cmd_dump, false},
3086 {"list", cmd_dump, false},
3087 {"purge", cmd_purge, false},
3088 {"debug", cmd_debug, false},
3089 {"retry", cmd_retry, false},
3090 {"connect", cmd_connect, false},
3091 {"disconnect", cmd_disconnect, false},
3092 {"top", cmd_top, false},
3093 {"pcap", cmd_pcap, false},
3094 {"log", cmd_log, false},
3095 {"pid", cmd_pid, false},
3096 {"config", cmd_config, true},
3097 {"add", cmd_config, false},
3098 {"del", cmd_config, false},
3099 {"get", cmd_config, false},
3100 {"set", cmd_config, false},
3101 {"init", cmd_init, false},
3102 {"generate-keys", cmd_generate_keys, false},
3103 #ifndef DISABLE_LEGACY
3104 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3106 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3107 {"help", cmd_help, false},
3108 {"version", cmd_version, false},
3109 {"info", cmd_info, false},
3110 {"edit", cmd_edit, false},
3111 {"export", cmd_export, false},
3112 {"export-all", cmd_export_all, false},
3113 {"import", cmd_import, false},
3114 {"exchange", cmd_exchange, false},
3115 {"exchange-all", cmd_exchange_all, false},
3116 {"invite", cmd_invite, false},
3117 {"join", cmd_join, false},
3118 {"network", cmd_network, false},
3119 {"fsck", cmd_fsck, false},
3120 {"sign", cmd_sign, false},
3121 {"verify", cmd_verify, false},
3122 {NULL, NULL, false},
3125 #ifdef HAVE_READLINE
3126 static char *complete_command(const char *text, int state) {
3135 while(commands[i].command) {
3136 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3137 return xstrdup(commands[i].command);
3146 static char *complete_dump(const char *text, int state) {
3147 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3157 if(!strncasecmp(matches[i], text, strlen(text))) {
3158 return xstrdup(matches[i]);
3167 static char *complete_config(const char *text, int state) {
3176 while(variables[i].name) {
3177 char *dot = strchr(text, '.');
3180 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3182 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3186 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3187 return xstrdup(variables[i].name);
3197 static char *complete_info(const char *text, int state) {
3203 if(!connect_tincd(false)) {
3207 // Check the list of nodes
3208 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3209 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3212 while(recvline(fd, line, sizeof(line))) {
3214 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3227 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3231 if(!strncmp(item, text, strlen(text))) {
3232 return xstrdup(strip_weight(item));
3239 static char *complete_nothing(const char *text, int state) {
3245 static char **completion(const char *text, int start, int end) {
3247 char **matches = NULL;
3250 matches = rl_completion_matches(text, complete_command);
3251 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3252 matches = rl_completion_matches(text, complete_dump);
3253 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3254 matches = rl_completion_matches(text, complete_config);
3255 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3256 matches = rl_completion_matches(text, complete_config);
3257 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3258 matches = rl_completion_matches(text, complete_config);
3259 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3260 matches = rl_completion_matches(text, complete_config);
3261 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3262 matches = rl_completion_matches(text, complete_info);
3269 static int cmd_shell(int argc, char *argv[]) {
3270 xasprintf(&prompt, "%s> ", identname);
3274 int maxargs = argc + 16;
3275 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3277 for(int i = 0; i < argc; i++) {
3281 #ifdef HAVE_READLINE
3282 rl_readline_name = "tinc";
3283 rl_completion_entry_function = complete_nothing;
3284 rl_attempted_completion_function = completion;
3285 rl_filename_completion_desired = 0;
3290 #ifdef HAVE_READLINE
3295 rl_basic_word_break_characters = "\t\n ";
3296 line = readline(prompt);
3297 copy = line ? xstrdup(line) : NULL;
3299 line = fgets(buf, sizeof(buf), stdin);
3305 fputs(prompt, stdout);
3308 line = fgets(buf, sizeof(buf), stdin);
3315 /* Ignore comments */
3324 char *p = line + strspn(line, " \t\n");
3325 char *next = strtok(p, " \t\n");
3328 if(nargc >= maxargs) {
3330 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3335 next = strtok(NULL, " \t\n");
3342 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3343 #ifdef HAVE_READLINE
3352 for(int i = 0; commands[i].command; i++) {
3353 if(!strcasecmp(nargv[argc], commands[i].command)) {
3354 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3360 #ifdef HAVE_READLINE
3369 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3374 #ifdef HAVE_READLINE
3386 static void cleanup() {
3392 int main(int argc, char *argv[]) {
3393 program_name = argv[0];
3396 tty = isatty(0) && isatty(1);
3398 if(!parse_options(argc, argv)) {
3403 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3404 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3418 static struct WSAData wsa_state;
3420 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3421 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3427 gettimeofday(&now, NULL);
3428 srand(now.tv_sec + now.tv_usec);
3431 if(optind >= argc) {
3432 return cmd_shell(argc, argv);
3435 for(int i = 0; commands[i].command; i++) {
3436 if(!strcasecmp(argv[optind], commands[i].command)) {
3437 return commands[i].function(argc - optind, argv + optind);
3441 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);