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);
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 key pair.\n"
131 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
132 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
134 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\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);
241 umask(~perms & 0777);
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;
267 FILE *r = fopen(filename, "r");
274 int result = snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
276 if(result < sizeof(tmpfile)) {
277 struct stat st = {.st_mode = 0600};
278 fstat(fileno(r), &st);
279 w = fopenmask(tmpfile, "w", st.st_mode);
282 while(fgets(buf, sizeof(buf), r)) {
283 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
284 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
290 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
297 if(block || ed25519pubkey) {
301 if(fputs(buf, w) < 0) {
307 if(block && !strncmp(buf, "-----END ", 9)) {
317 if(ferror(r) || fclose(r) < 0) {
323 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
333 // We cannot atomically replace files on Windows.
334 char bakfile[PATH_MAX] = "";
335 snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
337 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
338 rename(bakfile, filename);
341 if(rename(tmpfile, filename)) {
343 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
348 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
355 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
357 char directory[PATH_MAX] = ".";
363 /* Check stdin and stdout */
365 /* Ask for a file and/or directory name. */
366 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
368 if(fgets(buf, sizeof(buf), stdin) == NULL) {
369 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
373 size_t len = strlen(buf);
386 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
389 if(filename[0] != '/') {
391 /* The directory is a relative path or a filename. */
392 getcwd(directory, sizeof(directory));
394 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
395 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
407 disable_old_keys(filename, what);
409 /* Open it first to keep the inode busy */
411 r = fopenmask(filename, mode, perms);
414 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
422 Generate a public/private Ed25519 key pair, and ask for a file to store
425 static bool ed25519_keygen(bool ask) {
428 char fname[PATH_MAX];
430 fprintf(stderr, "Generating Ed25519 key pair:\n");
432 if(!(key = ecdsa_generate())) {
433 fprintf(stderr, "Error during key generation!\n");
436 fprintf(stderr, "Done.\n");
439 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
440 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
446 if(!ecdsa_write_pem_private_key(key, f)) {
447 fprintf(stderr, "Error writing private key!\n");
454 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
456 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
459 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
465 char *pubkey = ecdsa_get_base64_public_key(key);
466 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
484 #ifndef DISABLE_LEGACY
486 Generate a public/private RSA key pair, and ask for a file to store
489 static bool rsa_keygen(int bits, bool ask) {
492 char fname[PATH_MAX];
494 // Make sure the key size is a multiple of 8 bits.
497 // Make sure that a valid key size is used.
498 if(bits < 1024 || bits > 8192) {
499 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
501 } else if(bits < 2048) {
502 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
505 fprintf(stderr, "Generating %d bits keys:\n", bits);
507 if(!(key = rsa_generate(bits, 0x10001))) {
508 fprintf(stderr, "Error during key generation!\n");
511 fprintf(stderr, "Done.\n");
514 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
515 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
521 if(!rsa_write_pem_private_key(key, f)) {
522 fprintf(stderr, "Error writing private key!\n");
529 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
531 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
534 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
540 if(!rsa_write_pem_public_key(key, f)) {
541 fprintf(stderr, "Error writing public key!\n");
564 bool recvline(int fd, char *line, size_t len) {
565 char *newline = NULL;
571 while(!(newline = memchr(buffer, '\n', blen))) {
572 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
574 if(result == -1 && sockerrno == EINTR) {
576 } else if(result <= 0) {
583 if((size_t)(newline - buffer) >= len) {
587 len = newline - buffer;
589 memcpy(line, buffer, len);
591 memmove(buffer, newline + 1, blen - len - 1);
597 static bool recvdata(int fd, char *data, size_t len) {
599 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
601 if(result == -1 && sockerrno == EINTR) {
603 } else if(result <= 0) {
610 memcpy(data, buffer, len);
611 memmove(buffer, buffer + len, blen - len);
617 bool sendline(int fd, char *format, ...) {
618 static char buffer[4096];
623 va_start(ap, format);
624 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
625 buffer[sizeof(buffer) - 1] = 0;
628 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
636 int result = send(fd, p, blen, MSG_NOSIGNAL);
638 if(result == -1 && sockerrno == EINTR) {
640 } else if(result <= 0) {
651 static void pcap(int fd, FILE *out, uint32_t snaplen) {
652 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
660 uint32_t tz_accuracy;
667 snaplen ? snaplen : sizeof(data),
680 fwrite(&header, sizeof(header), 1, out);
685 while(recvline(fd, line, sizeof(line))) {
687 int n = sscanf(line, "%d %d %d", &code, &req, &len);
688 gettimeofday(&tv, NULL);
690 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || (size_t)len > sizeof(data)) {
694 if(!recvdata(fd, data, len)) {
698 packet.tv_sec = tv.tv_sec;
699 packet.tv_usec = tv.tv_usec;
701 packet.origlen = len;
702 fwrite(&packet, sizeof(packet), 1, out);
703 fwrite(data, len, 1, out);
708 static void logcontrol(int fd, FILE *out, int level) {
709 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
713 while(recvline(fd, line, sizeof(line))) {
715 int n = sscanf(line, "%d %d %d", &code, &req, &len);
717 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
721 if(!recvdata(fd, data, len)) {
725 fwrite(data, len, 1, out);
731 static bool stop_tincd(void) {
732 if(!connect_tincd(true)) {
736 sendline(fd, "%d %d", CONTROL, REQ_STOP);
738 while(recvline(fd, line, sizeof(line))) {
739 // wait for tincd to close the connection...
750 static bool remove_service(void) {
751 SC_HANDLE manager = NULL;
752 SC_HANDLE service = NULL;
753 SERVICE_STATUS status = {0};
754 bool success = false;
756 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
759 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
763 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
766 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
767 success = stop_tincd();
769 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
775 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
776 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
778 fprintf(stderr, "%s service stopped\n", identname);
781 if(!DeleteService(service)) {
782 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
791 CloseServiceHandle(service);
795 CloseServiceHandle(manager);
799 fprintf(stderr, "%s service removed\n", identname);
806 bool connect_tincd(bool verbose) {
811 struct timeval tv = {0, 0};
813 if(select(fd + 1, &r, NULL, NULL, &tv)) {
814 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
822 FILE *f = fopen(pidfilename, "r");
826 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
835 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
837 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
848 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
849 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
850 /* clean up the stale socket and pid file */
852 unlink(unixsocketname);
856 struct sockaddr_un sa;
858 sa.sun_family = AF_UNIX;
860 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
862 sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
864 fd = socket(AF_UNIX, SOCK_STREAM, 0);
868 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
874 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
876 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
885 struct addrinfo hints = {
886 .ai_family = AF_UNSPEC,
887 .ai_socktype = SOCK_STREAM,
888 .ai_protocol = IPPROTO_TCP,
892 struct addrinfo *res = NULL;
894 if(getaddrinfo(host, port, &hints, &res) || !res) {
896 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
902 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
906 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
912 unsigned long arg = 0;
914 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
916 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
920 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
922 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
934 static const int one = 1;
935 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
938 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
943 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
945 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
953 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
955 fprintf(stderr, "Could not fully establish control socket connection\n");
967 static int cmd_start(int argc, char *argv[]) {
968 if(connect_tincd(false)) {
970 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
972 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
979 char *slash = strrchr(program_name, '/');
983 if((c = strrchr(program_name, '\\')) > slash) {
990 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
996 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
1001 Windows has no real concept of an "argv array". A command line is just one string.
1002 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
1003 it uses quotes to handle spaces in arguments.
1004 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
1005 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
1006 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
1008 xasprintf(&arg0, "\"%s\"", arg0);
1010 nargv[nargc++] = arg0;
1012 for(int i = 1; i < optind; i++) {
1013 nargv[nargc++] = orig_argv[i];
1016 for(int i = 1; i < argc; i++) {
1017 nargv[nargc++] = argv[i];
1021 int status = spawnvp(_P_WAIT, c, nargv);
1024 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
1030 int pfd[2] = {-1, -1};
1032 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
1033 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
1041 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1049 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1050 setenv("TINC_UMBILICAL", buf, true);
1051 exit(execvp(c, nargv));
1058 int status = -1, result;
1060 signal(SIGINT, SIG_IGN);
1063 // Pass all log messages from the umbilical to stderr.
1064 // A nul-byte right before closure means tincd started successfully.
1065 bool failure = true;
1069 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1070 failure = buf[len - 1];
1085 // Make sure the child process is really gone.
1086 result = waitpid(pid, &status, 0);
1089 signal(SIGINT, SIG_DFL);
1092 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1093 fprintf(stderr, "Error starting %s\n", c);
1101 static int cmd_stop(int argc, char *argv[]) {
1105 fprintf(stderr, "Too many arguments!\n");
1110 return remove_service();
1115 if(kill(pid, SIGTERM)) {
1116 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1120 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1121 waitpid(pid, NULL, 0);
1132 static int cmd_restart(int argc, char *argv[]) {
1134 return cmd_start(argc, argv);
1137 static int cmd_reload(int argc, char *argv[]) {
1141 fprintf(stderr, "Too many arguments!\n");
1145 if(!connect_tincd(true)) {
1149 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1151 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1152 fprintf(stderr, "Could not reload configuration.\n");
1160 static int dump_invitations(void) {
1161 char dname[PATH_MAX];
1162 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1163 DIR *dir = opendir(dname);
1166 if(errno == ENOENT) {
1167 fprintf(stderr, "No outstanding invitations.\n");
1171 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1179 while((ent = readdir(dir))) {
1180 char buf[MAX_STRING_SIZE];
1182 if(b64decode(ent->d_name, buf, 24) != 18) {
1186 char fname[PATH_MAX];
1188 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1189 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1193 FILE *f = fopen(fname, "r");
1196 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1202 if(!fgets(buf, sizeof(buf), f)) {
1203 fprintf(stderr, "Invalid invitation file %s\n", fname);
1210 char *eol = buf + strlen(buf);
1212 while(strchr("\t \r\n", *--eol)) {
1216 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1217 fprintf(stderr, "Invalid invitation file %s\n", fname);
1222 printf("%s %s\n", ent->d_name, buf + 7);
1228 fprintf(stderr, "No outstanding invitations.\n");
1234 static int cmd_dump(int argc, char *argv[]) {
1235 bool only_reachable = false;
1237 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1238 if(strcasecmp(argv[2], "nodes")) {
1239 fprintf(stderr, "`reachable' only supported for nodes.\n");
1244 only_reachable = true;
1250 fprintf(stderr, "Invalid number of arguments.\n");
1255 if(!strcasecmp(argv[1], "invitations")) {
1256 return dump_invitations();
1259 if(!connect_tincd(true)) {
1265 if(!strcasecmp(argv[1], "nodes")) {
1266 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1267 } else if(!strcasecmp(argv[1], "edges")) {
1268 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1269 } else if(!strcasecmp(argv[1], "subnets")) {
1270 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1271 } else if(!strcasecmp(argv[1], "connections")) {
1272 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1273 } else if(!strcasecmp(argv[1], "graph")) {
1274 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1275 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1277 } else if(!strcasecmp(argv[1], "digraph")) {
1278 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1279 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1282 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1288 printf("graph {\n");
1289 } else if(do_graph == 2) {
1290 printf("digraph {\n");
1293 while(recvline(fd, line, sizeof(line))) {
1294 char node1[4096], node2[4096];
1295 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1298 if(do_graph && req == REQ_DUMP_NODES) {
1320 char local_host[4096];
1321 char local_port[4096];
1324 int cipher, digest, maclength, compression, distance, socket, weight;
1325 short int pmtu, minmtu, maxmtu;
1326 unsigned int options, status_int;
1327 node_status_t status;
1328 long int last_state_change;
1330 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1333 case REQ_DUMP_NODES: {
1334 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);
1337 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1341 memcpy(&status, &status_int, sizeof(status));
1344 const char *color = "black";
1346 if(!strcmp(host, "MYSELF")) {
1348 } else if(!status.reachable) {
1350 } else if(strcmp(via, node)) {
1352 } else if(!status.validkey) {
1354 } else if(minmtu > 0) {
1358 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1360 if(only_reachable && !status.reachable) {
1364 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,
1365 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);
1367 if(udp_ping_rtt != -1) {
1368 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1376 case REQ_DUMP_EDGES: {
1377 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);
1380 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1385 float w = 1 + 65536.0 / weight;
1387 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1388 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1389 } else if(do_graph == 2) {
1390 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1393 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);
1398 case REQ_DUMP_SUBNETS: {
1399 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1402 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1406 printf("%s owner %s\n", strip_weight(subnet), node);
1410 case REQ_DUMP_CONNECTIONS: {
1411 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1414 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1418 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1423 fprintf(stderr, "Unable to parse dump from tincd.\n");
1428 fprintf(stderr, "Error receiving dump.\n");
1432 static int cmd_purge(int argc, char *argv[]) {
1436 fprintf(stderr, "Too many arguments!\n");
1440 if(!connect_tincd(true)) {
1444 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1446 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1447 fprintf(stderr, "Could not purge old information.\n");
1454 static int cmd_debug(int argc, char *argv[]) {
1456 fprintf(stderr, "Invalid number of arguments.\n");
1460 if(!connect_tincd(true)) {
1464 int debuglevel = atoi(argv[1]);
1467 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1469 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1470 fprintf(stderr, "Could not set debug level.\n");
1474 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1478 static int cmd_retry(int argc, char *argv[]) {
1482 fprintf(stderr, "Too many arguments!\n");
1486 if(!connect_tincd(true)) {
1490 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1492 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1493 fprintf(stderr, "Could not retry outgoing connections.\n");
1500 static int cmd_connect(int argc, char *argv[]) {
1502 fprintf(stderr, "Invalid number of arguments.\n");
1506 if(!check_id(argv[1])) {
1507 fprintf(stderr, "Invalid name for node.\n");
1511 if(!connect_tincd(true)) {
1515 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1517 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1518 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1525 static int cmd_disconnect(int argc, char *argv[]) {
1527 fprintf(stderr, "Invalid number of arguments.\n");
1531 if(!check_id(argv[1])) {
1532 fprintf(stderr, "Invalid name for node.\n");
1536 if(!connect_tincd(true)) {
1540 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1542 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1543 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1550 static int cmd_top(int argc, char *argv[]) {
1554 fprintf(stderr, "Too many arguments!\n");
1560 if(!connect_tincd(true)) {
1567 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1572 static int cmd_pcap(int argc, char *argv[]) {
1574 fprintf(stderr, "Too many arguments!\n");
1578 if(!connect_tincd(true)) {
1582 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1587 static void sigint_handler(int sig) {
1590 fprintf(stderr, "\n");
1591 shutdown(fd, SHUT_RDWR);
1595 static int cmd_log(int argc, char *argv[]) {
1597 fprintf(stderr, "Too many arguments!\n");
1601 if(!connect_tincd(true)) {
1606 signal(SIGINT, sigint_handler);
1609 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1612 signal(SIGINT, SIG_DFL);
1620 static int cmd_pid(int argc, char *argv[]) {
1624 fprintf(stderr, "Too many arguments!\n");
1628 if(!connect_tincd(true) || !pid) {
1632 printf("%d\n", pid);
1636 int rstrip(char *value) {
1637 int len = strlen(value);
1639 while(len && strchr("\t\r\n ", value[len - 1])) {
1646 char *get_my_name(bool verbose) {
1647 FILE *f = fopen(tinc_conf, "r");
1651 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1660 while(fgets(buf, sizeof(buf), f)) {
1661 int len = strcspn(buf, "\t =");
1663 value += strspn(value, "\t ");
1667 value += strspn(value, "\t ");
1670 if(!rstrip(value)) {
1676 if(strcasecmp(buf, "Name")) {
1682 return replace_name(value);
1689 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1695 ecdsa_t *get_pubkey(FILE *f) {
1699 while(fgets(buf, sizeof(buf), f)) {
1700 int len = strcspn(buf, "\t =");
1702 value += strspn(value, "\t ");
1706 value += strspn(value, "\t ");
1709 if(!rstrip(value)) {
1715 if(strcasecmp(buf, "Ed25519PublicKey")) {
1720 return ecdsa_set_base64_public_key(value);
1727 const var_t variables[] = {
1728 /* Server configuration */
1729 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1730 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1731 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1732 {"BindToInterface", VAR_SERVER},
1733 {"Broadcast", VAR_SERVER | VAR_SAFE},
1734 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1735 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1736 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1737 {"Device", VAR_SERVER},
1738 {"DeviceStandby", VAR_SERVER},
1739 {"DeviceType", VAR_SERVER},
1740 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1741 {"Ed25519PrivateKeyFile", VAR_SERVER},
1742 {"ExperimentalProtocol", VAR_SERVER},
1743 {"Forwarding", VAR_SERVER},
1744 {"FWMark", VAR_SERVER},
1745 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1746 {"Hostnames", VAR_SERVER},
1747 {"IffOneQueue", VAR_SERVER},
1748 {"Interface", VAR_SERVER},
1749 {"InvitationExpire", VAR_SERVER},
1750 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1751 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1752 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1753 {"LogLevel", VAR_SERVER},
1754 {"MACExpire", VAR_SERVER | VAR_SAFE},
1755 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1756 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1757 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1758 {"Mode", VAR_SERVER | VAR_SAFE},
1759 {"Name", VAR_SERVER},
1760 {"PingInterval", VAR_SERVER | VAR_SAFE},
1761 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1762 {"PriorityInheritance", VAR_SERVER},
1763 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1764 {"PrivateKeyFile", VAR_SERVER},
1765 {"ProcessPriority", VAR_SERVER},
1766 {"Proxy", VAR_SERVER},
1767 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1768 {"ScriptsExtension", VAR_SERVER},
1769 {"ScriptsInterpreter", VAR_SERVER},
1770 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1771 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1772 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1773 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1774 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1775 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1776 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1777 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1778 {"UDPRcvBuf", VAR_SERVER},
1779 {"UDPSndBuf", VAR_SERVER},
1780 {"UPnP", VAR_SERVER},
1781 {"UPnPDiscoverWait", VAR_SERVER},
1782 {"UPnPRefreshPeriod", VAR_SERVER},
1783 {"VDEGroup", VAR_SERVER},
1784 {"VDEPort", VAR_SERVER},
1785 /* Host configuration */
1786 {"Address", VAR_HOST | VAR_MULTIPLE},
1787 {"Cipher", VAR_SERVER | VAR_HOST},
1788 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1789 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1790 {"Digest", VAR_SERVER | VAR_HOST},
1791 {"Ed25519PublicKey", VAR_HOST},
1792 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1793 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1794 {"MACLength", VAR_SERVER | VAR_HOST},
1795 {"PMTU", VAR_SERVER | VAR_HOST},
1796 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1798 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1799 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1800 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1801 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1802 {"Weight", VAR_HOST | VAR_SAFE},
1806 static int cmd_config(int argc, char *argv[]) {
1808 fprintf(stderr, "Invalid number of arguments.\n");
1812 if(strcasecmp(argv[0], "config")) {
1818 if(!strcasecmp(argv[1], "get")) {
1820 } else if(!strcasecmp(argv[1], "add")) {
1821 argv++, argc--, action = 1;
1822 } else if(!strcasecmp(argv[1], "del")) {
1823 argv++, argc--, action = -1;
1824 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1825 argv++, argc--, action = 0;
1829 fprintf(stderr, "Invalid number of arguments.\n");
1833 // Concatenate the rest of the command line
1834 strncpy(line, argv[1], sizeof(line) - 1);
1836 for(int i = 2; i < argc; i++) {
1837 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1838 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1841 // Liberal parsing into node name, variable name and value.
1847 len = strcspn(line, "\t =");
1849 value += strspn(value, "\t ");
1853 value += strspn(value, "\t ");
1857 variable = strchr(line, '.');
1867 fprintf(stderr, "No variable given.\n");
1871 if(action >= 0 && !*value) {
1872 fprintf(stderr, "No value for variable given.\n");
1876 if(action < -1 && *value) {
1880 /* Some simple checks. */
1882 bool warnonremove = false;
1884 for(int i = 0; variables[i].name; i++) {
1885 if(strcasecmp(variables[i].name, variable)) {
1890 variable = (char *)variables[i].name;
1892 if(!strcasecmp(variable, "Subnet")) {
1895 if(!str2net(&s, value)) {
1896 fprintf(stderr, "Malformed subnet definition %s\n", value);
1899 if(!subnetcheck(s)) {
1900 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1905 /* Discourage use of obsolete variables. */
1907 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1909 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1911 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1916 /* Don't put server variables in host config files */
1918 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1920 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1922 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1927 /* Should this go into our own host config file? */
1929 if(!node && !(variables[i].type & VAR_SERVER)) {
1930 node = get_my_name(true);
1937 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1938 Turn on warnings when it seems variables might be removed unintentionally. */
1940 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1941 warnonremove = true;
1943 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1944 warnonremove = true;
1950 if(node && !check_id(node)) {
1951 fprintf(stderr, "Invalid name for node.\n");
1956 if(force || action < 0) {
1957 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1959 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1964 // Open the right configuration file.
1965 char filename[PATH_MAX];
1968 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1970 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1973 FILE *f = fopen(filename, "r");
1976 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1980 char tmpfile[PATH_MAX];
1984 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1985 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1989 tf = fopen(tmpfile, "w");
1992 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1998 // Copy the file, making modifications on the fly, unless we are just getting a value.
2002 bool removed = false;
2005 while(fgets(buf1, sizeof(buf1), f)) {
2006 buf1[sizeof(buf1) - 1] = 0;
2007 strncpy(buf2, buf1, sizeof(buf2));
2009 // Parse line in a simple way
2013 len = strcspn(buf2, "\t =");
2014 bvalue = buf2 + len;
2015 bvalue += strspn(bvalue, "\t ");
2017 if(*bvalue == '=') {
2019 bvalue += strspn(bvalue, "\t ");
2026 if(!strcasecmp(buf2, variable)) {
2030 printf("%s\n", bvalue);
2032 } else if(action == -1) {
2033 if(!*value || !strcasecmp(bvalue, value)) {
2039 } else if(action == 0) {
2040 // Warn if "set" was used for variables that can occur multiple times
2041 if(warnonremove && strcasecmp(bvalue, value)) {
2042 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2045 // Already set? Delete the rest...
2050 // Otherwise, replace.
2051 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2052 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2059 } else if(action > 0) {
2060 // Check if we've already seen this variable with the same value
2061 if(!strcasecmp(bvalue, value)) {
2068 // Copy original line...
2069 if(fputs(buf1, tf) < 0) {
2070 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2074 // Add newline if it is missing...
2075 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2076 if(fputc('\n', tf) < 0) {
2077 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2084 // Make sure we read everything...
2085 if(ferror(f) || !feof(f)) {
2086 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2091 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2095 // Add new variable if necessary.
2096 if((action > 0 && !found) || (action == 0 && !set)) {
2097 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2098 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2107 fprintf(stderr, "No matching configuration variables found.\n");
2112 // Make sure we wrote everything...
2114 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2118 // Could we find what we had to remove?
2119 if(action < 0 && !removed) {
2121 fprintf(stderr, "No configuration variables deleted.\n");
2125 // Replace the configuration file with the new one
2128 if(remove(filename)) {
2129 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2135 if(rename(tmpfile, filename)) {
2136 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2140 // Silently try notifying a running tincd of changes.
2141 if(connect_tincd(false)) {
2142 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2148 static bool try_bind(int port) {
2149 struct addrinfo *ai = NULL, *aip;
2150 struct addrinfo hint = {
2151 .ai_flags = AI_PASSIVE,
2152 .ai_family = AF_UNSPEC,
2153 .ai_socktype = SOCK_STREAM,
2154 .ai_protocol = IPPROTO_TCP,
2157 bool success = true;
2159 snprintf(portstr, sizeof(portstr), "%d", port);
2161 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2165 for(aip = ai; aip; aip = aip->ai_next) {
2166 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2173 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2186 int check_port(const char *name) {
2191 fprintf(stderr, "Warning: could not bind to port 655. ");
2193 for(int i = 0; i < 100; i++) {
2194 int port = 0x1000 + (rand() & 0x7fff);
2196 if(try_bind(port)) {
2197 char filename[PATH_MAX];
2198 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2199 FILE *f = fopen(filename, "a");
2202 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2203 fprintf(stderr, "Please change tinc's Port manually.\n");
2207 fprintf(f, "Port = %d\n", port);
2209 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2214 fprintf(stderr, "Please change tinc's Port manually.\n");
2218 static int cmd_init(int argc, char *argv[]) {
2219 if(!access(tinc_conf, F_OK)) {
2220 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2225 fprintf(stderr, "Too many arguments!\n");
2227 } else if(argc < 2) {
2230 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2232 if(!fgets(buf, sizeof(buf), stdin)) {
2233 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2237 int len = rstrip(buf);
2240 fprintf(stderr, "No name given!\n");
2246 fprintf(stderr, "No Name given!\n");
2250 name = strdup(argv[1]);
2253 fprintf(stderr, "No Name given!\n");
2258 if(!check_id(name)) {
2259 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2263 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2264 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2268 if(mkdir(confbase, 0777) && errno != EEXIST) {
2269 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2273 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2274 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2278 FILE *f = fopen(tinc_conf, "w");
2281 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2285 fprintf(f, "Name = %s\n", name);
2288 #ifndef DISABLE_LEGACY
2290 if(!rsa_keygen(2048, false)) {
2296 if(!ed25519_keygen(false)) {
2303 char filename[PATH_MAX];
2304 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2306 if(access(filename, F_OK)) {
2307 FILE *f = fopenmask(filename, "w", 0777);
2310 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2314 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");
2324 static int cmd_generate_keys(int argc, char *argv[]) {
2325 #ifdef DISABLE_LEGACY
2333 fprintf(stderr, "Too many arguments!\n");
2338 name = get_my_name(false);
2341 #ifndef DISABLE_LEGACY
2343 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2349 if(!ed25519_keygen(true)) {
2356 #ifndef DISABLE_LEGACY
2357 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2359 fprintf(stderr, "Too many arguments!\n");
2364 name = get_my_name(false);
2367 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2371 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2375 fprintf(stderr, "Too many arguments!\n");
2380 name = get_my_name(false);
2383 return !ed25519_keygen(true);
2386 static int cmd_help(int argc, char *argv[]) {
2394 static int cmd_version(int argc, char *argv[]) {
2398 fprintf(stderr, "Too many arguments!\n");
2406 static int cmd_info(int argc, char *argv[]) {
2408 fprintf(stderr, "Invalid number of arguments.\n");
2412 if(!connect_tincd(true)) {
2416 return info(fd, argv[1]);
2419 static const char *conffiles[] = {
2430 static int cmd_edit(int argc, char *argv[]) {
2432 fprintf(stderr, "Invalid number of arguments.\n");
2436 char filename[PATH_MAX] = "";
2438 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2439 for(int i = 0; conffiles[i]; i++) {
2440 if(!strcmp(argv[1], conffiles[i])) {
2441 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2450 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2451 char *dash = strchr(argv[1], '-');
2456 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2457 fprintf(stderr, "Invalid configuration filename.\n");
2465 const char *editor = getenv("VISUAL");
2468 editor = getenv("EDITOR");
2475 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2477 xasprintf(&command, "edit \"%s\"", filename);
2479 int result = system(command);
2486 // Silently try notifying a running tincd of changes.
2487 if(connect_tincd(false)) {
2488 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2494 static int export(const char *name, FILE *out) {
2495 char filename[PATH_MAX];
2496 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2497 FILE *in = fopen(filename, "r");
2500 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2504 fprintf(out, "Name = %s\n", name);
2507 while(fgets(buf, sizeof(buf), in)) {
2508 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2514 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2523 static int cmd_export(int argc, char *argv[]) {
2527 fprintf(stderr, "Too many arguments!\n");
2531 char *name = get_my_name(true);
2537 int result = export(name, stdout);
2547 static int cmd_export_all(int argc, char *argv[]) {
2551 fprintf(stderr, "Too many arguments!\n");
2555 DIR *dir = opendir(hosts_dir);
2558 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2566 while((ent = readdir(dir))) {
2567 if(!check_id(ent->d_name)) {
2574 printf("#---------------------------------------------------------------#\n");
2577 result |= export(ent->d_name, stdout);
2589 static int cmd_import(int argc, char *argv[]) {
2593 fprintf(stderr, "Too many arguments!\n");
2602 char filename[PATH_MAX] = "";
2604 bool firstline = true;
2606 while(fgets(buf, sizeof(buf), in)) {
2607 if(sscanf(buf, "Name = %4095s", name) == 1) {
2610 if(!check_id(name)) {
2611 fprintf(stderr, "Invalid Name in input!\n");
2619 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2620 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2624 if(!force && !access(filename, F_OK)) {
2625 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2630 out = fopen(filename, "w");
2633 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2639 } else if(firstline) {
2640 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2645 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2650 if(fputs(buf, out) < 0) {
2651 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2662 fprintf(stderr, "Imported %d host configuration files.\n", count);
2665 fprintf(stderr, "No host configuration files imported.\n");
2670 static int cmd_exchange(int argc, char *argv[]) {
2671 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2674 static int cmd_exchange_all(int argc, char *argv[]) {
2675 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2678 static int switch_network(char *name) {
2679 if(strcmp(name, ".")) {
2680 if(!check_netname(name, false)) {
2681 fprintf(stderr, "Invalid character in netname!\n");
2685 if(!check_netname(name, true)) {
2686 fprintf(stderr, "Warning: unsafe character in netname!\n");
2696 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2703 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2704 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2705 xasprintf(&prompt, "%s> ", identname);
2710 static int cmd_network(int argc, char *argv[]) {
2712 fprintf(stderr, "Too many arguments!\n");
2717 return switch_network(argv[1]);
2720 DIR *dir = opendir(confdir);
2723 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2729 while((ent = readdir(dir))) {
2730 if(*ent->d_name == '.') {
2734 if(!strcmp(ent->d_name, "tinc.conf")) {
2739 char fname[PATH_MAX];
2740 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2742 if(!access(fname, R_OK)) {
2743 printf("%s\n", ent->d_name);
2752 static int cmd_fsck(int argc, char *argv[]) {
2756 fprintf(stderr, "Too many arguments!\n");
2760 return fsck(orig_argv[0]);
2763 static void *readfile(FILE *in, size_t *len) {
2765 size_t bufsize = 4096;
2766 char *buf = xmalloc(bufsize);
2769 size_t read = fread(buf + count, 1, bufsize - count, in);
2777 if(count >= bufsize) {
2779 buf = xrealloc(buf, bufsize);
2790 static int cmd_sign(int argc, char *argv[]) {
2792 fprintf(stderr, "Too many arguments!\n");
2797 name = get_my_name(true);
2804 char fname[PATH_MAX];
2805 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2806 FILE *fp = fopen(fname, "r");
2809 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2813 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2816 fprintf(stderr, "Could not read private key from %s\n", fname);
2826 in = fopen(argv[1], "rb");
2829 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2838 char *data = readfile(in, &len);
2845 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2850 // Ensure we sign our name and current time as well
2851 long t = time(NULL);
2853 xasprintf(&trailer, " %s %ld", name, t);
2854 int trailer_len = strlen(trailer);
2856 data = xrealloc(data, len + trailer_len);
2857 memcpy(data + len, trailer, trailer_len);
2862 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2863 fprintf(stderr, "Error generating signature\n");
2869 b64encode(sig, sig, 64);
2872 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2873 fwrite(data, len, 1, stdout);
2879 static int cmd_verify(int argc, char *argv[]) {
2881 fprintf(stderr, "Not enough arguments!\n");
2886 fprintf(stderr, "Too many arguments!\n");
2890 char *node = argv[1];
2892 if(!strcmp(node, ".")) {
2894 name = get_my_name(true);
2902 } else if(!strcmp(node, "*")) {
2905 if(!check_id(node)) {
2906 fprintf(stderr, "Invalid node name\n");
2914 in = fopen(argv[2], "rb");
2917 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2925 char *data = readfile(in, &len);
2932 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2936 char *newline = memchr(data, '\n', len);
2938 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2939 fprintf(stderr, "Invalid input\n");
2945 size_t skip = newline - data;
2947 char signer[MAX_STRING_SIZE] = "";
2948 char sig[MAX_STRING_SIZE] = "";
2951 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2952 fprintf(stderr, "Invalid input\n");
2957 if(node && strcmp(node, signer)) {
2958 fprintf(stderr, "Signature is not made by %s\n", node);
2968 xasprintf(&trailer, " %s %ld", signer, t);
2969 int trailer_len = strlen(trailer);
2971 data = xrealloc(data, len + trailer_len);
2972 memcpy(data + len, trailer, trailer_len);
2975 newline = data + skip;
2977 char fname[PATH_MAX];
2978 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2979 FILE *fp = fopen(fname, "r");
2982 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2987 ecdsa_t *key = get_pubkey(fp);
2991 key = ecdsa_read_pem_public_key(fp);
2995 fprintf(stderr, "Could not read public key from %s\n", fname);
3003 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
3004 fprintf(stderr, "Invalid signature\n");
3012 fwrite(newline, len - (newline - data), 1, stdout);
3018 static const struct {
3019 const char *command;
3020 int (*function)(int argc, char *argv[]);
3023 {"start", cmd_start, false},
3024 {"stop", cmd_stop, false},
3025 {"restart", cmd_restart, false},
3026 {"reload", cmd_reload, false},
3027 {"dump", cmd_dump, false},
3028 {"list", cmd_dump, false},
3029 {"purge", cmd_purge, false},
3030 {"debug", cmd_debug, false},
3031 {"retry", cmd_retry, false},
3032 {"connect", cmd_connect, false},
3033 {"disconnect", cmd_disconnect, false},
3034 {"top", cmd_top, false},
3035 {"pcap", cmd_pcap, false},
3036 {"log", cmd_log, false},
3037 {"pid", cmd_pid, false},
3038 {"config", cmd_config, true},
3039 {"add", cmd_config, false},
3040 {"del", cmd_config, false},
3041 {"get", cmd_config, false},
3042 {"set", cmd_config, false},
3043 {"init", cmd_init, false},
3044 {"generate-keys", cmd_generate_keys, false},
3045 #ifndef DISABLE_LEGACY
3046 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3048 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3049 {"help", cmd_help, false},
3050 {"version", cmd_version, false},
3051 {"info", cmd_info, false},
3052 {"edit", cmd_edit, false},
3053 {"export", cmd_export, false},
3054 {"export-all", cmd_export_all, false},
3055 {"import", cmd_import, false},
3056 {"exchange", cmd_exchange, false},
3057 {"exchange-all", cmd_exchange_all, false},
3058 {"invite", cmd_invite, false},
3059 {"join", cmd_join, false},
3060 {"network", cmd_network, false},
3061 {"fsck", cmd_fsck, false},
3062 {"sign", cmd_sign, false},
3063 {"verify", cmd_verify, false},
3064 {NULL, NULL, false},
3067 #ifdef HAVE_READLINE
3068 static char *complete_command(const char *text, int state) {
3077 while(commands[i].command) {
3078 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3079 return xstrdup(commands[i].command);
3088 static char *complete_dump(const char *text, int state) {
3089 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3099 if(!strncasecmp(matches[i], text, strlen(text))) {
3100 return xstrdup(matches[i]);
3109 static char *complete_config(const char *text, int state) {
3118 while(variables[i].name) {
3119 char *dot = strchr(text, '.');
3122 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3124 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3128 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3129 return xstrdup(variables[i].name);
3139 static char *complete_info(const char *text, int state) {
3145 if(!connect_tincd(false)) {
3149 // Check the list of nodes
3150 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3151 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3154 while(recvline(fd, line, sizeof(line))) {
3156 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3169 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3173 if(!strncmp(item, text, strlen(text))) {
3174 return xstrdup(strip_weight(item));
3181 static char *complete_nothing(const char *text, int state) {
3187 static char **completion(const char *text, int start, int end) {
3189 char **matches = NULL;
3192 matches = rl_completion_matches(text, complete_command);
3193 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3194 matches = rl_completion_matches(text, complete_dump);
3195 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3196 matches = rl_completion_matches(text, complete_config);
3197 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3198 matches = rl_completion_matches(text, complete_config);
3199 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3200 matches = rl_completion_matches(text, complete_config);
3201 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3202 matches = rl_completion_matches(text, complete_config);
3203 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3204 matches = rl_completion_matches(text, complete_info);
3211 static int cmd_shell(int argc, char *argv[]) {
3212 xasprintf(&prompt, "%s> ", identname);
3216 int maxargs = argc + 16;
3217 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3219 for(int i = 0; i < argc; i++) {
3223 #ifdef HAVE_READLINE
3224 rl_readline_name = "tinc";
3225 rl_completion_entry_function = complete_nothing;
3226 rl_attempted_completion_function = completion;
3227 rl_filename_completion_desired = 0;
3232 #ifdef HAVE_READLINE
3237 rl_basic_word_break_characters = "\t\n ";
3238 line = readline(prompt);
3239 copy = line ? xstrdup(line) : NULL;
3241 line = fgets(buf, sizeof(buf), stdin);
3247 fputs(prompt, stdout);
3250 line = fgets(buf, sizeof(buf), stdin);
3257 /* Ignore comments */
3266 char *p = line + strspn(line, " \t\n");
3267 char *next = strtok(p, " \t\n");
3270 if(nargc >= maxargs) {
3272 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3277 next = strtok(NULL, " \t\n");
3284 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3285 #ifdef HAVE_READLINE
3294 for(int i = 0; commands[i].command; i++) {
3295 if(!strcasecmp(nargv[argc], commands[i].command)) {
3296 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3302 #ifdef HAVE_READLINE
3311 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3316 #ifdef HAVE_READLINE
3329 int main(int argc, char *argv[]) {
3330 program_name = argv[0];
3333 tty = isatty(0) && isatty(1);
3335 if(!parse_options(argc, argv)) {
3340 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3341 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3354 static struct WSAData wsa_state;
3356 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3357 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3366 if(optind >= argc) {
3367 return cmd_shell(argc, argv);
3370 for(int i = 0; commands[i].command; i++) {
3371 if(!strcasecmp(argv[optind], commands[i].command)) {
3372 return commands[i].function(argc - optind, argv + optind);
3376 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);