2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2018 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);
96 printf("Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
97 "See the AUTHORS file for a complete list.\n\n"
98 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
99 "and you are welcome to redistribute it under certain conditions;\n"
100 "see the file COPYING for details.\n");
103 static void usage(bool status) {
105 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
107 printf("Usage: %s [options] command\n\n", program_name);
108 printf("Valid options are:\n"
109 " -b, --batch Don't ask for anything (non-interactive mode).\n"
110 " -c, --config=DIR Read configuration options from DIR.\n"
111 " -n, --net=NETNAME Connect to net NETNAME.\n"
112 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
113 " --force Force some commands to work despite warnings.\n"
114 " --help Display this help and exit.\n"
115 " --version Output version information and exit.\n"
117 "Valid commands are:\n"
118 " init [name] Create initial configuration files.\n"
119 " get VARIABLE Print current value of VARIABLE\n"
120 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
121 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
122 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
123 " start [tincd options] Start tincd.\n"
124 " stop Stop tincd.\n"
125 " restart [tincd options] Restart tincd.\n"
126 " reload Partially reload configuration of running tincd.\n"
127 " pid Show PID of currently running tincd.\n"
128 #ifdef DISABLE_LEGACY
129 " generate-keys Generate a new Ed25519 public/private keypair.\n"
131 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
132 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
134 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
135 " dump Dump a list of one of the following things:\n"
136 " [reachable] nodes - all known nodes in the VPN\n"
137 " edges - all known connections in the VPN\n"
138 " subnets - all known subnets in the VPN\n"
139 " connections - all meta connections with ourself\n"
140 " [di]graph - graph of the VPN in dotty format\n"
141 " invitations - outstanding invitations\n"
142 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
143 " purge Purge unreachable nodes\n"
144 " debug N Set debug level\n"
145 " retry Retry all outgoing connections\n"
146 " disconnect NODE Close meta connection with NODE\n"
148 " top Show real-time statistics\n"
150 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
151 " log [level] Dump log output [up to the specified level]\n"
152 " export Export host configuration of local node to standard output\n"
153 " export-all Export all host configuration files to standard output\n"
154 " import Import host configuration file(s) from standard input\n"
155 " exchange Same as export followed by import\n"
156 " exchange-all Same as export-all followed by import\n"
157 " invite NODE [...] Generate an invitation for NODE\n"
158 " join INVITATION Join a VPN using an INVITATION\n"
159 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
160 " fsck Check the configuration files for problems.\n"
161 " sign [FILE] Generate a signed version of a file.\n"
162 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
164 printf("Report bugs to tinc@tinc-vpn.org.\n");
168 static bool parse_options(int argc, char **argv) {
170 int option_index = 0;
172 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
174 case 0: /* long option */
181 case 'c': /* config file */
182 confbase = xstrdup(optarg);
183 confbasegiven = true;
186 case 'n': /* net name given */
187 netname = xstrdup(optarg);
190 case 1: /* show help */
194 case 2: /* show version */
198 case 3: /* open control socket here */
199 pidfilename = xstrdup(optarg);
206 case '?': /* wrong options */
215 if(!netname && (netname = getenv("NETNAME"))) {
216 netname = xstrdup(netname);
219 /* netname "." is special: a "top-level name" */
221 if(netname && (!*netname || !strcmp(netname, "."))) {
226 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
227 fprintf(stderr, "Invalid character in netname!\n");
234 /* Open a file with the desired permissions, minus the umask.
235 Also, if we want to create an executable file, we call fchmod()
236 to set the executable bits. */
238 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
239 mode_t mask = umask(0);
242 FILE *f = fopen(filename, mode);
245 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
251 if((perms & 0444) && f) {
252 fchmod(fileno(f), perms);
260 static void disable_old_keys(const char *filename, const char *what) {
261 char tmpfile[PATH_MAX] = "";
263 bool disabled = false;
268 r = fopen(filename, "r");
274 snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
276 struct stat st = {.st_mode = 0600};
277 fstat(fileno(r), &st);
278 w = fopenmask(tmpfile, "w", st.st_mode);
280 while(fgets(buf, sizeof(buf), r)) {
281 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
282 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
288 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
295 if(block || ed25519pubkey) {
299 if(fputs(buf, w) < 0) {
305 if(block && !strncmp(buf, "-----END ", 9)) {
315 if(ferror(r) || fclose(r) < 0) {
321 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
331 // We cannot atomically replace files on Windows.
332 char bakfile[PATH_MAX] = "";
333 snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
335 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
336 rename(bakfile, filename);
339 if(rename(tmpfile, filename)) {
341 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
346 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
353 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
355 char directory[PATH_MAX] = ".";
361 /* Check stdin and stdout */
363 /* Ask for a file and/or directory name. */
364 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
366 if(fgets(buf, sizeof(buf), stdin) == NULL) {
367 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
371 size_t len = strlen(buf);
384 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
387 if(filename[0] != '/') {
389 /* The directory is a relative path or a filename. */
390 getcwd(directory, sizeof(directory));
392 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
393 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
405 disable_old_keys(filename, what);
407 /* Open it first to keep the inode busy */
409 r = fopenmask(filename, mode, perms);
412 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
420 Generate a public/private Ed25519 keypair, and ask for a file to store
423 static bool ed25519_keygen(bool ask) {
426 char fname[PATH_MAX];
428 fprintf(stderr, "Generating Ed25519 keypair:\n");
430 if(!(key = ecdsa_generate())) {
431 fprintf(stderr, "Error during key generation!\n");
434 fprintf(stderr, "Done.\n");
437 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
438 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
444 if(!ecdsa_write_pem_private_key(key, f)) {
445 fprintf(stderr, "Error writing private key!\n");
452 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
454 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
457 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
463 char *pubkey = ecdsa_get_base64_public_key(key);
464 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
482 #ifndef DISABLE_LEGACY
484 Generate a public/private RSA keypair, and ask for a file to store
487 static bool rsa_keygen(int bits, bool ask) {
490 char fname[PATH_MAX];
492 // Make sure the key size is a multiple of 8 bits.
495 // Make sure that a valid key size is used.
496 if(bits < 1024 || bits > 8192) {
497 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
499 } else if(bits < 2048) {
500 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
503 fprintf(stderr, "Generating %d bits keys:\n", bits);
505 if(!(key = rsa_generate(bits, 0x10001))) {
506 fprintf(stderr, "Error during key generation!\n");
509 fprintf(stderr, "Done.\n");
512 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
513 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
519 if(!rsa_write_pem_private_key(key, f)) {
520 fprintf(stderr, "Error writing private key!\n");
527 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
529 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
532 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
538 if(!rsa_write_pem_public_key(key, f)) {
539 fprintf(stderr, "Error writing public key!\n");
562 bool recvline(int fd, char *line, size_t len) {
563 char *newline = NULL;
569 while(!(newline = memchr(buffer, '\n', blen))) {
570 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
572 if(result == -1 && sockerrno == EINTR) {
574 } else if(result <= 0) {
581 if((size_t)(newline - buffer) >= len) {
585 len = newline - buffer;
587 memcpy(line, buffer, len);
589 memmove(buffer, newline + 1, blen - len - 1);
595 static bool recvdata(int fd, char *data, size_t len) {
597 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
599 if(result == -1 && sockerrno == EINTR) {
601 } else if(result <= 0) {
608 memcpy(data, buffer, len);
609 memmove(buffer, buffer + len, blen - len);
615 bool sendline(int fd, char *format, ...) {
616 static char buffer[4096];
621 va_start(ap, format);
622 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
623 buffer[sizeof(buffer) - 1] = 0;
626 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
634 int result = send(fd, p, blen, MSG_NOSIGNAL);
636 if(result == -1 && sockerrno == EINTR) {
638 } else if(result <= 0) {
649 static void pcap(int fd, FILE *out, uint32_t snaplen) {
650 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
658 uint32_t tz_accuracy;
665 snaplen ? snaplen : sizeof(data),
678 fwrite(&header, sizeof(header), 1, out);
683 while(recvline(fd, line, sizeof(line))) {
685 int n = sscanf(line, "%d %d %d", &code, &req, &len);
686 gettimeofday(&tv, NULL);
688 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || (size_t)len > sizeof(data)) {
692 if(!recvdata(fd, data, len)) {
696 packet.tv_sec = tv.tv_sec;
697 packet.tv_usec = tv.tv_usec;
699 packet.origlen = len;
700 fwrite(&packet, sizeof(packet), 1, out);
701 fwrite(data, len, 1, out);
706 static void logcontrol(int fd, FILE *out, int level) {
707 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
711 while(recvline(fd, line, sizeof(line))) {
713 int n = sscanf(line, "%d %d %d", &code, &req, &len);
715 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
719 if(!recvdata(fd, data, len)) {
723 fwrite(data, len, 1, out);
729 static bool stop_tincd(void) {
730 if(!connect_tincd(true)) {
734 sendline(fd, "%d %d", CONTROL, REQ_STOP);
736 while(recvline(fd, line, sizeof(line))) {
737 // wait for tincd to close the connection...
748 static bool remove_service(void) {
749 SC_HANDLE manager = NULL;
750 SC_HANDLE service = NULL;
751 SERVICE_STATUS status = {0};
752 bool success = false;
754 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
757 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
761 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
764 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
765 success = stop_tincd();
767 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
773 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
774 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
776 fprintf(stderr, "%s service stopped\n", identname);
779 if(!DeleteService(service)) {
780 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
789 CloseServiceHandle(service);
793 CloseServiceHandle(manager);
797 fprintf(stderr, "%s service removed\n", identname);
804 bool connect_tincd(bool verbose) {
809 struct timeval tv = {0, 0};
811 if(select(fd + 1, &r, NULL, NULL, &tv)) {
812 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
820 FILE *f = fopen(pidfilename, "r");
824 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
833 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
835 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
846 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
847 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
848 /* clean up the stale socket and pid file */
850 unlink(unixsocketname);
854 struct sockaddr_un sa;
856 sa.sun_family = AF_UNIX;
858 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
860 sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
862 fd = socket(AF_UNIX, SOCK_STREAM, 0);
866 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
872 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
874 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
883 struct addrinfo hints = {
884 .ai_family = AF_UNSPEC,
885 .ai_socktype = SOCK_STREAM,
886 .ai_protocol = IPPROTO_TCP,
890 struct addrinfo *res = NULL;
892 if(getaddrinfo(host, port, &hints, &res) || !res) {
894 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
900 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
904 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
910 unsigned long arg = 0;
912 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
914 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
918 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
920 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
932 static const int one = 1;
933 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
936 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
941 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
943 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
951 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
953 fprintf(stderr, "Could not fully establish control socket connection\n");
965 static int cmd_start(int argc, char *argv[]) {
966 if(connect_tincd(false)) {
968 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
970 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
977 char *slash = strrchr(program_name, '/');
981 if((c = strrchr(program_name, '\\')) > slash) {
988 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
994 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
999 Windows has no real concept of an "argv array". A command line is just one string.
1000 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
1001 it uses quotes to handle spaces in arguments.
1002 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
1003 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
1004 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
1006 xasprintf(&arg0, "\"%s\"", arg0);
1008 nargv[nargc++] = arg0;
1010 for(int i = 1; i < optind; i++) {
1011 nargv[nargc++] = orig_argv[i];
1014 for(int i = 1; i < argc; i++) {
1015 nargv[nargc++] = argv[i];
1019 int status = spawnvp(_P_WAIT, c, nargv);
1022 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
1028 int pfd[2] = {-1, -1};
1030 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
1031 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
1039 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1047 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1048 setenv("TINC_UMBILICAL", buf, true);
1049 exit(execvp(c, nargv));
1056 int status = -1, result;
1058 signal(SIGINT, SIG_IGN);
1061 // Pass all log messages from the umbilical to stderr.
1062 // A nul-byte right before closure means tincd started successfully.
1063 bool failure = true;
1067 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1068 failure = buf[len - 1];
1083 // Make sure the child process is really gone.
1084 result = waitpid(pid, &status, 0);
1087 signal(SIGINT, SIG_DFL);
1090 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1091 fprintf(stderr, "Error starting %s\n", c);
1099 static int cmd_stop(int argc, char *argv[]) {
1103 fprintf(stderr, "Too many arguments!\n");
1108 return remove_service();
1113 if(kill(pid, SIGTERM)) {
1114 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1118 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1119 waitpid(pid, NULL, 0);
1130 static int cmd_restart(int argc, char *argv[]) {
1132 return cmd_start(argc, argv);
1135 static int cmd_reload(int argc, char *argv[]) {
1139 fprintf(stderr, "Too many arguments!\n");
1143 if(!connect_tincd(true)) {
1147 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1149 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1150 fprintf(stderr, "Could not reload configuration.\n");
1158 static int dump_invitations(void) {
1159 char dname[PATH_MAX];
1160 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1161 DIR *dir = opendir(dname);
1164 if(errno == ENOENT) {
1165 fprintf(stderr, "No outstanding invitations.\n");
1169 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1177 while((ent = readdir(dir))) {
1178 char buf[MAX_STRING_SIZE];
1180 if(b64decode(ent->d_name, buf, 24) != 18) {
1184 char fname[PATH_MAX];
1186 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1187 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1191 FILE *f = fopen(fname, "r");
1194 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1200 if(!fgets(buf, sizeof(buf), f)) {
1201 fprintf(stderr, "Invalid invitation file %s\n", fname);
1208 char *eol = buf + strlen(buf);
1210 while(strchr("\t \r\n", *--eol)) {
1214 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1215 fprintf(stderr, "Invalid invitation file %s\n", fname);
1220 printf("%s %s\n", ent->d_name, buf + 7);
1226 fprintf(stderr, "No outstanding invitations.\n");
1232 static int cmd_dump(int argc, char *argv[]) {
1233 bool only_reachable = false;
1235 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1236 if(strcasecmp(argv[2], "nodes")) {
1237 fprintf(stderr, "`reachable' only supported for nodes.\n");
1242 only_reachable = true;
1248 fprintf(stderr, "Invalid number of arguments.\n");
1253 if(!strcasecmp(argv[1], "invitations")) {
1254 return dump_invitations();
1257 if(!connect_tincd(true)) {
1263 if(!strcasecmp(argv[1], "nodes")) {
1264 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1265 } else if(!strcasecmp(argv[1], "edges")) {
1266 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1267 } else if(!strcasecmp(argv[1], "subnets")) {
1268 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1269 } else if(!strcasecmp(argv[1], "connections")) {
1270 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1271 } else if(!strcasecmp(argv[1], "graph")) {
1272 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1273 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1275 } else if(!strcasecmp(argv[1], "digraph")) {
1276 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1277 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1280 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1286 printf("graph {\n");
1287 } else if(do_graph == 2) {
1288 printf("digraph {\n");
1291 while(recvline(fd, line, sizeof(line))) {
1292 char node1[4096], node2[4096];
1293 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1296 if(do_graph && req == REQ_DUMP_NODES) {
1318 char local_host[4096];
1319 char local_port[4096];
1322 int cipher, digest, maclength, compression, distance, socket, weight;
1323 short int pmtu, minmtu, maxmtu;
1324 unsigned int options, status_int;
1325 node_status_t status;
1326 long int last_state_change;
1328 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1331 case REQ_DUMP_NODES: {
1332 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);
1335 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1339 memcpy(&status, &status_int, sizeof(status));
1342 const char *color = "black";
1344 if(!strcmp(host, "MYSELF")) {
1346 } else if(!status.reachable) {
1348 } else if(strcmp(via, node)) {
1350 } else if(!status.validkey) {
1352 } else if(minmtu > 0) {
1356 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1358 if(only_reachable && !status.reachable) {
1362 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,
1363 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);
1365 if(udp_ping_rtt != -1) {
1366 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1374 case REQ_DUMP_EDGES: {
1375 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);
1378 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1383 float w = 1 + 65536.0 / weight;
1385 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1386 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1387 } else if(do_graph == 2) {
1388 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1391 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);
1396 case REQ_DUMP_SUBNETS: {
1397 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1400 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1404 printf("%s owner %s\n", strip_weight(subnet), node);
1408 case REQ_DUMP_CONNECTIONS: {
1409 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1412 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1416 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1421 fprintf(stderr, "Unable to parse dump from tincd.\n");
1426 fprintf(stderr, "Error receiving dump.\n");
1430 static int cmd_purge(int argc, char *argv[]) {
1434 fprintf(stderr, "Too many arguments!\n");
1438 if(!connect_tincd(true)) {
1442 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1444 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1445 fprintf(stderr, "Could not purge old information.\n");
1452 static int cmd_debug(int argc, char *argv[]) {
1454 fprintf(stderr, "Invalid number of arguments.\n");
1458 if(!connect_tincd(true)) {
1462 int debuglevel = atoi(argv[1]);
1465 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1467 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1468 fprintf(stderr, "Could not set debug level.\n");
1472 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1476 static int cmd_retry(int argc, char *argv[]) {
1480 fprintf(stderr, "Too many arguments!\n");
1484 if(!connect_tincd(true)) {
1488 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1490 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1491 fprintf(stderr, "Could not retry outgoing connections.\n");
1498 static int cmd_connect(int argc, char *argv[]) {
1500 fprintf(stderr, "Invalid number of arguments.\n");
1504 if(!check_id(argv[1])) {
1505 fprintf(stderr, "Invalid name for node.\n");
1509 if(!connect_tincd(true)) {
1513 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1515 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1516 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1523 static int cmd_disconnect(int argc, char *argv[]) {
1525 fprintf(stderr, "Invalid number of arguments.\n");
1529 if(!check_id(argv[1])) {
1530 fprintf(stderr, "Invalid name for node.\n");
1534 if(!connect_tincd(true)) {
1538 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1540 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1541 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1548 static int cmd_top(int argc, char *argv[]) {
1552 fprintf(stderr, "Too many arguments!\n");
1558 if(!connect_tincd(true)) {
1565 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1570 static int cmd_pcap(int argc, char *argv[]) {
1572 fprintf(stderr, "Too many arguments!\n");
1576 if(!connect_tincd(true)) {
1580 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1585 static void sigint_handler(int sig) {
1588 fprintf(stderr, "\n");
1589 shutdown(fd, SHUT_RDWR);
1593 static int cmd_log(int argc, char *argv[]) {
1595 fprintf(stderr, "Too many arguments!\n");
1599 if(!connect_tincd(true)) {
1604 signal(SIGINT, sigint_handler);
1607 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1610 signal(SIGINT, SIG_DFL);
1618 static int cmd_pid(int argc, char *argv[]) {
1622 fprintf(stderr, "Too many arguments!\n");
1626 if(!connect_tincd(true) || !pid) {
1630 printf("%d\n", pid);
1634 int rstrip(char *value) {
1635 int len = strlen(value);
1637 while(len && strchr("\t\r\n ", value[len - 1])) {
1644 char *get_my_name(bool verbose) {
1645 FILE *f = fopen(tinc_conf, "r");
1649 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1658 while(fgets(buf, sizeof(buf), f)) {
1659 int len = strcspn(buf, "\t =");
1661 value += strspn(value, "\t ");
1665 value += strspn(value, "\t ");
1668 if(!rstrip(value)) {
1674 if(strcasecmp(buf, "Name")) {
1680 return replace_name(value);
1687 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1693 ecdsa_t *get_pubkey(FILE *f) {
1697 while(fgets(buf, sizeof(buf), f)) {
1698 int len = strcspn(buf, "\t =");
1700 value += strspn(value, "\t ");
1704 value += strspn(value, "\t ");
1707 if(!rstrip(value)) {
1713 if(strcasecmp(buf, "Ed25519PublicKey")) {
1718 return ecdsa_set_base64_public_key(value);
1725 const var_t variables[] = {
1726 /* Server configuration */
1727 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1728 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1729 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1730 {"BindToInterface", VAR_SERVER},
1731 {"Broadcast", VAR_SERVER | VAR_SAFE},
1732 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1733 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1734 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1735 {"Device", VAR_SERVER},
1736 {"DeviceStandby", VAR_SERVER},
1737 {"DeviceType", VAR_SERVER},
1738 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1739 {"Ed25519PrivateKeyFile", VAR_SERVER},
1740 {"ExperimentalProtocol", VAR_SERVER},
1741 {"Forwarding", VAR_SERVER},
1742 {"FWMark", VAR_SERVER},
1743 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1744 {"Hostnames", VAR_SERVER},
1745 {"IffOneQueue", VAR_SERVER},
1746 {"Interface", VAR_SERVER},
1747 {"InvitationExpire", VAR_SERVER},
1748 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1749 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1750 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1751 {"LogLevel", VAR_SERVER},
1752 {"MACExpire", VAR_SERVER | VAR_SAFE},
1753 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1754 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1755 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1756 {"Mode", VAR_SERVER | VAR_SAFE},
1757 {"Name", VAR_SERVER},
1758 {"PingInterval", VAR_SERVER | VAR_SAFE},
1759 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1760 {"PriorityInheritance", VAR_SERVER},
1761 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1762 {"PrivateKeyFile", VAR_SERVER},
1763 {"ProcessPriority", VAR_SERVER},
1764 {"Proxy", VAR_SERVER},
1765 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1766 {"ScriptsExtension", VAR_SERVER},
1767 {"ScriptsInterpreter", VAR_SERVER},
1768 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1769 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1770 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1771 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1772 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1773 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1774 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1775 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1776 {"UDPRcvBuf", VAR_SERVER},
1777 {"UDPSndBuf", VAR_SERVER},
1778 {"UPnP", VAR_SERVER},
1779 {"UPnPDiscoverWait", VAR_SERVER},
1780 {"UPnPRefreshPeriod", VAR_SERVER},
1781 {"VDEGroup", VAR_SERVER},
1782 {"VDEPort", VAR_SERVER},
1783 /* Host configuration */
1784 {"Address", VAR_HOST | VAR_MULTIPLE},
1785 {"Cipher", VAR_SERVER | VAR_HOST},
1786 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1787 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1788 {"Digest", VAR_SERVER | VAR_HOST},
1789 {"Ed25519PublicKey", VAR_HOST},
1790 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1791 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1792 {"MACLength", VAR_SERVER | VAR_HOST},
1793 {"PMTU", VAR_SERVER | VAR_HOST},
1794 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1796 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1797 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1798 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1799 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1800 {"Weight", VAR_HOST | VAR_SAFE},
1804 static int cmd_config(int argc, char *argv[]) {
1806 fprintf(stderr, "Invalid number of arguments.\n");
1810 if(strcasecmp(argv[0], "config")) {
1816 if(!strcasecmp(argv[1], "get")) {
1818 } else if(!strcasecmp(argv[1], "add")) {
1819 argv++, argc--, action = 1;
1820 } else if(!strcasecmp(argv[1], "del")) {
1821 argv++, argc--, action = -1;
1822 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1823 argv++, argc--, action = 0;
1827 fprintf(stderr, "Invalid number of arguments.\n");
1831 // Concatenate the rest of the command line
1832 strncpy(line, argv[1], sizeof(line) - 1);
1834 for(int i = 2; i < argc; i++) {
1835 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1836 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1839 // Liberal parsing into node name, variable name and value.
1845 len = strcspn(line, "\t =");
1847 value += strspn(value, "\t ");
1851 value += strspn(value, "\t ");
1855 variable = strchr(line, '.');
1865 fprintf(stderr, "No variable given.\n");
1869 if(action >= 0 && !*value) {
1870 fprintf(stderr, "No value for variable given.\n");
1874 if(action < -1 && *value) {
1878 /* Some simple checks. */
1880 bool warnonremove = false;
1882 for(int i = 0; variables[i].name; i++) {
1883 if(strcasecmp(variables[i].name, variable)) {
1888 variable = (char *)variables[i].name;
1890 if(!strcasecmp(variable, "Subnet")) {
1893 if(!str2net(&s, value)) {
1894 fprintf(stderr, "Malformed subnet definition %s\n", value);
1897 if(!subnetcheck(s)) {
1898 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1903 /* Discourage use of obsolete variables. */
1905 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1907 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1909 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1914 /* Don't put server variables in host config files */
1916 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1918 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1920 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1925 /* Should this go into our own host config file? */
1927 if(!node && !(variables[i].type & VAR_SERVER)) {
1928 node = get_my_name(true);
1935 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1936 Turn on warnings when it seems variables might be removed unintentionally. */
1938 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1939 warnonremove = true;
1941 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1942 warnonremove = true;
1948 if(node && !check_id(node)) {
1949 fprintf(stderr, "Invalid name for node.\n");
1954 if(force || action < 0) {
1955 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1957 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1962 // Open the right configuration file.
1963 char filename[PATH_MAX];
1966 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1968 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1971 FILE *f = fopen(filename, "r");
1974 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1978 char tmpfile[PATH_MAX];
1982 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1983 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1987 tf = fopen(tmpfile, "w");
1990 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1996 // Copy the file, making modifications on the fly, unless we are just getting a value.
2000 bool removed = false;
2003 while(fgets(buf1, sizeof(buf1), f)) {
2004 buf1[sizeof(buf1) - 1] = 0;
2005 strncpy(buf2, buf1, sizeof(buf2));
2007 // Parse line in a simple way
2011 len = strcspn(buf2, "\t =");
2012 bvalue = buf2 + len;
2013 bvalue += strspn(bvalue, "\t ");
2015 if(*bvalue == '=') {
2017 bvalue += strspn(bvalue, "\t ");
2024 if(!strcasecmp(buf2, variable)) {
2028 printf("%s\n", bvalue);
2030 } else if(action == -1) {
2031 if(!*value || !strcasecmp(bvalue, value)) {
2037 } else if(action == 0) {
2038 // Warn if "set" was used for variables that can occur multiple times
2039 if(warnonremove && strcasecmp(bvalue, value)) {
2040 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2043 // Already set? Delete the rest...
2048 // Otherwise, replace.
2049 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2050 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2057 } else if(action > 0) {
2058 // Check if we've already seen this variable with the same value
2059 if(!strcasecmp(bvalue, value)) {
2066 // Copy original line...
2067 if(fputs(buf1, tf) < 0) {
2068 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2072 // Add newline if it is missing...
2073 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2074 if(fputc('\n', tf) < 0) {
2075 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2082 // Make sure we read everything...
2083 if(ferror(f) || !feof(f)) {
2084 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2089 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2093 // Add new variable if necessary.
2094 if((action > 0 && !found) || (action == 0 && !set)) {
2095 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2096 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2105 fprintf(stderr, "No matching configuration variables found.\n");
2110 // Make sure we wrote everything...
2112 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2116 // Could we find what we had to remove?
2117 if(action < 0 && !removed) {
2119 fprintf(stderr, "No configuration variables deleted.\n");
2123 // Replace the configuration file with the new one
2126 if(remove(filename)) {
2127 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2133 if(rename(tmpfile, filename)) {
2134 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2138 // Silently try notifying a running tincd of changes.
2139 if(connect_tincd(false)) {
2140 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2146 static bool try_bind(int port) {
2147 struct addrinfo *ai = NULL, *aip;
2148 struct addrinfo hint = {
2149 .ai_flags = AI_PASSIVE,
2150 .ai_family = AF_UNSPEC,
2151 .ai_socktype = SOCK_STREAM,
2152 .ai_protocol = IPPROTO_TCP,
2155 bool success = true;
2157 snprintf(portstr, sizeof(portstr), "%d", port);
2159 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2163 for(aip = ai; aip; aip = aip->ai_next) {
2164 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2171 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2184 int check_port(const char *name) {
2189 fprintf(stderr, "Warning: could not bind to port 655. ");
2191 for(int i = 0; i < 100; i++) {
2192 int port = 0x1000 + (rand() & 0x7fff);
2194 if(try_bind(port)) {
2195 char filename[PATH_MAX];
2196 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2197 FILE *f = fopen(filename, "a");
2200 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2201 fprintf(stderr, "Please change tinc's Port manually.\n");
2205 fprintf(f, "Port = %d\n", port);
2207 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2212 fprintf(stderr, "Please change tinc's Port manually.\n");
2216 static int cmd_init(int argc, char *argv[]) {
2217 if(!access(tinc_conf, F_OK)) {
2218 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2223 fprintf(stderr, "Too many arguments!\n");
2225 } else if(argc < 2) {
2228 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2230 if(!fgets(buf, sizeof(buf), stdin)) {
2231 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2235 int len = rstrip(buf);
2238 fprintf(stderr, "No name given!\n");
2244 fprintf(stderr, "No Name given!\n");
2248 name = strdup(argv[1]);
2251 fprintf(stderr, "No Name given!\n");
2256 if(!check_id(name)) {
2257 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2261 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2262 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2266 if(mkdir(confbase, 0777) && errno != EEXIST) {
2267 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2271 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2272 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2276 FILE *f = fopen(tinc_conf, "w");
2279 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2283 fprintf(f, "Name = %s\n", name);
2286 #ifndef DISABLE_LEGACY
2288 if(!rsa_keygen(2048, false)) {
2294 if(!ed25519_keygen(false)) {
2301 char filename[PATH_MAX];
2302 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2304 if(access(filename, F_OK)) {
2305 FILE *f = fopenmask(filename, "w", 0777);
2308 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2312 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");
2322 static int cmd_generate_keys(int argc, char *argv[]) {
2323 #ifdef DISABLE_LEGACY
2331 fprintf(stderr, "Too many arguments!\n");
2336 name = get_my_name(false);
2339 #ifndef DISABLE_LEGACY
2341 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2347 if(!ed25519_keygen(true)) {
2354 #ifndef DISABLE_LEGACY
2355 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2357 fprintf(stderr, "Too many arguments!\n");
2362 name = get_my_name(false);
2365 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2369 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2373 fprintf(stderr, "Too many arguments!\n");
2378 name = get_my_name(false);
2381 return !ed25519_keygen(true);
2384 static int cmd_help(int argc, char *argv[]) {
2392 static int cmd_version(int argc, char *argv[]) {
2396 fprintf(stderr, "Too many arguments!\n");
2404 static int cmd_info(int argc, char *argv[]) {
2406 fprintf(stderr, "Invalid number of arguments.\n");
2410 if(!connect_tincd(true)) {
2414 return info(fd, argv[1]);
2417 static const char *conffiles[] = {
2428 static int cmd_edit(int argc, char *argv[]) {
2430 fprintf(stderr, "Invalid number of arguments.\n");
2434 char filename[PATH_MAX] = "";
2436 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2437 for(int i = 0; conffiles[i]; i++) {
2438 if(!strcmp(argv[1], conffiles[i])) {
2439 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2448 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2449 char *dash = strchr(argv[1], '-');
2454 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2455 fprintf(stderr, "Invalid configuration filename.\n");
2463 const char *editor = getenv("VISUAL");
2466 editor = getenv("EDITOR");
2473 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2475 xasprintf(&command, "edit \"%s\"", filename);
2477 int result = system(command);
2484 // Silently try notifying a running tincd of changes.
2485 if(connect_tincd(false)) {
2486 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2492 static int export(const char *name, FILE *out) {
2493 char filename[PATH_MAX];
2494 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2495 FILE *in = fopen(filename, "r");
2498 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2502 fprintf(out, "Name = %s\n", name);
2505 while(fgets(buf, sizeof(buf), in)) {
2506 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2512 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2521 static int cmd_export(int argc, char *argv[]) {
2525 fprintf(stderr, "Too many arguments!\n");
2529 char *name = get_my_name(true);
2535 int result = export(name, stdout);
2545 static int cmd_export_all(int argc, char *argv[]) {
2549 fprintf(stderr, "Too many arguments!\n");
2553 DIR *dir = opendir(hosts_dir);
2556 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2564 while((ent = readdir(dir))) {
2565 if(!check_id(ent->d_name)) {
2572 printf("#---------------------------------------------------------------#\n");
2575 result |= export(ent->d_name, stdout);
2587 static int cmd_import(int argc, char *argv[]) {
2591 fprintf(stderr, "Too many arguments!\n");
2600 char filename[PATH_MAX] = "";
2602 bool firstline = true;
2604 while(fgets(buf, sizeof(buf), in)) {
2605 if(sscanf(buf, "Name = %4095s", name) == 1) {
2608 if(!check_id(name)) {
2609 fprintf(stderr, "Invalid Name in input!\n");
2617 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2618 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2622 if(!force && !access(filename, F_OK)) {
2623 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2628 out = fopen(filename, "w");
2631 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2637 } else if(firstline) {
2638 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2643 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2648 if(fputs(buf, out) < 0) {
2649 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2660 fprintf(stderr, "Imported %d host configuration files.\n", count);
2663 fprintf(stderr, "No host configuration files imported.\n");
2668 static int cmd_exchange(int argc, char *argv[]) {
2669 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2672 static int cmd_exchange_all(int argc, char *argv[]) {
2673 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2676 static int switch_network(char *name) {
2677 if(strcmp(name, ".")) {
2678 if(!check_netname(name, false)) {
2679 fprintf(stderr, "Invalid character in netname!\n");
2683 if(!check_netname(name, true)) {
2684 fprintf(stderr, "Warning: unsafe character in netname!\n");
2694 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2701 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2702 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2703 xasprintf(&prompt, "%s> ", identname);
2708 static int cmd_network(int argc, char *argv[]) {
2710 fprintf(stderr, "Too many arguments!\n");
2715 return switch_network(argv[1]);
2718 DIR *dir = opendir(confdir);
2721 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2727 while((ent = readdir(dir))) {
2728 if(*ent->d_name == '.') {
2732 if(!strcmp(ent->d_name, "tinc.conf")) {
2737 char fname[PATH_MAX];
2738 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2740 if(!access(fname, R_OK)) {
2741 printf("%s\n", ent->d_name);
2750 static int cmd_fsck(int argc, char *argv[]) {
2754 fprintf(stderr, "Too many arguments!\n");
2758 return fsck(orig_argv[0]);
2761 static void *readfile(FILE *in, size_t *len) {
2763 size_t bufsize = 4096;
2764 char *buf = xmalloc(bufsize);
2767 size_t read = fread(buf + count, 1, bufsize - count, in);
2775 if(count >= bufsize) {
2777 buf = xrealloc(buf, bufsize);
2788 static int cmd_sign(int argc, char *argv[]) {
2790 fprintf(stderr, "Too many arguments!\n");
2795 name = get_my_name(true);
2802 char fname[PATH_MAX];
2803 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2804 FILE *fp = fopen(fname, "r");
2807 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2811 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2814 fprintf(stderr, "Could not read private key from %s\n", fname);
2824 in = fopen(argv[1], "rb");
2827 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2836 char *data = readfile(in, &len);
2843 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2848 // Ensure we sign our name and current time as well
2849 long t = time(NULL);
2851 xasprintf(&trailer, " %s %ld", name, t);
2852 int trailer_len = strlen(trailer);
2854 data = xrealloc(data, len + trailer_len);
2855 memcpy(data + len, trailer, trailer_len);
2860 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2861 fprintf(stderr, "Error generating signature\n");
2867 b64encode(sig, sig, 64);
2870 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2871 fwrite(data, len, 1, stdout);
2877 static int cmd_verify(int argc, char *argv[]) {
2879 fprintf(stderr, "Not enough arguments!\n");
2884 fprintf(stderr, "Too many arguments!\n");
2888 char *node = argv[1];
2890 if(!strcmp(node, ".")) {
2892 name = get_my_name(true);
2900 } else if(!strcmp(node, "*")) {
2903 if(!check_id(node)) {
2904 fprintf(stderr, "Invalid node name\n");
2912 in = fopen(argv[2], "rb");
2915 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2923 char *data = readfile(in, &len);
2930 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2934 char *newline = memchr(data, '\n', len);
2936 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2937 fprintf(stderr, "Invalid input\n");
2943 size_t skip = newline - data;
2945 char signer[MAX_STRING_SIZE] = "";
2946 char sig[MAX_STRING_SIZE] = "";
2949 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2950 fprintf(stderr, "Invalid input\n");
2955 if(node && strcmp(node, signer)) {
2956 fprintf(stderr, "Signature is not made by %s\n", node);
2966 xasprintf(&trailer, " %s %ld", signer, t);
2967 int trailer_len = strlen(trailer);
2969 data = xrealloc(data, len + trailer_len);
2970 memcpy(data + len, trailer, trailer_len);
2973 newline = data + skip;
2975 char fname[PATH_MAX];
2976 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2977 FILE *fp = fopen(fname, "r");
2980 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2985 ecdsa_t *key = get_pubkey(fp);
2989 key = ecdsa_read_pem_public_key(fp);
2993 fprintf(stderr, "Could not read public key from %s\n", fname);
3001 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
3002 fprintf(stderr, "Invalid signature\n");
3010 fwrite(newline, len - (newline - data), 1, stdout);
3016 static const struct {
3017 const char *command;
3018 int (*function)(int argc, char *argv[]);
3021 {"start", cmd_start, false},
3022 {"stop", cmd_stop, false},
3023 {"restart", cmd_restart, false},
3024 {"reload", cmd_reload, false},
3025 {"dump", cmd_dump, false},
3026 {"list", cmd_dump, false},
3027 {"purge", cmd_purge, false},
3028 {"debug", cmd_debug, false},
3029 {"retry", cmd_retry, false},
3030 {"connect", cmd_connect, false},
3031 {"disconnect", cmd_disconnect, false},
3032 {"top", cmd_top, false},
3033 {"pcap", cmd_pcap, false},
3034 {"log", cmd_log, false},
3035 {"pid", cmd_pid, false},
3036 {"config", cmd_config, true},
3037 {"add", cmd_config, false},
3038 {"del", cmd_config, false},
3039 {"get", cmd_config, false},
3040 {"set", cmd_config, false},
3041 {"init", cmd_init, false},
3042 {"generate-keys", cmd_generate_keys, false},
3043 #ifndef DISABLE_LEGACY
3044 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3046 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3047 {"help", cmd_help, false},
3048 {"version", cmd_version, false},
3049 {"info", cmd_info, false},
3050 {"edit", cmd_edit, false},
3051 {"export", cmd_export, false},
3052 {"export-all", cmd_export_all, false},
3053 {"import", cmd_import, false},
3054 {"exchange", cmd_exchange, false},
3055 {"exchange-all", cmd_exchange_all, false},
3056 {"invite", cmd_invite, false},
3057 {"join", cmd_join, false},
3058 {"network", cmd_network, false},
3059 {"fsck", cmd_fsck, false},
3060 {"sign", cmd_sign, false},
3061 {"verify", cmd_verify, false},
3062 {NULL, NULL, false},
3065 #ifdef HAVE_READLINE
3066 static char *complete_command(const char *text, int state) {
3075 while(commands[i].command) {
3076 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3077 return xstrdup(commands[i].command);
3086 static char *complete_dump(const char *text, int state) {
3087 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3097 if(!strncasecmp(matches[i], text, strlen(text))) {
3098 return xstrdup(matches[i]);
3107 static char *complete_config(const char *text, int state) {
3116 while(variables[i].name) {
3117 char *dot = strchr(text, '.');
3120 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3122 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3126 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3127 return xstrdup(variables[i].name);
3137 static char *complete_info(const char *text, int state) {
3143 if(!connect_tincd(false)) {
3147 // Check the list of nodes
3148 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3149 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3152 while(recvline(fd, line, sizeof(line))) {
3154 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3167 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3171 if(!strncmp(item, text, strlen(text))) {
3172 return xstrdup(strip_weight(item));
3179 static char *complete_nothing(const char *text, int state) {
3185 static char **completion(const char *text, int start, int end) {
3187 char **matches = NULL;
3190 matches = rl_completion_matches(text, complete_command);
3191 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3192 matches = rl_completion_matches(text, complete_dump);
3193 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3194 matches = rl_completion_matches(text, complete_config);
3195 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3196 matches = rl_completion_matches(text, complete_config);
3197 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3198 matches = rl_completion_matches(text, complete_config);
3199 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3200 matches = rl_completion_matches(text, complete_config);
3201 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3202 matches = rl_completion_matches(text, complete_info);
3209 static int cmd_shell(int argc, char *argv[]) {
3210 xasprintf(&prompt, "%s> ", identname);
3214 int maxargs = argc + 16;
3215 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3217 for(int i = 0; i < argc; i++) {
3221 #ifdef HAVE_READLINE
3222 rl_readline_name = "tinc";
3223 rl_completion_entry_function = complete_nothing;
3224 rl_attempted_completion_function = completion;
3225 rl_filename_completion_desired = 0;
3230 #ifdef HAVE_READLINE
3235 rl_basic_word_break_characters = "\t\n ";
3236 line = readline(prompt);
3237 copy = line ? xstrdup(line) : NULL;
3239 line = fgets(buf, sizeof(buf), stdin);
3245 fputs(prompt, stdout);
3248 line = fgets(buf, sizeof(buf), stdin);
3255 /* Ignore comments */
3264 char *p = line + strspn(line, " \t\n");
3265 char *next = strtok(p, " \t\n");
3268 if(nargc >= maxargs) {
3270 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3275 next = strtok(NULL, " \t\n");
3282 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3283 #ifdef HAVE_READLINE
3292 for(int i = 0; commands[i].command; i++) {
3293 if(!strcasecmp(nargv[argc], commands[i].command)) {
3294 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3300 #ifdef HAVE_READLINE
3309 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3314 #ifdef HAVE_READLINE
3327 int main(int argc, char *argv[]) {
3328 program_name = argv[0];
3331 tty = isatty(0) && isatty(1);
3333 if(!parse_options(argc, argv)) {
3338 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3339 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3352 static struct WSAData wsa_state;
3354 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3355 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3364 if(optind >= argc) {
3365 return cmd_shell(argc, argv);
3368 for(int i = 0; commands[i].command; i++) {
3369 if(!strcasecmp(argv[optind], commands[i].command)) {
3370 return commands[i].function(argc - optind, argv + optind);
3374 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);