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] = ".";
358 /* Check stdin and stdout */
360 /* Ask for a file and/or directory name. */
361 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
363 if(fgets(buf, sizeof(buf), stdin) == NULL) {
364 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
368 size_t len = strlen(buf);
381 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
384 if(filename[0] != '/') {
386 /* The directory is a relative path or a filename. */
387 getcwd(directory, sizeof(directory));
388 snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename);
392 disable_old_keys(filename, what);
394 /* Open it first to keep the inode busy */
396 r = fopenmask(filename, mode, perms);
399 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
407 Generate a public/private Ed25519 keypair, and ask for a file to store
410 static bool ed25519_keygen(bool ask) {
413 char fname[PATH_MAX];
415 fprintf(stderr, "Generating Ed25519 keypair:\n");
417 if(!(key = ecdsa_generate())) {
418 fprintf(stderr, "Error during key generation!\n");
421 fprintf(stderr, "Done.\n");
424 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
425 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
431 if(!ecdsa_write_pem_private_key(key, f)) {
432 fprintf(stderr, "Error writing private key!\n");
439 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
441 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
444 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
450 char *pubkey = ecdsa_get_base64_public_key(key);
451 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
469 #ifndef DISABLE_LEGACY
471 Generate a public/private RSA keypair, and ask for a file to store
474 static bool rsa_keygen(int bits, bool ask) {
477 char fname[PATH_MAX];
479 // Make sure the key size is a multiple of 8 bits.
482 // Make sure that a valid key size is used.
483 if(bits < 1024 || bits > 8192) {
484 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
486 } else if(bits < 2048) {
487 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
490 fprintf(stderr, "Generating %d bits keys:\n", bits);
492 if(!(key = rsa_generate(bits, 0x10001))) {
493 fprintf(stderr, "Error during key generation!\n");
496 fprintf(stderr, "Done.\n");
499 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
500 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
506 if(!rsa_write_pem_private_key(key, f)) {
507 fprintf(stderr, "Error writing private key!\n");
514 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
516 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
519 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
525 if(!rsa_write_pem_public_key(key, f)) {
526 fprintf(stderr, "Error writing public key!\n");
549 bool recvline(int fd, char *line, size_t len) {
550 char *newline = NULL;
556 while(!(newline = memchr(buffer, '\n', blen))) {
557 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
559 if(result == -1 && sockerrno == EINTR) {
561 } else if(result <= 0) {
568 if(newline - buffer >= len) {
572 len = newline - buffer;
574 memcpy(line, buffer, len);
576 memmove(buffer, newline + 1, blen - len - 1);
582 bool recvdata(int fd, char *data, size_t len) {
588 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
590 if(result == -1 && sockerrno == EINTR) {
592 } else if(result <= 0) {
599 memcpy(data, buffer, len);
600 memmove(buffer, buffer + len, blen - len);
606 bool sendline(int fd, char *format, ...) {
607 static char buffer[4096];
612 va_start(ap, format);
613 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
614 buffer[sizeof(buffer) - 1] = 0;
617 if(blen < 1 || blen >= sizeof(buffer)) {
625 int result = send(fd, p, blen, MSG_NOSIGNAL);
627 if(result == -1 && sockerrno == EINTR) {
629 } else if(result <= 0) {
640 static void pcap(int fd, FILE *out, int snaplen) {
641 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
649 uint32_t tz_accuracy;
656 snaplen ? : sizeof(data),
669 fwrite(&header, sizeof(header), 1, out);
674 while(recvline(fd, line, sizeof(line))) {
676 int n = sscanf(line, "%d %d %d", &code, &req, &len);
677 gettimeofday(&tv, NULL);
679 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof(data)) {
683 if(!recvdata(fd, data, len)) {
687 packet.tv_sec = tv.tv_sec;
688 packet.tv_usec = tv.tv_usec;
690 packet.origlen = len;
691 fwrite(&packet, sizeof(packet), 1, out);
692 fwrite(data, len, 1, out);
697 static void logcontrol(int fd, FILE *out, int level) {
698 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
702 while(recvline(fd, line, sizeof(line))) {
704 int n = sscanf(line, "%d %d %d", &code, &req, &len);
706 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof(data)) {
710 if(!recvdata(fd, data, len)) {
714 fwrite(data, len, 1, out);
721 static bool remove_service(void) {
722 SC_HANDLE manager = NULL;
723 SC_HANDLE service = NULL;
724 SERVICE_STATUS status = {0};
725 bool success = false;
727 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
730 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
734 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
737 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
741 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
742 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
744 fprintf(stderr, "%s service stopped\n", identname);
747 if(!DeleteService(service)) {
748 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
757 CloseServiceHandle(service);
761 CloseServiceHandle(manager);
765 fprintf(stderr, "%s service removed\n", identname);
772 bool connect_tincd(bool verbose) {
777 struct timeval tv = {0, 0};
779 if(select(fd + 1, &r, NULL, NULL, &tv)) {
780 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
788 FILE *f = fopen(pidfilename, "r");
792 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
801 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
803 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
814 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
815 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
816 /* clean up the stale socket and pid file */
818 unlink(unixsocketname);
822 struct sockaddr_un sa;
824 sa.sun_family = AF_UNIX;
826 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
828 fd = socket(AF_UNIX, SOCK_STREAM, 0);
832 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
838 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
840 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
849 struct addrinfo hints = {
850 .ai_family = AF_UNSPEC,
851 .ai_socktype = SOCK_STREAM,
852 .ai_protocol = IPPROTO_TCP,
856 struct addrinfo *res = NULL;
858 if(getaddrinfo(host, port, &hints, &res) || !res) {
860 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
866 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
870 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
877 unsigned long arg = 0;
879 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
881 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
887 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
889 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
901 static const int one = 1;
902 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
908 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
910 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
918 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
920 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
922 fprintf(stderr, "Could not fully establish control socket connection\n");
934 static int cmd_start(int argc, char *argv[]) {
935 if(connect_tincd(false)) {
937 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
939 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
946 char *slash = strrchr(program_name, '/');
950 if((c = strrchr(program_name, '\\')) > slash) {
957 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
963 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
968 Windows has no real concept of an "argv array". A command line is just one string.
969 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
970 it uses quotes to handle spaces in arguments.
971 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
972 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
973 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
975 xasprintf(&arg0, "\"%s\"", arg0);
977 nargv[nargc++] = arg0;
979 for(int i = 1; i < optind; i++) {
980 nargv[nargc++] = orig_argv[i];
983 for(int i = 1; i < argc; i++) {
984 nargv[nargc++] = argv[i];
988 int status = spawnvp(_P_WAIT, c, nargv);
991 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
997 int pfd[2] = {-1, -1};
999 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
1000 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
1008 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1016 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1017 setenv("TINC_UMBILICAL", buf, true);
1018 exit(execvp(c, nargv));
1025 int status = -1, result;
1027 signal(SIGINT, SIG_IGN);
1030 // Pass all log messages from the umbilical to stderr.
1031 // A nul-byte right before closure means tincd started successfully.
1032 bool failure = true;
1036 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1037 failure = buf[len - 1];
1052 // Make sure the child process is really gone.
1053 result = waitpid(pid, &status, 0);
1056 signal(SIGINT, SIG_DFL);
1059 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1060 fprintf(stderr, "Error starting %s\n", c);
1068 static int cmd_stop(int argc, char *argv[]) {
1070 fprintf(stderr, "Too many arguments!\n");
1076 if(!connect_tincd(true)) {
1078 if(kill(pid, SIGTERM)) {
1079 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1083 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1084 waitpid(pid, NULL, 0);
1091 sendline(fd, "%d %d", CONTROL, REQ_STOP);
1093 while(recvline(fd, line, sizeof(line))) {
1094 // Wait for tincd to close the connection...
1099 if(!remove_service()) {
1111 static int cmd_restart(int argc, char *argv[]) {
1113 return cmd_start(argc, argv);
1116 static int cmd_reload(int argc, char *argv[]) {
1118 fprintf(stderr, "Too many arguments!\n");
1122 if(!connect_tincd(true)) {
1126 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1128 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1129 fprintf(stderr, "Could not reload configuration.\n");
1137 static int dump_invitations(void) {
1138 char dname[PATH_MAX];
1139 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1140 DIR *dir = opendir(dname);
1143 if(errno == ENOENT) {
1144 fprintf(stderr, "No outstanding invitations.\n");
1148 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1156 while((ent = readdir(dir))) {
1157 char buf[MAX_STRING_SIZE];
1159 if(b64decode(ent->d_name, buf, 24) != 18) {
1163 char fname[PATH_MAX];
1164 snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name);
1165 FILE *f = fopen(fname, "r");
1168 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1174 if(!fgets(buf, sizeof(buf), f)) {
1175 fprintf(stderr, "Invalid invitation file %s\n", fname);
1182 char *eol = buf + strlen(buf);
1184 while(strchr("\t \r\n", *--eol)) {
1188 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1189 fprintf(stderr, "Invalid invitation file %s\n", fname);
1194 printf("%s %s\n", ent->d_name, buf + 7);
1200 fprintf(stderr, "No outstanding invitations.\n");
1206 static int cmd_dump(int argc, char *argv[]) {
1207 bool only_reachable = false;
1209 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1210 if(strcasecmp(argv[2], "nodes")) {
1211 fprintf(stderr, "`reachable' only supported for nodes.\n");
1216 only_reachable = true;
1222 fprintf(stderr, "Invalid number of arguments.\n");
1227 if(!strcasecmp(argv[1], "invitations")) {
1228 return dump_invitations();
1231 if(!connect_tincd(true)) {
1237 if(!strcasecmp(argv[1], "nodes")) {
1238 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1239 } else if(!strcasecmp(argv[1], "edges")) {
1240 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1241 } else if(!strcasecmp(argv[1], "subnets")) {
1242 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1243 } else if(!strcasecmp(argv[1], "connections")) {
1244 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1245 } else if(!strcasecmp(argv[1], "graph")) {
1246 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1247 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1249 } else if(!strcasecmp(argv[1], "digraph")) {
1250 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1251 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1254 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1260 printf("graph {\n");
1261 } else if(do_graph == 2) {
1262 printf("digraph {\n");
1265 while(recvline(fd, line, sizeof(line))) {
1266 char node1[4096], node2[4096];
1267 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1270 if(do_graph && req == REQ_DUMP_NODES) {
1292 char local_host[4096];
1293 char local_port[4096];
1296 int cipher, digest, maclength, compression, distance, socket, weight;
1297 short int pmtu, minmtu, maxmtu;
1298 unsigned int options, status_int;
1299 node_status_t status;
1300 long int last_state_change;
1302 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1305 case REQ_DUMP_NODES: {
1306 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);
1309 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1313 memcpy(&status, &status_int, sizeof(status));
1316 const char *color = "black";
1318 if(!strcmp(host, "MYSELF")) {
1320 } else if(!status.reachable) {
1322 } else if(strcmp(via, node)) {
1324 } else if(!status.validkey) {
1326 } else if(minmtu > 0) {
1330 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1332 if(only_reachable && !status.reachable) {
1336 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,
1337 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);
1339 if(udp_ping_rtt != -1) {
1340 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1348 case REQ_DUMP_EDGES: {
1349 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);
1352 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1357 float w = 1 + 65536.0 / weight;
1359 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1360 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1361 } else if(do_graph == 2) {
1362 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1365 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);
1370 case REQ_DUMP_SUBNETS: {
1371 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1374 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1378 printf("%s owner %s\n", strip_weight(subnet), node);
1382 case REQ_DUMP_CONNECTIONS: {
1383 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1386 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1390 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1395 fprintf(stderr, "Unable to parse dump from tincd.\n");
1400 fprintf(stderr, "Error receiving dump.\n");
1404 static int cmd_purge(int argc, char *argv[]) {
1406 fprintf(stderr, "Too many arguments!\n");
1410 if(!connect_tincd(true)) {
1414 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1416 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1417 fprintf(stderr, "Could not purge old information.\n");
1424 static int cmd_debug(int argc, char *argv[]) {
1426 fprintf(stderr, "Invalid number of arguments.\n");
1430 if(!connect_tincd(true)) {
1434 int debuglevel = atoi(argv[1]);
1437 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1439 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1440 fprintf(stderr, "Could not set debug level.\n");
1444 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1448 static int cmd_retry(int argc, char *argv[]) {
1450 fprintf(stderr, "Too many arguments!\n");
1454 if(!connect_tincd(true)) {
1458 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1460 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1461 fprintf(stderr, "Could not retry outgoing connections.\n");
1468 static int cmd_connect(int argc, char *argv[]) {
1470 fprintf(stderr, "Invalid number of arguments.\n");
1474 if(!check_id(argv[1])) {
1475 fprintf(stderr, "Invalid name for node.\n");
1479 if(!connect_tincd(true)) {
1483 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1485 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1486 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1493 static int cmd_disconnect(int argc, char *argv[]) {
1495 fprintf(stderr, "Invalid number of arguments.\n");
1499 if(!check_id(argv[1])) {
1500 fprintf(stderr, "Invalid name for node.\n");
1504 if(!connect_tincd(true)) {
1508 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1510 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1511 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1518 static int cmd_top(int argc, char *argv[]) {
1520 fprintf(stderr, "Too many arguments!\n");
1526 if(!connect_tincd(true)) {
1533 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1538 static int cmd_pcap(int argc, char *argv[]) {
1540 fprintf(stderr, "Too many arguments!\n");
1544 if(!connect_tincd(true)) {
1548 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1553 static void sigint_handler(int sig) {
1554 fprintf(stderr, "\n");
1555 shutdown(fd, SHUT_RDWR);
1559 static int cmd_log(int argc, char *argv[]) {
1561 fprintf(stderr, "Too many arguments!\n");
1565 if(!connect_tincd(true)) {
1570 signal(SIGINT, sigint_handler);
1573 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1576 signal(SIGINT, SIG_DFL);
1584 static int cmd_pid(int argc, char *argv[]) {
1586 fprintf(stderr, "Too many arguments!\n");
1590 if(!connect_tincd(true) || !pid) {
1594 printf("%d\n", pid);
1598 int rstrip(char *value) {
1599 int len = strlen(value);
1601 while(len && strchr("\t\r\n ", value[len - 1])) {
1608 char *get_my_name(bool verbose) {
1609 FILE *f = fopen(tinc_conf, "r");
1613 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1622 while(fgets(buf, sizeof(buf), f)) {
1623 int len = strcspn(buf, "\t =");
1625 value += strspn(value, "\t ");
1629 value += strspn(value, "\t ");
1632 if(!rstrip(value)) {
1638 if(strcasecmp(buf, "Name")) {
1644 return replace_name(value);
1651 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1657 ecdsa_t *get_pubkey(FILE *f) {
1661 while(fgets(buf, sizeof(buf), f)) {
1662 int len = strcspn(buf, "\t =");
1664 value += strspn(value, "\t ");
1668 value += strspn(value, "\t ");
1671 if(!rstrip(value)) {
1677 if(strcasecmp(buf, "Ed25519PublicKey")) {
1682 return ecdsa_set_base64_public_key(value);
1689 const var_t variables[] = {
1690 /* Server configuration */
1691 {"AddressFamily", VAR_SERVER},
1692 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1693 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1694 {"BindToInterface", VAR_SERVER},
1695 {"Broadcast", VAR_SERVER | VAR_SAFE},
1696 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1697 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1698 {"DecrementTTL", VAR_SERVER},
1699 {"Device", VAR_SERVER},
1700 {"DeviceStandby", VAR_SERVER},
1701 {"DeviceType", VAR_SERVER},
1702 {"DirectOnly", VAR_SERVER},
1703 {"Ed25519PrivateKeyFile", VAR_SERVER},
1704 {"ExperimentalProtocol", VAR_SERVER},
1705 {"Forwarding", VAR_SERVER},
1706 {"FWMark", VAR_SERVER},
1707 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1708 {"Hostnames", VAR_SERVER},
1709 {"IffOneQueue", VAR_SERVER},
1710 {"Interface", VAR_SERVER},
1711 {"InvitationExpire", VAR_SERVER},
1712 {"KeyExpire", VAR_SERVER},
1713 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1714 {"LocalDiscovery", VAR_SERVER},
1715 {"LogLevel", VAR_SERVER},
1716 {"MACExpire", VAR_SERVER},
1717 {"MaxConnectionBurst", VAR_SERVER},
1718 {"MaxOutputBufferSize", VAR_SERVER},
1719 {"MaxTimeout", VAR_SERVER},
1720 {"Mode", VAR_SERVER | VAR_SAFE},
1721 {"Name", VAR_SERVER},
1722 {"PingInterval", VAR_SERVER},
1723 {"PingTimeout", VAR_SERVER},
1724 {"PriorityInheritance", VAR_SERVER},
1725 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1726 {"PrivateKeyFile", VAR_SERVER},
1727 {"ProcessPriority", VAR_SERVER},
1728 {"Proxy", VAR_SERVER},
1729 {"ReplayWindow", VAR_SERVER},
1730 {"ScriptsExtension", VAR_SERVER},
1731 {"ScriptsInterpreter", VAR_SERVER},
1732 {"StrictSubnets", VAR_SERVER},
1733 {"TunnelServer", VAR_SERVER},
1734 {"UDPDiscovery", VAR_SERVER},
1735 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1736 {"UDPDiscoveryInterval", VAR_SERVER},
1737 {"UDPDiscoveryTimeout", VAR_SERVER},
1738 {"MTUInfoInterval", VAR_SERVER},
1739 {"UDPInfoInterval", VAR_SERVER},
1740 {"UDPRcvBuf", VAR_SERVER},
1741 {"UDPSndBuf", VAR_SERVER},
1742 {"UPnP", VAR_SERVER},
1743 {"UPnPDiscoverWait", VAR_SERVER},
1744 {"UPnPRefreshPeriod", VAR_SERVER},
1745 {"VDEGroup", VAR_SERVER},
1746 {"VDEPort", VAR_SERVER},
1747 /* Host configuration */
1748 {"Address", VAR_HOST | VAR_MULTIPLE},
1749 {"Cipher", VAR_SERVER | VAR_HOST},
1750 {"ClampMSS", VAR_SERVER | VAR_HOST},
1751 {"Compression", VAR_SERVER | VAR_HOST},
1752 {"Digest", VAR_SERVER | VAR_HOST},
1753 {"Ed25519PublicKey", VAR_HOST},
1754 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1755 {"IndirectData", VAR_SERVER | VAR_HOST},
1756 {"MACLength", VAR_SERVER | VAR_HOST},
1757 {"PMTU", VAR_SERVER | VAR_HOST},
1758 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1760 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1761 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1762 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1763 {"TCPOnly", VAR_SERVER | VAR_HOST},
1764 {"Weight", VAR_HOST | VAR_SAFE},
1768 static int cmd_config(int argc, char *argv[]) {
1770 fprintf(stderr, "Invalid number of arguments.\n");
1774 if(strcasecmp(argv[0], "config")) {
1780 if(!strcasecmp(argv[1], "get")) {
1782 } else if(!strcasecmp(argv[1], "add")) {
1783 argv++, argc--, action = 1;
1784 } else if(!strcasecmp(argv[1], "del")) {
1785 argv++, argc--, action = -1;
1786 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1787 argv++, argc--, action = 0;
1791 fprintf(stderr, "Invalid number of arguments.\n");
1795 // Concatenate the rest of the command line
1796 strncpy(line, argv[1], sizeof(line) - 1);
1798 for(int i = 2; i < argc; i++) {
1799 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1800 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1803 // Liberal parsing into node name, variable name and value.
1809 len = strcspn(line, "\t =");
1811 value += strspn(value, "\t ");
1815 value += strspn(value, "\t ");
1819 variable = strchr(line, '.');
1829 fprintf(stderr, "No variable given.\n");
1833 if(action >= 0 && !*value) {
1834 fprintf(stderr, "No value for variable given.\n");
1838 if(action < -1 && *value) {
1842 /* Some simple checks. */
1844 bool warnonremove = false;
1846 for(int i = 0; variables[i].name; i++) {
1847 if(strcasecmp(variables[i].name, variable)) {
1852 variable = (char *)variables[i].name;
1854 /* Discourage use of obsolete variables. */
1856 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1858 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1860 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1865 /* Don't put server variables in host config files */
1867 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1869 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1871 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1876 /* Should this go into our own host config file? */
1878 if(!node && !(variables[i].type & VAR_SERVER)) {
1879 node = get_my_name(true);
1886 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1887 Turn on warnings when it seems variables might be removed unintentionally. */
1889 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1890 warnonremove = true;
1892 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1893 warnonremove = true;
1899 if(node && !check_id(node)) {
1900 fprintf(stderr, "Invalid name for node.\n");
1905 if(force || action < 0) {
1906 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1908 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1913 // Open the right configuration file.
1914 char filename[PATH_MAX];
1917 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1919 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1922 FILE *f = fopen(filename, "r");
1925 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1929 char tmpfile[PATH_MAX];
1933 snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename);
1934 tf = fopen(tmpfile, "w");
1937 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1943 // Copy the file, making modifications on the fly, unless we are just getting a value.
1947 bool removed = false;
1950 while(fgets(buf1, sizeof(buf1), f)) {
1951 buf1[sizeof(buf1) - 1] = 0;
1952 strncpy(buf2, buf1, sizeof(buf2));
1954 // Parse line in a simple way
1958 len = strcspn(buf2, "\t =");
1959 bvalue = buf2 + len;
1960 bvalue += strspn(bvalue, "\t ");
1962 if(*bvalue == '=') {
1964 bvalue += strspn(bvalue, "\t ");
1971 if(!strcasecmp(buf2, variable)) {
1975 printf("%s\n", bvalue);
1977 } else if(action == -1) {
1978 if(!*value || !strcasecmp(bvalue, value)) {
1984 } else if(action == 0) {
1985 // Warn if "set" was used for variables that can occur multiple times
1986 if(warnonremove && strcasecmp(bvalue, value)) {
1987 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1990 // Already set? Delete the rest...
1995 // Otherwise, replace.
1996 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1997 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2004 } else if(action > 0) {
2005 // Check if we've already seen this variable with the same value
2006 if(!strcasecmp(bvalue, value)) {
2013 // Copy original line...
2014 if(fputs(buf1, tf) < 0) {
2015 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2019 // Add newline if it is missing...
2020 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2021 if(fputc('\n', tf) < 0) {
2022 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2029 // Make sure we read everything...
2030 if(ferror(f) || !feof(f)) {
2031 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2036 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2040 // Add new variable if necessary.
2041 if((action > 0 && !found) || (action == 0 && !set)) {
2042 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2043 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2052 fprintf(stderr, "No matching configuration variables found.\n");
2057 // Make sure we wrote everything...
2059 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2063 // Could we find what we had to remove?
2064 if(action < 0 && !removed) {
2066 fprintf(stderr, "No configuration variables deleted.\n");
2070 // Replace the configuration file with the new one
2073 if(remove(filename)) {
2074 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2080 if(rename(tmpfile, filename)) {
2081 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2085 // Silently try notifying a running tincd of changes.
2086 if(connect_tincd(false)) {
2087 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2093 static bool try_bind(int port) {
2094 struct addrinfo *ai = NULL, *aip;
2095 struct addrinfo hint = {
2096 .ai_flags = AI_PASSIVE,
2097 .ai_family = AF_UNSPEC,
2098 .ai_socktype = SOCK_STREAM,
2099 .ai_protocol = IPPROTO_TCP,
2102 bool success = true;
2104 snprintf(portstr, sizeof(portstr), "%d", port);
2106 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2110 for(aip = ai; aip; aip = aip->ai_next) {
2111 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2118 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2131 int check_port(char *name) {
2136 fprintf(stderr, "Warning: could not bind to port 655. ");
2138 for(int i = 0; i < 100; i++) {
2139 int port = 0x1000 + (rand() & 0x7fff);
2141 if(try_bind(port)) {
2142 char filename[PATH_MAX];
2143 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2144 FILE *f = fopen(filename, "a");
2147 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2148 fprintf(stderr, "Please change tinc's Port manually.\n");
2152 fprintf(f, "Port = %d\n", port);
2154 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2159 fprintf(stderr, "Please change tinc's Port manually.\n");
2163 static int cmd_init(int argc, char *argv[]) {
2164 if(!access(tinc_conf, F_OK)) {
2165 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2170 fprintf(stderr, "Too many arguments!\n");
2172 } else if(argc < 2) {
2175 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2177 if(!fgets(buf, sizeof(buf), stdin)) {
2178 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2182 int len = rstrip(buf);
2185 fprintf(stderr, "No name given!\n");
2191 fprintf(stderr, "No Name given!\n");
2195 name = strdup(argv[1]);
2198 fprintf(stderr, "No Name given!\n");
2203 if(!check_id(name)) {
2204 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2208 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2209 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2213 if(mkdir(confbase, 0777) && errno != EEXIST) {
2214 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2218 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2219 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2223 FILE *f = fopen(tinc_conf, "w");
2226 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2230 fprintf(f, "Name = %s\n", name);
2233 #ifndef DISABLE_LEGACY
2235 if(!rsa_keygen(2048, false)) {
2241 if(!ed25519_keygen(false)) {
2248 char filename[PATH_MAX];
2249 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2251 if(access(filename, F_OK)) {
2252 FILE *f = fopenmask(filename, "w", 0777);
2255 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2259 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");
2269 static int cmd_generate_keys(int argc, char *argv[]) {
2270 #ifdef DISABLE_LEGACY
2277 fprintf(stderr, "Too many arguments!\n");
2282 name = get_my_name(false);
2285 #ifndef DISABLE_LEGACY
2287 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2293 if(!ed25519_keygen(true)) {
2300 #ifndef DISABLE_LEGACY
2301 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2303 fprintf(stderr, "Too many arguments!\n");
2308 name = get_my_name(false);
2311 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2315 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2317 fprintf(stderr, "Too many arguments!\n");
2322 name = get_my_name(false);
2325 return !ed25519_keygen(true);
2328 static int cmd_help(int argc, char *argv[]) {
2333 static int cmd_version(int argc, char *argv[]) {
2335 fprintf(stderr, "Too many arguments!\n");
2343 static int cmd_info(int argc, char *argv[]) {
2345 fprintf(stderr, "Invalid number of arguments.\n");
2349 if(!connect_tincd(true)) {
2353 return info(fd, argv[1]);
2356 static const char *conffiles[] = {
2367 static int cmd_edit(int argc, char *argv[]) {
2369 fprintf(stderr, "Invalid number of arguments.\n");
2373 char filename[PATH_MAX] = "";
2375 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2376 for(int i = 0; conffiles[i]; i++) {
2377 if(!strcmp(argv[1], conffiles[i])) {
2378 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2387 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2388 char *dash = strchr(argv[1], '-');
2393 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2394 fprintf(stderr, "Invalid configuration filename.\n");
2402 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ? : getenv("EDITOR") ? : "vi", filename);
2404 xasprintf(&command, "edit \"%s\"", filename);
2406 int result = system(command);
2413 // Silently try notifying a running tincd of changes.
2414 if(connect_tincd(false)) {
2415 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2421 static int export(const char *name, FILE *out) {
2422 char filename[PATH_MAX];
2423 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2424 FILE *in = fopen(filename, "r");
2427 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2431 fprintf(out, "Name = %s\n", name);
2434 while(fgets(buf, sizeof(buf), in)) {
2435 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2441 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2450 static int cmd_export(int argc, char *argv[]) {
2452 fprintf(stderr, "Too many arguments!\n");
2456 char *name = get_my_name(true);
2462 int result = export(name, stdout);
2472 static int cmd_export_all(int argc, char *argv[]) {
2474 fprintf(stderr, "Too many arguments!\n");
2478 DIR *dir = opendir(hosts_dir);
2481 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2489 while((ent = readdir(dir))) {
2490 if(!check_id(ent->d_name)) {
2497 printf("#---------------------------------------------------------------#\n");
2500 result |= export(ent->d_name, stdout);
2512 static int cmd_import(int argc, char *argv[]) {
2514 fprintf(stderr, "Too many arguments!\n");
2523 char filename[PATH_MAX] = "";
2525 bool firstline = true;
2527 while(fgets(buf, sizeof(buf), in)) {
2528 if(sscanf(buf, "Name = %4095s", name) == 1) {
2531 if(!check_id(name)) {
2532 fprintf(stderr, "Invalid Name in input!\n");
2540 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2542 if(!force && !access(filename, F_OK)) {
2543 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2548 out = fopen(filename, "w");
2551 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2557 } else if(firstline) {
2558 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2563 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2568 if(fputs(buf, out) < 0) {
2569 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2580 fprintf(stderr, "Imported %d host configuration files.\n", count);
2583 fprintf(stderr, "No host configuration files imported.\n");
2588 static int cmd_exchange(int argc, char *argv[]) {
2589 return cmd_export(argc, argv) ? : cmd_import(argc, argv);
2592 static int cmd_exchange_all(int argc, char *argv[]) {
2593 return cmd_export_all(argc, argv) ? : cmd_import(argc, argv);
2596 static int switch_network(char *name) {
2597 if(strcmp(name, ".")) {
2598 if(!check_netname(name, false)) {
2599 fprintf(stderr, "Invalid character in netname!\n");
2603 if(!check_netname(name, true)) {
2604 fprintf(stderr, "Warning: unsafe character in netname!\n");
2614 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2621 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2622 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2623 xasprintf(&prompt, "%s> ", identname);
2628 static int cmd_network(int argc, char *argv[]) {
2630 fprintf(stderr, "Too many arguments!\n");
2635 return switch_network(argv[1]);
2638 DIR *dir = opendir(confdir);
2641 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2647 while((ent = readdir(dir))) {
2648 if(*ent->d_name == '.') {
2652 if(!strcmp(ent->d_name, "tinc.conf")) {
2657 char fname[PATH_MAX];
2658 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2660 if(!access(fname, R_OK)) {
2661 printf("%s\n", ent->d_name);
2670 static int cmd_fsck(int argc, char *argv[]) {
2672 fprintf(stderr, "Too many arguments!\n");
2676 return fsck(orig_argv[0]);
2679 static void *readfile(FILE *in, size_t *len) {
2681 size_t alloced = 4096;
2682 char *buf = xmalloc(alloced);
2685 size_t read = fread(buf + count, 1, alloced - count, in);
2693 if(count >= alloced) {
2695 buf = xrealloc(buf, alloced);
2706 static int cmd_sign(int argc, char *argv[]) {
2708 fprintf(stderr, "Too many arguments!\n");
2713 name = get_my_name(true);
2720 char fname[PATH_MAX];
2721 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2722 FILE *fp = fopen(fname, "r");
2725 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2729 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2732 fprintf(stderr, "Could not read private key from %s\n", fname);
2742 in = fopen(argv[1], "rb");
2745 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2754 char *data = readfile(in, &len);
2761 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2766 // Ensure we sign our name and current time as well
2767 long t = time(NULL);
2769 xasprintf(&trailer, " %s %ld", name, t);
2770 int trailer_len = strlen(trailer);
2772 data = xrealloc(data, len + trailer_len);
2773 memcpy(data + len, trailer, trailer_len);
2778 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2779 fprintf(stderr, "Error generating signature\n");
2785 b64encode(sig, sig, 64);
2788 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2789 fwrite(data, len, 1, stdout);
2795 static int cmd_verify(int argc, char *argv[]) {
2797 fprintf(stderr, "Not enough arguments!\n");
2802 fprintf(stderr, "Too many arguments!\n");
2806 char *node = argv[1];
2808 if(!strcmp(node, ".")) {
2810 name = get_my_name(true);
2818 } else if(!strcmp(node, "*")) {
2821 if(!check_id(node)) {
2822 fprintf(stderr, "Invalid node name\n");
2830 in = fopen(argv[2], "rb");
2833 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2841 char *data = readfile(in, &len);
2848 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2852 char *newline = memchr(data, '\n', len);
2854 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2855 fprintf(stderr, "Invalid input\n");
2861 size_t skip = newline - data;
2863 char signer[MAX_STRING_SIZE] = "";
2864 char sig[MAX_STRING_SIZE] = "";
2867 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2868 fprintf(stderr, "Invalid input\n");
2873 if(node && strcmp(node, signer)) {
2874 fprintf(stderr, "Signature is not made by %s\n", node);
2884 xasprintf(&trailer, " %s %ld", signer, t);
2885 int trailer_len = strlen(trailer);
2887 data = xrealloc(data, len + trailer_len);
2888 memcpy(data + len, trailer, trailer_len);
2891 newline = data + skip;
2893 char fname[PATH_MAX];
2894 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2895 FILE *fp = fopen(fname, "r");
2898 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2903 ecdsa_t *key = get_pubkey(fp);
2907 key = ecdsa_read_pem_public_key(fp);
2911 fprintf(stderr, "Could not read public key from %s\n", fname);
2919 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2920 fprintf(stderr, "Invalid signature\n");
2928 fwrite(newline, len - (newline - data), 1, stdout);
2934 static const struct {
2935 const char *command;
2936 int (*function)(int argc, char *argv[]);
2939 {"start", cmd_start},
2941 {"restart", cmd_restart},
2942 {"reload", cmd_reload},
2945 {"purge", cmd_purge},
2946 {"debug", cmd_debug},
2947 {"retry", cmd_retry},
2948 {"connect", cmd_connect},
2949 {"disconnect", cmd_disconnect},
2954 {"config", cmd_config, true},
2955 {"add", cmd_config},
2956 {"del", cmd_config},
2957 {"get", cmd_config},
2958 {"set", cmd_config},
2960 {"generate-keys", cmd_generate_keys},
2961 #ifndef DISABLE_LEGACY
2962 {"generate-rsa-keys", cmd_generate_rsa_keys},
2964 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2966 {"version", cmd_version},
2969 {"export", cmd_export},
2970 {"export-all", cmd_export_all},
2971 {"import", cmd_import},
2972 {"exchange", cmd_exchange},
2973 {"exchange-all", cmd_exchange_all},
2974 {"invite", cmd_invite},
2976 {"network", cmd_network},
2979 {"verify", cmd_verify},
2983 #ifdef HAVE_READLINE
2984 static char *complete_command(const char *text, int state) {
2993 while(commands[i].command) {
2994 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
2995 return xstrdup(commands[i].command);
3004 static char *complete_dump(const char *text, int state) {
3005 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3015 if(!strncasecmp(matches[i], text, strlen(text))) {
3016 return xstrdup(matches[i]);
3025 static char *complete_config(const char *text, int state) {
3034 while(variables[i].name) {
3035 char *dot = strchr(text, '.');
3038 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3040 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3044 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3045 return xstrdup(variables[i].name);
3055 static char *complete_info(const char *text, int state) {
3061 if(!connect_tincd(false)) {
3065 // Check the list of nodes
3066 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3067 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3070 while(recvline(fd, line, sizeof(line))) {
3072 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3085 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3089 if(!strncmp(item, text, strlen(text))) {
3090 return xstrdup(strip_weight(item));
3097 static char *complete_nothing(const char *text, int state) {
3101 static char **completion(const char *text, int start, int end) {
3102 char **matches = NULL;
3105 matches = rl_completion_matches(text, complete_command);
3106 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3107 matches = rl_completion_matches(text, complete_dump);
3108 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3109 matches = rl_completion_matches(text, complete_config);
3110 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3111 matches = rl_completion_matches(text, complete_config);
3112 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3113 matches = rl_completion_matches(text, complete_config);
3114 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3115 matches = rl_completion_matches(text, complete_config);
3116 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3117 matches = rl_completion_matches(text, complete_info);
3124 static int cmd_shell(int argc, char *argv[]) {
3125 xasprintf(&prompt, "%s> ", identname);
3129 int maxargs = argc + 16;
3130 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3132 for(int i = 0; i < argc; i++) {
3136 #ifdef HAVE_READLINE
3137 rl_readline_name = "tinc";
3138 rl_completion_entry_function = complete_nothing;
3139 rl_attempted_completion_function = completion;
3140 rl_filename_completion_desired = 0;
3145 #ifdef HAVE_READLINE
3150 rl_basic_word_break_characters = "\t\n ";
3151 line = readline(prompt);
3154 copy = xstrdup(line);
3157 line = fgets(buf, sizeof(buf), stdin);
3163 fputs(prompt, stdout);
3166 line = fgets(buf, sizeof(buf), stdin);
3173 /* Ignore comments */
3182 char *p = line + strspn(line, " \t\n");
3183 char *next = strtok(p, " \t\n");
3186 if(nargc >= maxargs) {
3188 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3193 next = strtok(NULL, " \t\n");
3200 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3207 for(int i = 0; commands[i].command; i++) {
3208 if(!strcasecmp(nargv[argc], commands[i].command)) {
3209 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3215 #ifdef HAVE_READLINE
3224 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3239 int main(int argc, char *argv[]) {
3240 program_name = argv[0];
3243 tty = isatty(0) && isatty(1);
3245 if(!parse_options(argc, argv)) {
3250 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3251 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3264 static struct WSAData wsa_state;
3266 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3267 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
3276 if(optind >= argc) {
3277 return cmd_shell(argc, argv);
3280 for(int i = 0; commands[i].command; i++) {
3281 if(!strcasecmp(argv[optind], commands[i].command)) {
3282 return commands[i].function(argc - optind, argv + optind);
3286 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);