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"
45 #define MSG_NOSIGNAL 0
48 static char **orig_argv;
51 /* If nonzero, display usage information and exit. */
52 static bool show_help = false;
54 /* If nonzero, print the version on standard output and exit. */
55 static bool show_version = false;
57 static char *name = NULL;
58 static char controlcookie[1025];
59 char *tinc_conf = NULL;
60 char *hosts_dir = NULL;
63 // Horrible global variables...
72 bool confbasegiven = false;
73 bool netnamegiven = false;
74 char *scriptinterpreter = NULL;
75 char *scriptextension = "";
81 static struct option const long_options[] = {
82 {"batch", no_argument, NULL, 'b'},
83 {"config", required_argument, NULL, 'c'},
84 {"net", required_argument, NULL, 'n'},
85 {"help", no_argument, NULL, 1},
86 {"version", no_argument, NULL, 2},
87 {"pidfile", required_argument, NULL, 3},
88 {"force", no_argument, NULL, 4},
92 static void version(void) {
93 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
94 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
95 printf("Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
96 "See the AUTHORS file for a complete list.\n\n"
97 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
98 "and you are welcome to redistribute it under certain conditions;\n"
99 "see the file COPYING for details.\n");
102 static void usage(bool status) {
104 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
106 printf("Usage: %s [options] command\n\n", program_name);
107 printf("Valid options are:\n"
108 " -b, --batch Don't ask for anything (non-interactive mode).\n"
109 " -c, --config=DIR Read configuration options from DIR.\n"
110 " -n, --net=NETNAME Connect to net NETNAME.\n"
111 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
112 " --force Force some commands to work despite warnings.\n"
113 " --help Display this help and exit.\n"
114 " --version Output version information and exit.\n"
116 "Valid commands are:\n"
117 " init [name] Create initial configuration files.\n"
118 " get VARIABLE Print current value of VARIABLE\n"
119 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
120 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
121 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
122 " start [tincd options] Start tincd.\n"
123 " stop Stop tincd.\n"
124 " restart [tincd options] Restart tincd.\n"
125 " reload Partially reload configuration of running tincd.\n"
126 " pid Show PID of currently running tincd.\n"
127 #ifdef DISABLE_LEGACY
128 " generate-keys Generate a new Ed25519 public/private keypair.\n"
130 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
131 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
133 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
134 " dump Dump a list of one of the following things:\n"
135 " [reachable] nodes - all known nodes in the VPN\n"
136 " edges - all known connections in the VPN\n"
137 " subnets - all known subnets in the VPN\n"
138 " connections - all meta connections with ourself\n"
139 " [di]graph - graph of the VPN in dotty format\n"
140 " invitations - outstanding invitations\n"
141 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
142 " purge Purge unreachable nodes\n"
143 " debug N Set debug level\n"
144 " retry Retry all outgoing connections\n"
145 " disconnect NODE Close meta connection with NODE\n"
147 " top Show real-time statistics\n"
149 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
150 " log [level] Dump log output [up to the specified level]\n"
151 " export Export host configuration of local node to standard output\n"
152 " export-all Export all host configuration files to standard output\n"
153 " import Import host configuration file(s) from standard input\n"
154 " exchange Same as export followed by import\n"
155 " exchange-all Same as export-all followed by import\n"
156 " invite NODE [...] Generate an invitation for NODE\n"
157 " join INVITATION Join a VPN using an INVITATION\n"
158 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
159 " fsck Check the configuration files for problems.\n"
160 " sign [FILE] Generate a signed version of a file.\n"
161 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
163 printf("Report bugs to tinc@tinc-vpn.org.\n");
167 static bool parse_options(int argc, char **argv) {
169 int option_index = 0;
171 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
173 case 0: /* long option */
180 case 'c': /* config file */
181 confbase = xstrdup(optarg);
182 confbasegiven = true;
185 case 'n': /* net name given */
186 netname = xstrdup(optarg);
189 case 1: /* show help */
193 case 2: /* show version */
197 case 3: /* open control socket here */
198 pidfilename = xstrdup(optarg);
205 case '?': /* wrong options */
214 if(!netname && (netname = getenv("NETNAME"))) {
215 netname = xstrdup(netname);
218 /* netname "." is special: a "top-level name" */
220 if(netname && (!*netname || !strcmp(netname, "."))) {
225 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
226 fprintf(stderr, "Invalid character in netname!\n");
233 /* Open a file with the desired permissions, minus the umask.
234 Also, if we want to create an executable file, we call fchmod()
235 to set the executable bits. */
237 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
238 mode_t mask = umask(0);
241 FILE *f = fopen(filename, mode);
244 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
250 if((perms & 0444) && f) {
251 fchmod(fileno(f), perms);
259 static void disable_old_keys(const char *filename, const char *what) {
260 char tmpfile[PATH_MAX] = "";
262 bool disabled = false;
267 r = fopen(filename, "r");
273 snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
275 struct stat st = {.st_mode = 0600};
276 fstat(fileno(r), &st);
277 w = fopenmask(tmpfile, "w", st.st_mode);
279 while(fgets(buf, sizeof(buf), r)) {
280 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
281 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
287 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
294 if(block || ed25519pubkey) {
298 if(fputs(buf, w) < 0) {
304 if(block && !strncmp(buf, "-----END ", 9)) {
314 if(ferror(r) || fclose(r) < 0) {
320 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
330 // We cannot atomically replace files on Windows.
331 char bakfile[PATH_MAX] = "";
332 snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
334 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
335 rename(bakfile, filename);
338 if(rename(tmpfile, filename)) {
340 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
345 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
352 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
354 char directory[PATH_MAX] = ".";
360 /* Check stdin and stdout */
362 /* Ask for a file and/or directory name. */
363 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
365 if(fgets(buf, sizeof(buf), stdin) == NULL) {
366 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
370 size_t len = strlen(buf);
383 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
386 if(filename[0] != '/') {
388 /* The directory is a relative path or a filename. */
389 getcwd(directory, sizeof(directory));
391 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
392 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
404 disable_old_keys(filename, what);
406 /* Open it first to keep the inode busy */
408 r = fopenmask(filename, mode, perms);
411 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
419 Generate a public/private Ed25519 keypair, and ask for a file to store
422 static bool ed25519_keygen(bool ask) {
425 char fname[PATH_MAX];
427 fprintf(stderr, "Generating Ed25519 keypair:\n");
429 if(!(key = ecdsa_generate())) {
430 fprintf(stderr, "Error during key generation!\n");
433 fprintf(stderr, "Done.\n");
436 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
437 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
443 if(!ecdsa_write_pem_private_key(key, f)) {
444 fprintf(stderr, "Error writing private key!\n");
451 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
453 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
456 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
462 char *pubkey = ecdsa_get_base64_public_key(key);
463 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
481 #ifndef DISABLE_LEGACY
483 Generate a public/private RSA keypair, and ask for a file to store
486 static bool rsa_keygen(int bits, bool ask) {
489 char fname[PATH_MAX];
491 // Make sure the key size is a multiple of 8 bits.
494 // Make sure that a valid key size is used.
495 if(bits < 1024 || bits > 8192) {
496 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
498 } else if(bits < 2048) {
499 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
502 fprintf(stderr, "Generating %d bits keys:\n", bits);
504 if(!(key = rsa_generate(bits, 0x10001))) {
505 fprintf(stderr, "Error during key generation!\n");
508 fprintf(stderr, "Done.\n");
511 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
512 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
518 if(!rsa_write_pem_private_key(key, f)) {
519 fprintf(stderr, "Error writing private key!\n");
526 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
528 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
531 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
537 if(!rsa_write_pem_public_key(key, f)) {
538 fprintf(stderr, "Error writing public key!\n");
561 bool recvline(int fd, char *line, size_t len) {
562 char *newline = NULL;
568 while(!(newline = memchr(buffer, '\n', blen))) {
569 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
571 if(result == -1 && sockerrno == EINTR) {
573 } else if(result <= 0) {
580 if((size_t)(newline - buffer) >= len) {
584 len = newline - buffer;
586 memcpy(line, buffer, len);
588 memmove(buffer, newline + 1, blen - len - 1);
594 static bool recvdata(int fd, char *data, size_t len) {
596 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
598 if(result == -1 && sockerrno == EINTR) {
600 } else if(result <= 0) {
607 memcpy(data, buffer, len);
608 memmove(buffer, buffer + len, blen - len);
614 bool sendline(int fd, char *format, ...) {
615 static char buffer[4096];
620 va_start(ap, format);
621 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
622 buffer[sizeof(buffer) - 1] = 0;
625 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
633 int result = send(fd, p, blen, MSG_NOSIGNAL);
635 if(result == -1 && sockerrno == EINTR) {
637 } else if(result <= 0) {
648 static void pcap(int fd, FILE *out, uint32_t snaplen) {
649 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
657 uint32_t tz_accuracy;
664 snaplen ? snaplen : sizeof(data),
677 fwrite(&header, sizeof(header), 1, out);
682 while(recvline(fd, line, sizeof(line))) {
684 int n = sscanf(line, "%d %d %d", &code, &req, &len);
685 gettimeofday(&tv, NULL);
687 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || (size_t)len > sizeof(data)) {
691 if(!recvdata(fd, data, len)) {
695 packet.tv_sec = tv.tv_sec;
696 packet.tv_usec = tv.tv_usec;
698 packet.origlen = len;
699 fwrite(&packet, sizeof(packet), 1, out);
700 fwrite(data, len, 1, out);
705 static void logcontrol(int fd, FILE *out, int level) {
706 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
710 while(recvline(fd, line, sizeof(line))) {
712 int n = sscanf(line, "%d %d %d", &code, &req, &len);
714 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
718 if(!recvdata(fd, data, len)) {
722 fwrite(data, len, 1, out);
728 static bool stop_tincd(void) {
729 if(!connect_tincd(true)) {
733 sendline(fd, "%d %d", CONTROL, REQ_STOP);
735 while(recvline(fd, line, sizeof(line))) {
736 // wait for tincd to close the connection...
747 static bool remove_service(void) {
748 SC_HANDLE manager = NULL;
749 SC_HANDLE service = NULL;
750 SERVICE_STATUS status = {0};
751 bool success = false;
753 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
756 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
760 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
763 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
764 success = stop_tincd();
766 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
772 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
773 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
775 fprintf(stderr, "%s service stopped\n", identname);
778 if(!DeleteService(service)) {
779 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
788 CloseServiceHandle(service);
792 CloseServiceHandle(manager);
796 fprintf(stderr, "%s service removed\n", identname);
803 bool connect_tincd(bool verbose) {
808 struct timeval tv = {0, 0};
810 if(select(fd + 1, &r, NULL, NULL, &tv)) {
811 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
819 FILE *f = fopen(pidfilename, "r");
823 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
832 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
834 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
845 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
846 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
847 /* clean up the stale socket and pid file */
849 unlink(unixsocketname);
853 struct sockaddr_un sa;
855 sa.sun_family = AF_UNIX;
857 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
859 sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
861 fd = socket(AF_UNIX, SOCK_STREAM, 0);
865 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
871 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
873 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
882 struct addrinfo hints = {
883 .ai_family = AF_UNSPEC,
884 .ai_socktype = SOCK_STREAM,
885 .ai_protocol = IPPROTO_TCP,
889 struct addrinfo *res = NULL;
891 if(getaddrinfo(host, port, &hints, &res) || !res) {
893 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
899 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
903 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
909 unsigned long arg = 0;
911 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
913 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
917 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
919 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
931 static const int one = 1;
932 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
935 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
940 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
942 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
950 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
952 fprintf(stderr, "Could not fully establish control socket connection\n");
964 static int cmd_start(int argc, char *argv[]) {
965 if(connect_tincd(false)) {
967 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
969 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
976 char *slash = strrchr(program_name, '/');
980 if((c = strrchr(program_name, '\\')) > slash) {
987 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
993 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
998 Windows has no real concept of an "argv array". A command line is just one string.
999 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
1000 it uses quotes to handle spaces in arguments.
1001 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
1002 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
1003 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
1005 xasprintf(&arg0, "\"%s\"", arg0);
1007 nargv[nargc++] = arg0;
1009 for(int i = 1; i < optind; i++) {
1010 nargv[nargc++] = orig_argv[i];
1013 for(int i = 1; i < argc; i++) {
1014 nargv[nargc++] = argv[i];
1018 int status = spawnvp(_P_WAIT, c, nargv);
1021 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
1027 int pfd[2] = {-1, -1};
1029 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
1030 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
1038 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1046 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1047 setenv("TINC_UMBILICAL", buf, true);
1048 exit(execvp(c, nargv));
1055 int status = -1, result;
1057 signal(SIGINT, SIG_IGN);
1060 // Pass all log messages from the umbilical to stderr.
1061 // A nul-byte right before closure means tincd started successfully.
1062 bool failure = true;
1066 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1067 failure = buf[len - 1];
1082 // Make sure the child process is really gone.
1083 result = waitpid(pid, &status, 0);
1086 signal(SIGINT, SIG_DFL);
1089 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1090 fprintf(stderr, "Error starting %s\n", c);
1098 static int cmd_stop(int argc, char *argv[]) {
1102 fprintf(stderr, "Too many arguments!\n");
1107 return remove_service();
1112 if(kill(pid, SIGTERM)) {
1113 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1117 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1118 waitpid(pid, NULL, 0);
1129 static int cmd_restart(int argc, char *argv[]) {
1131 return cmd_start(argc, argv);
1134 static int cmd_reload(int argc, char *argv[]) {
1138 fprintf(stderr, "Too many arguments!\n");
1142 if(!connect_tincd(true)) {
1146 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1148 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1149 fprintf(stderr, "Could not reload configuration.\n");
1157 static int dump_invitations(void) {
1158 char dname[PATH_MAX];
1159 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1160 DIR *dir = opendir(dname);
1163 if(errno == ENOENT) {
1164 fprintf(stderr, "No outstanding invitations.\n");
1168 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1176 while((ent = readdir(dir))) {
1177 char buf[MAX_STRING_SIZE];
1179 if(b64decode(ent->d_name, buf, 24) != 18) {
1183 char fname[PATH_MAX];
1185 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1186 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1190 FILE *f = fopen(fname, "r");
1193 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1199 if(!fgets(buf, sizeof(buf), f)) {
1200 fprintf(stderr, "Invalid invitation file %s\n", fname);
1207 char *eol = buf + strlen(buf);
1209 while(strchr("\t \r\n", *--eol)) {
1213 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1214 fprintf(stderr, "Invalid invitation file %s\n", fname);
1219 printf("%s %s\n", ent->d_name, buf + 7);
1225 fprintf(stderr, "No outstanding invitations.\n");
1231 static int cmd_dump(int argc, char *argv[]) {
1232 bool only_reachable = false;
1234 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1235 if(strcasecmp(argv[2], "nodes")) {
1236 fprintf(stderr, "`reachable' only supported for nodes.\n");
1241 only_reachable = true;
1247 fprintf(stderr, "Invalid number of arguments.\n");
1252 if(!strcasecmp(argv[1], "invitations")) {
1253 return dump_invitations();
1256 if(!connect_tincd(true)) {
1262 if(!strcasecmp(argv[1], "nodes")) {
1263 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1264 } else if(!strcasecmp(argv[1], "edges")) {
1265 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1266 } else if(!strcasecmp(argv[1], "subnets")) {
1267 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1268 } else if(!strcasecmp(argv[1], "connections")) {
1269 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1270 } else if(!strcasecmp(argv[1], "graph")) {
1271 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1272 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1274 } else if(!strcasecmp(argv[1], "digraph")) {
1275 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1276 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1279 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1285 printf("graph {\n");
1286 } else if(do_graph == 2) {
1287 printf("digraph {\n");
1290 while(recvline(fd, line, sizeof(line))) {
1291 char node1[4096], node2[4096];
1292 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1295 if(do_graph && req == REQ_DUMP_NODES) {
1317 char local_host[4096];
1318 char local_port[4096];
1321 int cipher, digest, maclength, compression, distance, socket, weight;
1322 short int pmtu, minmtu, maxmtu;
1323 unsigned int options, status_int;
1324 node_status_t status;
1325 long int last_state_change;
1327 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1330 case REQ_DUMP_NODES: {
1331 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);
1334 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1338 memcpy(&status, &status_int, sizeof(status));
1341 const char *color = "black";
1343 if(!strcmp(host, "MYSELF")) {
1345 } else if(!status.reachable) {
1347 } else if(strcmp(via, node)) {
1349 } else if(!status.validkey) {
1351 } else if(minmtu > 0) {
1355 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1357 if(only_reachable && !status.reachable) {
1361 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,
1362 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);
1364 if(udp_ping_rtt != -1) {
1365 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1373 case REQ_DUMP_EDGES: {
1374 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);
1377 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1382 float w = 1 + 65536.0 / weight;
1384 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1385 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1386 } else if(do_graph == 2) {
1387 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1390 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);
1395 case REQ_DUMP_SUBNETS: {
1396 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1399 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1403 printf("%s owner %s\n", strip_weight(subnet), node);
1407 case REQ_DUMP_CONNECTIONS: {
1408 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1411 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1415 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1420 fprintf(stderr, "Unable to parse dump from tincd.\n");
1425 fprintf(stderr, "Error receiving dump.\n");
1429 static int cmd_purge(int argc, char *argv[]) {
1433 fprintf(stderr, "Too many arguments!\n");
1437 if(!connect_tincd(true)) {
1441 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1443 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1444 fprintf(stderr, "Could not purge old information.\n");
1451 static int cmd_debug(int argc, char *argv[]) {
1453 fprintf(stderr, "Invalid number of arguments.\n");
1457 if(!connect_tincd(true)) {
1461 int debuglevel = atoi(argv[1]);
1464 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1466 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1467 fprintf(stderr, "Could not set debug level.\n");
1471 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1475 static int cmd_retry(int argc, char *argv[]) {
1479 fprintf(stderr, "Too many arguments!\n");
1483 if(!connect_tincd(true)) {
1487 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1489 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1490 fprintf(stderr, "Could not retry outgoing connections.\n");
1497 static int cmd_connect(int argc, char *argv[]) {
1499 fprintf(stderr, "Invalid number of arguments.\n");
1503 if(!check_id(argv[1])) {
1504 fprintf(stderr, "Invalid name for node.\n");
1508 if(!connect_tincd(true)) {
1512 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1514 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1515 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1522 static int cmd_disconnect(int argc, char *argv[]) {
1524 fprintf(stderr, "Invalid number of arguments.\n");
1528 if(!check_id(argv[1])) {
1529 fprintf(stderr, "Invalid name for node.\n");
1533 if(!connect_tincd(true)) {
1537 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1539 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1540 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1547 static int cmd_top(int argc, char *argv[]) {
1551 fprintf(stderr, "Too many arguments!\n");
1557 if(!connect_tincd(true)) {
1564 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1569 static int cmd_pcap(int argc, char *argv[]) {
1571 fprintf(stderr, "Too many arguments!\n");
1575 if(!connect_tincd(true)) {
1579 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1584 static void sigint_handler(int sig) {
1587 fprintf(stderr, "\n");
1588 shutdown(fd, SHUT_RDWR);
1592 static int cmd_log(int argc, char *argv[]) {
1594 fprintf(stderr, "Too many arguments!\n");
1598 if(!connect_tincd(true)) {
1603 signal(SIGINT, sigint_handler);
1606 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1609 signal(SIGINT, SIG_DFL);
1617 static int cmd_pid(int argc, char *argv[]) {
1621 fprintf(stderr, "Too many arguments!\n");
1625 if(!connect_tincd(true) || !pid) {
1629 printf("%d\n", pid);
1633 int rstrip(char *value) {
1634 int len = strlen(value);
1636 while(len && strchr("\t\r\n ", value[len - 1])) {
1643 char *get_my_name(bool verbose) {
1644 FILE *f = fopen(tinc_conf, "r");
1648 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1657 while(fgets(buf, sizeof(buf), f)) {
1658 int len = strcspn(buf, "\t =");
1660 value += strspn(value, "\t ");
1664 value += strspn(value, "\t ");
1667 if(!rstrip(value)) {
1673 if(strcasecmp(buf, "Name")) {
1679 return replace_name(value);
1686 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1692 ecdsa_t *get_pubkey(FILE *f) {
1696 while(fgets(buf, sizeof(buf), f)) {
1697 int len = strcspn(buf, "\t =");
1699 value += strspn(value, "\t ");
1703 value += strspn(value, "\t ");
1706 if(!rstrip(value)) {
1712 if(strcasecmp(buf, "Ed25519PublicKey")) {
1717 return ecdsa_set_base64_public_key(value);
1724 const var_t variables[] = {
1725 /* Server configuration */
1726 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1727 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1728 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1729 {"BindToInterface", VAR_SERVER},
1730 {"Broadcast", VAR_SERVER | VAR_SAFE},
1731 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1732 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1733 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1734 {"Device", VAR_SERVER},
1735 {"DeviceStandby", VAR_SERVER},
1736 {"DeviceType", VAR_SERVER},
1737 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1738 {"Ed25519PrivateKeyFile", VAR_SERVER},
1739 {"ExperimentalProtocol", VAR_SERVER},
1740 {"Forwarding", VAR_SERVER},
1741 {"FWMark", VAR_SERVER},
1742 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1743 {"Hostnames", VAR_SERVER},
1744 {"IffOneQueue", VAR_SERVER},
1745 {"Interface", VAR_SERVER},
1746 {"InvitationExpire", VAR_SERVER},
1747 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1748 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1749 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1750 {"LogLevel", VAR_SERVER},
1751 {"MACExpire", VAR_SERVER | VAR_SAFE},
1752 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1753 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1754 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1755 {"Mode", VAR_SERVER | VAR_SAFE},
1756 {"Name", VAR_SERVER},
1757 {"PingInterval", VAR_SERVER | VAR_SAFE},
1758 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1759 {"PriorityInheritance", VAR_SERVER},
1760 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1761 {"PrivateKeyFile", VAR_SERVER},
1762 {"ProcessPriority", VAR_SERVER},
1763 {"Proxy", VAR_SERVER},
1764 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1765 {"ScriptsExtension", VAR_SERVER},
1766 {"ScriptsInterpreter", VAR_SERVER},
1767 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1768 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1769 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1770 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1771 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1772 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1773 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1774 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1775 {"UDPRcvBuf", VAR_SERVER},
1776 {"UDPSndBuf", VAR_SERVER},
1777 {"UPnP", VAR_SERVER},
1778 {"UPnPDiscoverWait", VAR_SERVER},
1779 {"UPnPRefreshPeriod", VAR_SERVER},
1780 {"VDEGroup", VAR_SERVER},
1781 {"VDEPort", VAR_SERVER},
1782 /* Host configuration */
1783 {"Address", VAR_HOST | VAR_MULTIPLE},
1784 {"Cipher", VAR_SERVER | VAR_HOST},
1785 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1786 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1787 {"Digest", VAR_SERVER | VAR_HOST},
1788 {"Ed25519PublicKey", VAR_HOST},
1789 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1790 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1791 {"MACLength", VAR_SERVER | VAR_HOST},
1792 {"PMTU", VAR_SERVER | VAR_HOST},
1793 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1795 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1796 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1797 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1798 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1799 {"Weight", VAR_HOST | VAR_SAFE},
1803 static int cmd_config(int argc, char *argv[]) {
1805 fprintf(stderr, "Invalid number of arguments.\n");
1809 if(strcasecmp(argv[0], "config")) {
1815 if(!strcasecmp(argv[1], "get")) {
1817 } else if(!strcasecmp(argv[1], "add")) {
1818 argv++, argc--, action = 1;
1819 } else if(!strcasecmp(argv[1], "del")) {
1820 argv++, argc--, action = -1;
1821 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1822 argv++, argc--, action = 0;
1826 fprintf(stderr, "Invalid number of arguments.\n");
1830 // Concatenate the rest of the command line
1831 strncpy(line, argv[1], sizeof(line) - 1);
1833 for(int i = 2; i < argc; i++) {
1834 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1835 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1838 // Liberal parsing into node name, variable name and value.
1844 len = strcspn(line, "\t =");
1846 value += strspn(value, "\t ");
1850 value += strspn(value, "\t ");
1854 variable = strchr(line, '.');
1864 fprintf(stderr, "No variable given.\n");
1868 if(action >= 0 && !*value) {
1869 fprintf(stderr, "No value for variable given.\n");
1873 if(action < -1 && *value) {
1877 /* Some simple checks. */
1879 bool warnonremove = false;
1881 for(int i = 0; variables[i].name; i++) {
1882 if(strcasecmp(variables[i].name, variable)) {
1887 variable = (char *)variables[i].name;
1889 /* Discourage use of obsolete variables. */
1891 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1893 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1895 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1900 /* Don't put server variables in host config files */
1902 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1904 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1906 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1911 /* Should this go into our own host config file? */
1913 if(!node && !(variables[i].type & VAR_SERVER)) {
1914 node = get_my_name(true);
1921 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1922 Turn on warnings when it seems variables might be removed unintentionally. */
1924 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1925 warnonremove = true;
1927 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1928 warnonremove = true;
1934 if(node && !check_id(node)) {
1935 fprintf(stderr, "Invalid name for node.\n");
1940 if(force || action < 0) {
1941 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1943 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1948 // Open the right configuration file.
1949 char filename[PATH_MAX];
1952 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1954 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1957 FILE *f = fopen(filename, "r");
1960 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1964 char tmpfile[PATH_MAX];
1968 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1969 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1973 tf = fopen(tmpfile, "w");
1976 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1982 // Copy the file, making modifications on the fly, unless we are just getting a value.
1986 bool removed = false;
1989 while(fgets(buf1, sizeof(buf1), f)) {
1990 buf1[sizeof(buf1) - 1] = 0;
1991 strncpy(buf2, buf1, sizeof(buf2));
1993 // Parse line in a simple way
1997 len = strcspn(buf2, "\t =");
1998 bvalue = buf2 + len;
1999 bvalue += strspn(bvalue, "\t ");
2001 if(*bvalue == '=') {
2003 bvalue += strspn(bvalue, "\t ");
2010 if(!strcasecmp(buf2, variable)) {
2014 printf("%s\n", bvalue);
2016 } else if(action == -1) {
2017 if(!*value || !strcasecmp(bvalue, value)) {
2023 } else if(action == 0) {
2024 // Warn if "set" was used for variables that can occur multiple times
2025 if(warnonremove && strcasecmp(bvalue, value)) {
2026 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2029 // Already set? Delete the rest...
2034 // Otherwise, replace.
2035 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2036 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2043 } else if(action > 0) {
2044 // Check if we've already seen this variable with the same value
2045 if(!strcasecmp(bvalue, value)) {
2052 // Copy original line...
2053 if(fputs(buf1, tf) < 0) {
2054 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2058 // Add newline if it is missing...
2059 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2060 if(fputc('\n', tf) < 0) {
2061 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2068 // Make sure we read everything...
2069 if(ferror(f) || !feof(f)) {
2070 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2075 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2079 // Add new variable if necessary.
2080 if((action > 0 && !found) || (action == 0 && !set)) {
2081 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2082 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2091 fprintf(stderr, "No matching configuration variables found.\n");
2096 // Make sure we wrote everything...
2098 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2102 // Could we find what we had to remove?
2103 if(action < 0 && !removed) {
2105 fprintf(stderr, "No configuration variables deleted.\n");
2109 // Replace the configuration file with the new one
2112 if(remove(filename)) {
2113 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2119 if(rename(tmpfile, filename)) {
2120 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2124 // Silently try notifying a running tincd of changes.
2125 if(connect_tincd(false)) {
2126 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2132 static bool try_bind(int port) {
2133 struct addrinfo *ai = NULL, *aip;
2134 struct addrinfo hint = {
2135 .ai_flags = AI_PASSIVE,
2136 .ai_family = AF_UNSPEC,
2137 .ai_socktype = SOCK_STREAM,
2138 .ai_protocol = IPPROTO_TCP,
2141 bool success = true;
2143 snprintf(portstr, sizeof(portstr), "%d", port);
2145 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2149 for(aip = ai; aip; aip = aip->ai_next) {
2150 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2157 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2170 int check_port(const char *name) {
2175 fprintf(stderr, "Warning: could not bind to port 655. ");
2177 for(int i = 0; i < 100; i++) {
2178 int port = 0x1000 + (rand() & 0x7fff);
2180 if(try_bind(port)) {
2181 char filename[PATH_MAX];
2182 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2183 FILE *f = fopen(filename, "a");
2186 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2187 fprintf(stderr, "Please change tinc's Port manually.\n");
2191 fprintf(f, "Port = %d\n", port);
2193 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2198 fprintf(stderr, "Please change tinc's Port manually.\n");
2202 static int cmd_init(int argc, char *argv[]) {
2203 if(!access(tinc_conf, F_OK)) {
2204 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2209 fprintf(stderr, "Too many arguments!\n");
2211 } else if(argc < 2) {
2214 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2216 if(!fgets(buf, sizeof(buf), stdin)) {
2217 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2221 int len = rstrip(buf);
2224 fprintf(stderr, "No name given!\n");
2230 fprintf(stderr, "No Name given!\n");
2234 name = strdup(argv[1]);
2237 fprintf(stderr, "No Name given!\n");
2242 if(!check_id(name)) {
2243 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2247 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2248 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2252 if(mkdir(confbase, 0777) && errno != EEXIST) {
2253 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2257 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2258 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2262 FILE *f = fopen(tinc_conf, "w");
2265 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2269 fprintf(f, "Name = %s\n", name);
2272 #ifndef DISABLE_LEGACY
2274 if(!rsa_keygen(2048, false)) {
2280 if(!ed25519_keygen(false)) {
2287 char filename[PATH_MAX];
2288 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2290 if(access(filename, F_OK)) {
2291 FILE *f = fopenmask(filename, "w", 0777);
2294 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2298 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");
2308 static int cmd_generate_keys(int argc, char *argv[]) {
2309 #ifdef DISABLE_LEGACY
2317 fprintf(stderr, "Too many arguments!\n");
2322 name = get_my_name(false);
2325 #ifndef DISABLE_LEGACY
2327 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2333 if(!ed25519_keygen(true)) {
2340 #ifndef DISABLE_LEGACY
2341 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2343 fprintf(stderr, "Too many arguments!\n");
2348 name = get_my_name(false);
2351 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2355 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2359 fprintf(stderr, "Too many arguments!\n");
2364 name = get_my_name(false);
2367 return !ed25519_keygen(true);
2370 static int cmd_help(int argc, char *argv[]) {
2378 static int cmd_version(int argc, char *argv[]) {
2382 fprintf(stderr, "Too many arguments!\n");
2390 static int cmd_info(int argc, char *argv[]) {
2392 fprintf(stderr, "Invalid number of arguments.\n");
2396 if(!connect_tincd(true)) {
2400 return info(fd, argv[1]);
2403 static const char *conffiles[] = {
2414 static int cmd_edit(int argc, char *argv[]) {
2416 fprintf(stderr, "Invalid number of arguments.\n");
2420 char filename[PATH_MAX] = "";
2422 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2423 for(int i = 0; conffiles[i]; i++) {
2424 if(!strcmp(argv[1], conffiles[i])) {
2425 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2434 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2435 char *dash = strchr(argv[1], '-');
2440 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2441 fprintf(stderr, "Invalid configuration filename.\n");
2449 const char *editor = getenv("VISUAL");
2452 editor = getenv("EDITOR");
2459 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2461 xasprintf(&command, "edit \"%s\"", filename);
2463 int result = system(command);
2470 // Silently try notifying a running tincd of changes.
2471 if(connect_tincd(false)) {
2472 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2478 static int export(const char *name, FILE *out) {
2479 char filename[PATH_MAX];
2480 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2481 FILE *in = fopen(filename, "r");
2484 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2488 fprintf(out, "Name = %s\n", name);
2491 while(fgets(buf, sizeof(buf), in)) {
2492 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2498 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2507 static int cmd_export(int argc, char *argv[]) {
2511 fprintf(stderr, "Too many arguments!\n");
2515 char *name = get_my_name(true);
2521 int result = export(name, stdout);
2531 static int cmd_export_all(int argc, char *argv[]) {
2535 fprintf(stderr, "Too many arguments!\n");
2539 DIR *dir = opendir(hosts_dir);
2542 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2550 while((ent = readdir(dir))) {
2551 if(!check_id(ent->d_name)) {
2558 printf("#---------------------------------------------------------------#\n");
2561 result |= export(ent->d_name, stdout);
2573 static int cmd_import(int argc, char *argv[]) {
2577 fprintf(stderr, "Too many arguments!\n");
2586 char filename[PATH_MAX] = "";
2588 bool firstline = true;
2590 while(fgets(buf, sizeof(buf), in)) {
2591 if(sscanf(buf, "Name = %4095s", name) == 1) {
2594 if(!check_id(name)) {
2595 fprintf(stderr, "Invalid Name in input!\n");
2603 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2604 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2608 if(!force && !access(filename, F_OK)) {
2609 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2614 out = fopen(filename, "w");
2617 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2623 } else if(firstline) {
2624 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2629 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2634 if(fputs(buf, out) < 0) {
2635 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2646 fprintf(stderr, "Imported %d host configuration files.\n", count);
2649 fprintf(stderr, "No host configuration files imported.\n");
2654 static int cmd_exchange(int argc, char *argv[]) {
2655 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2658 static int cmd_exchange_all(int argc, char *argv[]) {
2659 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2662 static int switch_network(char *name) {
2663 if(strcmp(name, ".")) {
2664 if(!check_netname(name, false)) {
2665 fprintf(stderr, "Invalid character in netname!\n");
2669 if(!check_netname(name, true)) {
2670 fprintf(stderr, "Warning: unsafe character in netname!\n");
2680 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2687 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2688 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2689 xasprintf(&prompt, "%s> ", identname);
2694 static int cmd_network(int argc, char *argv[]) {
2696 fprintf(stderr, "Too many arguments!\n");
2701 return switch_network(argv[1]);
2704 DIR *dir = opendir(confdir);
2707 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2713 while((ent = readdir(dir))) {
2714 if(*ent->d_name == '.') {
2718 if(!strcmp(ent->d_name, "tinc.conf")) {
2723 char fname[PATH_MAX];
2724 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2726 if(!access(fname, R_OK)) {
2727 printf("%s\n", ent->d_name);
2736 static int cmd_fsck(int argc, char *argv[]) {
2740 fprintf(stderr, "Too many arguments!\n");
2744 return fsck(orig_argv[0]);
2747 static void *readfile(FILE *in, size_t *len) {
2749 size_t bufsize = 4096;
2750 char *buf = xmalloc(bufsize);
2753 size_t read = fread(buf + count, 1, bufsize - count, in);
2761 if(count >= bufsize) {
2763 buf = xrealloc(buf, bufsize);
2774 static int cmd_sign(int argc, char *argv[]) {
2776 fprintf(stderr, "Too many arguments!\n");
2781 name = get_my_name(true);
2788 char fname[PATH_MAX];
2789 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2790 FILE *fp = fopen(fname, "r");
2793 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2797 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2800 fprintf(stderr, "Could not read private key from %s\n", fname);
2810 in = fopen(argv[1], "rb");
2813 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2822 char *data = readfile(in, &len);
2829 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2834 // Ensure we sign our name and current time as well
2835 long t = time(NULL);
2837 xasprintf(&trailer, " %s %ld", name, t);
2838 int trailer_len = strlen(trailer);
2840 data = xrealloc(data, len + trailer_len);
2841 memcpy(data + len, trailer, trailer_len);
2846 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2847 fprintf(stderr, "Error generating signature\n");
2853 b64encode(sig, sig, 64);
2856 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2857 fwrite(data, len, 1, stdout);
2863 static int cmd_verify(int argc, char *argv[]) {
2865 fprintf(stderr, "Not enough arguments!\n");
2870 fprintf(stderr, "Too many arguments!\n");
2874 char *node = argv[1];
2876 if(!strcmp(node, ".")) {
2878 name = get_my_name(true);
2886 } else if(!strcmp(node, "*")) {
2889 if(!check_id(node)) {
2890 fprintf(stderr, "Invalid node name\n");
2898 in = fopen(argv[2], "rb");
2901 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2909 char *data = readfile(in, &len);
2916 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2920 char *newline = memchr(data, '\n', len);
2922 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2923 fprintf(stderr, "Invalid input\n");
2929 size_t skip = newline - data;
2931 char signer[MAX_STRING_SIZE] = "";
2932 char sig[MAX_STRING_SIZE] = "";
2935 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2936 fprintf(stderr, "Invalid input\n");
2941 if(node && strcmp(node, signer)) {
2942 fprintf(stderr, "Signature is not made by %s\n", node);
2952 xasprintf(&trailer, " %s %ld", signer, t);
2953 int trailer_len = strlen(trailer);
2955 data = xrealloc(data, len + trailer_len);
2956 memcpy(data + len, trailer, trailer_len);
2959 newline = data + skip;
2961 char fname[PATH_MAX];
2962 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2963 FILE *fp = fopen(fname, "r");
2966 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2971 ecdsa_t *key = get_pubkey(fp);
2975 key = ecdsa_read_pem_public_key(fp);
2979 fprintf(stderr, "Could not read public key from %s\n", fname);
2987 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2988 fprintf(stderr, "Invalid signature\n");
2996 fwrite(newline, len - (newline - data), 1, stdout);
3002 static const struct {
3003 const char *command;
3004 int (*function)(int argc, char *argv[]);
3007 {"start", cmd_start, false},
3008 {"stop", cmd_stop, false},
3009 {"restart", cmd_restart, false},
3010 {"reload", cmd_reload, false},
3011 {"dump", cmd_dump, false},
3012 {"list", cmd_dump, false},
3013 {"purge", cmd_purge, false},
3014 {"debug", cmd_debug, false},
3015 {"retry", cmd_retry, false},
3016 {"connect", cmd_connect, false},
3017 {"disconnect", cmd_disconnect, false},
3018 {"top", cmd_top, false},
3019 {"pcap", cmd_pcap, false},
3020 {"log", cmd_log, false},
3021 {"pid", cmd_pid, false},
3022 {"config", cmd_config, true},
3023 {"add", cmd_config, false},
3024 {"del", cmd_config, false},
3025 {"get", cmd_config, false},
3026 {"set", cmd_config, false},
3027 {"init", cmd_init, false},
3028 {"generate-keys", cmd_generate_keys, false},
3029 #ifndef DISABLE_LEGACY
3030 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3032 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3033 {"help", cmd_help, false},
3034 {"version", cmd_version, false},
3035 {"info", cmd_info, false},
3036 {"edit", cmd_edit, false},
3037 {"export", cmd_export, false},
3038 {"export-all", cmd_export_all, false},
3039 {"import", cmd_import, false},
3040 {"exchange", cmd_exchange, false},
3041 {"exchange-all", cmd_exchange_all, false},
3042 {"invite", cmd_invite, false},
3043 {"join", cmd_join, false},
3044 {"network", cmd_network, false},
3045 {"fsck", cmd_fsck, false},
3046 {"sign", cmd_sign, false},
3047 {"verify", cmd_verify, false},
3048 {NULL, NULL, false},
3051 #ifdef HAVE_READLINE
3052 static char *complete_command(const char *text, int state) {
3061 while(commands[i].command) {
3062 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3063 return xstrdup(commands[i].command);
3072 static char *complete_dump(const char *text, int state) {
3073 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3083 if(!strncasecmp(matches[i], text, strlen(text))) {
3084 return xstrdup(matches[i]);
3093 static char *complete_config(const char *text, int state) {
3102 while(variables[i].name) {
3103 char *dot = strchr(text, '.');
3106 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3108 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3112 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3113 return xstrdup(variables[i].name);
3123 static char *complete_info(const char *text, int state) {
3129 if(!connect_tincd(false)) {
3133 // Check the list of nodes
3134 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3135 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3138 while(recvline(fd, line, sizeof(line))) {
3140 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3153 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3157 if(!strncmp(item, text, strlen(text))) {
3158 return xstrdup(strip_weight(item));
3165 static char *complete_nothing(const char *text, int state) {
3171 static char **completion(const char *text, int start, int end) {
3173 char **matches = NULL;
3176 matches = rl_completion_matches(text, complete_command);
3177 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3178 matches = rl_completion_matches(text, complete_dump);
3179 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3180 matches = rl_completion_matches(text, complete_config);
3181 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3182 matches = rl_completion_matches(text, complete_config);
3183 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3184 matches = rl_completion_matches(text, complete_config);
3185 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3186 matches = rl_completion_matches(text, complete_config);
3187 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3188 matches = rl_completion_matches(text, complete_info);
3195 static int cmd_shell(int argc, char *argv[]) {
3196 xasprintf(&prompt, "%s> ", identname);
3200 int maxargs = argc + 16;
3201 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3203 for(int i = 0; i < argc; i++) {
3207 #ifdef HAVE_READLINE
3208 rl_readline_name = "tinc";
3209 rl_completion_entry_function = complete_nothing;
3210 rl_attempted_completion_function = completion;
3211 rl_filename_completion_desired = 0;
3216 #ifdef HAVE_READLINE
3221 rl_basic_word_break_characters = "\t\n ";
3222 line = readline(prompt);
3223 copy = line ? xstrdup(line) : NULL;
3225 line = fgets(buf, sizeof(buf), stdin);
3231 fputs(prompt, stdout);
3234 line = fgets(buf, sizeof(buf), stdin);
3241 /* Ignore comments */
3250 char *p = line + strspn(line, " \t\n");
3251 char *next = strtok(p, " \t\n");
3254 if(nargc >= maxargs) {
3256 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3261 next = strtok(NULL, " \t\n");
3268 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3269 #ifdef HAVE_READLINE
3278 for(int i = 0; commands[i].command; i++) {
3279 if(!strcasecmp(nargv[argc], commands[i].command)) {
3280 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3286 #ifdef HAVE_READLINE
3295 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3300 #ifdef HAVE_READLINE
3313 int main(int argc, char *argv[]) {
3314 program_name = argv[0];
3317 tty = isatty(0) && isatty(1);
3319 if(!parse_options(argc, argv)) {
3324 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3325 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3338 static struct WSAData wsa_state;
3340 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3341 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3350 if(optind >= argc) {
3351 return cmd_shell(argc, argv);
3354 for(int i = 0; commands[i].command; i++) {
3355 if(!strcasecmp(argv[optind], commands[i].command)) {
3356 return commands[i].function(argc - optind, argv + optind);
3360 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);