2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2017 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-2017 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};
726 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
729 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
733 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
736 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
740 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
741 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
743 fprintf(stderr, "%s service stopped\n", identname);
746 if(!DeleteService(service)) {
747 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
751 CloseServiceHandle(service);
753 CloseServiceHandle(manager);
754 fprintf(stderr, "%s service removed\n", identname);
760 bool connect_tincd(bool verbose) {
765 struct timeval tv = {0, 0};
767 if(select(fd + 1, &r, NULL, NULL, &tv)) {
768 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
776 FILE *f = fopen(pidfilename, "r");
780 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
789 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
791 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
802 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
803 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
804 /* clean up the stale socket and pid file */
806 unlink(unixsocketname);
810 struct sockaddr_un sa;
812 sa.sun_family = AF_UNIX;
814 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
816 fd = socket(AF_UNIX, SOCK_STREAM, 0);
820 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
826 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
828 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
837 struct addrinfo hints = {
838 .ai_family = AF_UNSPEC,
839 .ai_socktype = SOCK_STREAM,
840 .ai_protocol = IPPROTO_TCP,
844 struct addrinfo *res = NULL;
846 if(getaddrinfo(host, port, &hints, &res) || !res) {
848 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
854 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
858 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
865 unsigned long arg = 0;
867 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
869 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
875 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
877 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
889 static const int one = 1;
890 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
896 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
898 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
906 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
908 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
910 fprintf(stderr, "Could not fully establish control socket connection\n");
922 static int cmd_start(int argc, char *argv[]) {
923 if(connect_tincd(false)) {
925 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
927 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
934 char *slash = strrchr(program_name, '/');
938 if((c = strrchr(program_name, '\\')) > slash) {
945 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
951 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
956 Windows has no real concept of an "argv array". A command line is just one string.
957 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
958 it uses quotes to handle spaces in arguments.
959 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
960 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
961 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
963 xasprintf(&arg0, "\"%s\"", arg0);
965 nargv[nargc++] = arg0;
967 for(int i = 1; i < optind; i++) {
968 nargv[nargc++] = orig_argv[i];
971 for(int i = 1; i < argc; i++) {
972 nargv[nargc++] = argv[i];
976 int status = spawnvp(_P_WAIT, c, nargv);
979 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
985 int pfd[2] = {-1, -1};
987 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
988 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
996 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1004 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1005 setenv("TINC_UMBILICAL", buf, true);
1006 exit(execvp(c, nargv));
1013 int status = -1, result;
1015 signal(SIGINT, SIG_IGN);
1018 // Pass all log messages from the umbilical to stderr.
1019 // A nul-byte right before closure means tincd started successfully.
1020 bool failure = true;
1024 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1025 failure = buf[len - 1];
1040 // Make sure the child process is really gone.
1041 result = waitpid(pid, &status, 0);
1044 signal(SIGINT, SIG_DFL);
1047 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1048 fprintf(stderr, "Error starting %s\n", c);
1056 static int cmd_stop(int argc, char *argv[]) {
1058 fprintf(stderr, "Too many arguments!\n");
1064 if(!connect_tincd(true)) {
1066 if(kill(pid, SIGTERM)) {
1067 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1071 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1072 waitpid(pid, NULL, 0);
1079 sendline(fd, "%d %d", CONTROL, REQ_STOP);
1081 while(recvline(fd, line, sizeof(line))) {
1082 // Wait for tincd to close the connection...
1087 if(!remove_service()) {
1099 static int cmd_restart(int argc, char *argv[]) {
1101 return cmd_start(argc, argv);
1104 static int cmd_reload(int argc, char *argv[]) {
1106 fprintf(stderr, "Too many arguments!\n");
1110 if(!connect_tincd(true)) {
1114 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1116 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1117 fprintf(stderr, "Could not reload configuration.\n");
1125 static int dump_invitations(void) {
1126 char dname[PATH_MAX];
1127 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1128 DIR *dir = opendir(dname);
1131 if(errno == ENOENT) {
1132 fprintf(stderr, "No outstanding invitations.\n");
1136 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1144 while((ent = readdir(dir))) {
1145 char buf[MAX_STRING_SIZE];
1147 if(b64decode(ent->d_name, buf, 24) != 18) {
1151 char fname[PATH_MAX];
1152 snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name);
1153 FILE *f = fopen(fname, "r");
1156 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1162 if(!fgets(buf, sizeof(buf), f)) {
1163 fprintf(stderr, "Invalid invitation file %s\n", fname);
1170 char *eol = buf + strlen(buf);
1172 while(strchr("\t \r\n", *--eol)) {
1176 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1177 fprintf(stderr, "Invalid invitation file %s\n", fname);
1182 printf("%s %s\n", ent->d_name, buf + 7);
1188 fprintf(stderr, "No outstanding invitations.\n");
1194 static int cmd_dump(int argc, char *argv[]) {
1195 bool only_reachable = false;
1197 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1198 if(strcasecmp(argv[2], "nodes")) {
1199 fprintf(stderr, "`reachable' only supported for nodes.\n");
1204 only_reachable = true;
1210 fprintf(stderr, "Invalid number of arguments.\n");
1215 if(!strcasecmp(argv[1], "invitations")) {
1216 return dump_invitations();
1219 if(!connect_tincd(true)) {
1225 if(!strcasecmp(argv[1], "nodes")) {
1226 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1227 } else if(!strcasecmp(argv[1], "edges")) {
1228 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1229 } else if(!strcasecmp(argv[1], "subnets")) {
1230 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1231 } else if(!strcasecmp(argv[1], "connections")) {
1232 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1233 } else if(!strcasecmp(argv[1], "graph")) {
1234 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1235 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1237 } else if(!strcasecmp(argv[1], "digraph")) {
1238 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1239 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1242 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1248 printf("graph {\n");
1249 } else if(do_graph == 2) {
1250 printf("digraph {\n");
1253 while(recvline(fd, line, sizeof(line))) {
1254 char node1[4096], node2[4096];
1255 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1258 if(do_graph && req == REQ_DUMP_NODES) {
1280 char local_host[4096];
1281 char local_port[4096];
1284 int cipher, digest, maclength, compression, distance, socket, weight;
1285 short int pmtu, minmtu, maxmtu;
1286 unsigned int options, status_int;
1287 node_status_t status;
1288 long int last_state_change;
1291 case REQ_DUMP_NODES: {
1292 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %d %d %d %d %x %x %4095s %4095s %d %hd %hd %hd %ld", node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
1295 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1299 memcpy(&status, &status_int, sizeof(status));
1302 const char *color = "black";
1304 if(!strcmp(host, "MYSELF")) {
1306 } else if(!status.reachable) {
1308 } else if(strcmp(via, node)) {
1310 } else if(!status.validkey) {
1312 } else if(minmtu > 0) {
1316 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1318 if(only_reachable && !status.reachable) {
1322 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)\n",
1323 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1328 case REQ_DUMP_EDGES: {
1329 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);
1332 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1337 float w = 1 + 65536.0 / weight;
1339 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1340 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1341 } else if(do_graph == 2) {
1342 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1345 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);
1350 case REQ_DUMP_SUBNETS: {
1351 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1354 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1358 printf("%s owner %s\n", strip_weight(subnet), node);
1362 case REQ_DUMP_CONNECTIONS: {
1363 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1366 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1370 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1375 fprintf(stderr, "Unable to parse dump from tincd.\n");
1380 fprintf(stderr, "Error receiving dump.\n");
1384 static int cmd_purge(int argc, char *argv[]) {
1386 fprintf(stderr, "Too many arguments!\n");
1390 if(!connect_tincd(true)) {
1394 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1396 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1397 fprintf(stderr, "Could not purge old information.\n");
1404 static int cmd_debug(int argc, char *argv[]) {
1406 fprintf(stderr, "Invalid number of arguments.\n");
1410 if(!connect_tincd(true)) {
1414 int debuglevel = atoi(argv[1]);
1417 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1419 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1420 fprintf(stderr, "Could not set debug level.\n");
1424 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1428 static int cmd_retry(int argc, char *argv[]) {
1430 fprintf(stderr, "Too many arguments!\n");
1434 if(!connect_tincd(true)) {
1438 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1440 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1441 fprintf(stderr, "Could not retry outgoing connections.\n");
1448 static int cmd_connect(int argc, char *argv[]) {
1450 fprintf(stderr, "Invalid number of arguments.\n");
1454 if(!check_id(argv[1])) {
1455 fprintf(stderr, "Invalid name for node.\n");
1459 if(!connect_tincd(true)) {
1463 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1465 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1466 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1473 static int cmd_disconnect(int argc, char *argv[]) {
1475 fprintf(stderr, "Invalid number of arguments.\n");
1479 if(!check_id(argv[1])) {
1480 fprintf(stderr, "Invalid name for node.\n");
1484 if(!connect_tincd(true)) {
1488 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1490 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1491 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1498 static int cmd_top(int argc, char *argv[]) {
1500 fprintf(stderr, "Too many arguments!\n");
1506 if(!connect_tincd(true)) {
1513 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1518 static int cmd_pcap(int argc, char *argv[]) {
1520 fprintf(stderr, "Too many arguments!\n");
1524 if(!connect_tincd(true)) {
1528 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1533 static void sigint_handler(int sig) {
1534 fprintf(stderr, "\n");
1535 shutdown(fd, SHUT_RDWR);
1539 static int cmd_log(int argc, char *argv[]) {
1541 fprintf(stderr, "Too many arguments!\n");
1545 if(!connect_tincd(true)) {
1550 signal(SIGINT, sigint_handler);
1553 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1556 signal(SIGINT, SIG_DFL);
1564 static int cmd_pid(int argc, char *argv[]) {
1566 fprintf(stderr, "Too many arguments!\n");
1570 if(!connect_tincd(true) || !pid) {
1574 printf("%d\n", pid);
1578 int rstrip(char *value) {
1579 int len = strlen(value);
1581 while(len && strchr("\t\r\n ", value[len - 1])) {
1588 char *get_my_name(bool verbose) {
1589 FILE *f = fopen(tinc_conf, "r");
1593 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1602 while(fgets(buf, sizeof(buf), f)) {
1603 int len = strcspn(buf, "\t =");
1605 value += strspn(value, "\t ");
1609 value += strspn(value, "\t ");
1612 if(!rstrip(value)) {
1618 if(strcasecmp(buf, "Name")) {
1624 return replace_name(value);
1631 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1637 ecdsa_t *get_pubkey(FILE *f) {
1641 while(fgets(buf, sizeof(buf), f)) {
1642 int len = strcspn(buf, "\t =");
1644 value += strspn(value, "\t ");
1648 value += strspn(value, "\t ");
1651 if(!rstrip(value)) {
1657 if(strcasecmp(buf, "Ed25519PublicKey")) {
1662 return ecdsa_set_base64_public_key(value);
1669 const var_t variables[] = {
1670 /* Server configuration */
1671 {"AddressFamily", VAR_SERVER},
1672 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1673 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1674 {"BindToInterface", VAR_SERVER},
1675 {"Broadcast", VAR_SERVER | VAR_SAFE},
1676 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1677 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1678 {"DecrementTTL", VAR_SERVER},
1679 {"Device", VAR_SERVER},
1680 {"DeviceStandby", VAR_SERVER},
1681 {"DeviceType", VAR_SERVER},
1682 {"DirectOnly", VAR_SERVER},
1683 {"Ed25519PrivateKeyFile", VAR_SERVER},
1684 {"ExperimentalProtocol", VAR_SERVER},
1685 {"Forwarding", VAR_SERVER},
1686 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1687 {"Hostnames", VAR_SERVER},
1688 {"IffOneQueue", VAR_SERVER},
1689 {"Interface", VAR_SERVER},
1690 {"InvitationExpire", VAR_SERVER},
1691 {"KeyExpire", VAR_SERVER},
1692 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1693 {"LocalDiscovery", VAR_SERVER},
1694 {"LogLevel", VAR_SERVER},
1695 {"MACExpire", VAR_SERVER},
1696 {"MaxConnectionBurst", VAR_SERVER},
1697 {"MaxOutputBufferSize", VAR_SERVER},
1698 {"MaxTimeout", VAR_SERVER},
1699 {"Mode", VAR_SERVER | VAR_SAFE},
1700 {"Name", VAR_SERVER},
1701 {"PingInterval", VAR_SERVER},
1702 {"PingTimeout", VAR_SERVER},
1703 {"PriorityInheritance", VAR_SERVER},
1704 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1705 {"PrivateKeyFile", VAR_SERVER},
1706 {"ProcessPriority", VAR_SERVER},
1707 {"Proxy", VAR_SERVER},
1708 {"ReplayWindow", VAR_SERVER},
1709 {"ScriptsExtension", VAR_SERVER},
1710 {"ScriptsInterpreter", VAR_SERVER},
1711 {"StrictSubnets", VAR_SERVER},
1712 {"TunnelServer", VAR_SERVER},
1713 {"UDPDiscovery", VAR_SERVER},
1714 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1715 {"UDPDiscoveryInterval", VAR_SERVER},
1716 {"UDPDiscoveryTimeout", VAR_SERVER},
1717 {"MTUInfoInterval", VAR_SERVER},
1718 {"UDPInfoInterval", VAR_SERVER},
1719 {"UDPRcvBuf", VAR_SERVER},
1720 {"UDPSndBuf", VAR_SERVER},
1721 {"UPnP", VAR_SERVER},
1722 {"UPnPDiscoverWait", VAR_SERVER},
1723 {"UPnPRefreshPeriod", VAR_SERVER},
1724 {"VDEGroup", VAR_SERVER},
1725 {"VDEPort", VAR_SERVER},
1726 /* Host configuration */
1727 {"Address", VAR_HOST | VAR_MULTIPLE},
1728 {"Cipher", VAR_SERVER | VAR_HOST},
1729 {"ClampMSS", VAR_SERVER | VAR_HOST},
1730 {"Compression", VAR_SERVER | VAR_HOST},
1731 {"Digest", VAR_SERVER | VAR_HOST},
1732 {"Ed25519PublicKey", VAR_HOST},
1733 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1734 {"IndirectData", VAR_SERVER | VAR_HOST},
1735 {"MACLength", VAR_SERVER | VAR_HOST},
1736 {"PMTU", VAR_SERVER | VAR_HOST},
1737 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1739 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1740 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1741 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1742 {"TCPOnly", VAR_SERVER | VAR_HOST},
1743 {"Weight", VAR_HOST | VAR_SAFE},
1747 static int cmd_config(int argc, char *argv[]) {
1749 fprintf(stderr, "Invalid number of arguments.\n");
1753 if(strcasecmp(argv[0], "config")) {
1759 if(!strcasecmp(argv[1], "get")) {
1761 } else if(!strcasecmp(argv[1], "add")) {
1762 argv++, argc--, action = 1;
1763 } else if(!strcasecmp(argv[1], "del")) {
1764 argv++, argc--, action = -1;
1765 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1766 argv++, argc--, action = 0;
1770 fprintf(stderr, "Invalid number of arguments.\n");
1774 // Concatenate the rest of the command line
1775 strncpy(line, argv[1], sizeof(line) - 1);
1777 for(int i = 2; i < argc; i++) {
1778 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1779 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1782 // Liberal parsing into node name, variable name and value.
1788 len = strcspn(line, "\t =");
1790 value += strspn(value, "\t ");
1794 value += strspn(value, "\t ");
1798 variable = strchr(line, '.');
1808 fprintf(stderr, "No variable given.\n");
1812 if(action >= 0 && !*value) {
1813 fprintf(stderr, "No value for variable given.\n");
1817 if(action < -1 && *value) {
1821 /* Some simple checks. */
1823 bool warnonremove = false;
1825 for(int i = 0; variables[i].name; i++) {
1826 if(strcasecmp(variables[i].name, variable)) {
1831 variable = (char *)variables[i].name;
1833 /* Discourage use of obsolete variables. */
1835 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1837 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1839 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1844 /* Don't put server variables in host config files */
1846 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1848 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1850 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1855 /* Should this go into our own host config file? */
1857 if(!node && !(variables[i].type & VAR_SERVER)) {
1858 node = get_my_name(true);
1865 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1866 Turn on warnings when it seems variables might be removed unintentionally. */
1868 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1869 warnonremove = true;
1871 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1872 warnonremove = true;
1878 if(node && !check_id(node)) {
1879 fprintf(stderr, "Invalid name for node.\n");
1884 if(force || action < 0) {
1885 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1887 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1892 // Open the right configuration file.
1893 char filename[PATH_MAX];
1896 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1898 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1901 FILE *f = fopen(filename, "r");
1904 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1908 char tmpfile[PATH_MAX];
1912 snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename);
1913 tf = fopen(tmpfile, "w");
1916 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1922 // Copy the file, making modifications on the fly, unless we are just getting a value.
1926 bool removed = false;
1929 while(fgets(buf1, sizeof(buf1), f)) {
1930 buf1[sizeof(buf1) - 1] = 0;
1931 strncpy(buf2, buf1, sizeof(buf2));
1933 // Parse line in a simple way
1937 len = strcspn(buf2, "\t =");
1938 bvalue = buf2 + len;
1939 bvalue += strspn(bvalue, "\t ");
1941 if(*bvalue == '=') {
1943 bvalue += strspn(bvalue, "\t ");
1950 if(!strcasecmp(buf2, variable)) {
1954 printf("%s\n", bvalue);
1956 } else if(action == -1) {
1957 if(!*value || !strcasecmp(bvalue, value)) {
1963 } else if(action == 0) {
1964 // Warn if "set" was used for variables that can occur multiple times
1965 if(warnonremove && strcasecmp(bvalue, value)) {
1966 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1969 // Already set? Delete the rest...
1974 // Otherwise, replace.
1975 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1976 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1983 } else if(action > 0) {
1984 // Check if we've already seen this variable with the same value
1985 if(!strcasecmp(bvalue, value)) {
1992 // Copy original line...
1993 if(fputs(buf1, tf) < 0) {
1994 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1998 // Add newline if it is missing...
1999 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2000 if(fputc('\n', tf) < 0) {
2001 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2008 // Make sure we read everything...
2009 if(ferror(f) || !feof(f)) {
2010 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2015 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2019 // Add new variable if necessary.
2020 if((action > 0 && !found) || (action == 0 && !set)) {
2021 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2022 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2031 fprintf(stderr, "No matching configuration variables found.\n");
2036 // Make sure we wrote everything...
2038 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2042 // Could we find what we had to remove?
2043 if(action < 0 && !removed) {
2045 fprintf(stderr, "No configuration variables deleted.\n");
2049 // Replace the configuration file with the new one
2052 if(remove(filename)) {
2053 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2059 if(rename(tmpfile, filename)) {
2060 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2064 // Silently try notifying a running tincd of changes.
2065 if(connect_tincd(false)) {
2066 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2072 static bool try_bind(int port) {
2073 struct addrinfo *ai = NULL, *aip;
2074 struct addrinfo hint = {
2075 .ai_flags = AI_PASSIVE,
2076 .ai_family = AF_UNSPEC,
2077 .ai_socktype = SOCK_STREAM,
2078 .ai_protocol = IPPROTO_TCP,
2081 bool success = true;
2083 snprintf(portstr, sizeof(portstr), "%d", port);
2085 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2089 for(aip = ai; aip; aip = aip->ai_next) {
2090 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2097 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2110 int check_port(char *name) {
2115 fprintf(stderr, "Warning: could not bind to port 655. ");
2117 for(int i = 0; i < 100; i++) {
2118 int port = 0x1000 + (rand() & 0x7fff);
2120 if(try_bind(port)) {
2121 char filename[PATH_MAX];
2122 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2123 FILE *f = fopen(filename, "a");
2126 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2127 fprintf(stderr, "Please change tinc's Port manually.\n");
2131 fprintf(f, "Port = %d\n", port);
2133 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2138 fprintf(stderr, "Please change tinc's Port manually.\n");
2142 static int cmd_init(int argc, char *argv[]) {
2143 if(!access(tinc_conf, F_OK)) {
2144 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2149 fprintf(stderr, "Too many arguments!\n");
2151 } else if(argc < 2) {
2154 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2156 if(!fgets(buf, sizeof(buf), stdin)) {
2157 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2161 int len = rstrip(buf);
2164 fprintf(stderr, "No name given!\n");
2170 fprintf(stderr, "No Name given!\n");
2174 name = strdup(argv[1]);
2177 fprintf(stderr, "No Name given!\n");
2182 if(!check_id(name)) {
2183 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2187 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2188 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2192 if(mkdir(confbase, 0777) && errno != EEXIST) {
2193 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2197 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2198 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2202 FILE *f = fopen(tinc_conf, "w");
2205 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2209 fprintf(f, "Name = %s\n", name);
2212 #ifndef DISABLE_LEGACY
2214 if(!rsa_keygen(2048, false)) {
2220 if(!ed25519_keygen(false)) {
2227 char filename[PATH_MAX];
2228 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2230 if(access(filename, F_OK)) {
2231 FILE *f = fopenmask(filename, "w", 0777);
2234 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2238 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");
2248 static int cmd_generate_keys(int argc, char *argv[]) {
2249 #ifdef DISABLE_LEGACY
2256 fprintf(stderr, "Too many arguments!\n");
2261 name = get_my_name(false);
2264 #ifndef DISABLE_LEGACY
2266 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2272 if(!ed25519_keygen(true)) {
2279 #ifndef DISABLE_LEGACY
2280 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2282 fprintf(stderr, "Too many arguments!\n");
2287 name = get_my_name(false);
2290 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2294 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2296 fprintf(stderr, "Too many arguments!\n");
2301 name = get_my_name(false);
2304 return !ed25519_keygen(true);
2307 static int cmd_help(int argc, char *argv[]) {
2312 static int cmd_version(int argc, char *argv[]) {
2314 fprintf(stderr, "Too many arguments!\n");
2322 static int cmd_info(int argc, char *argv[]) {
2324 fprintf(stderr, "Invalid number of arguments.\n");
2328 if(!connect_tincd(true)) {
2332 return info(fd, argv[1]);
2335 static const char *conffiles[] = {
2346 static int cmd_edit(int argc, char *argv[]) {
2348 fprintf(stderr, "Invalid number of arguments.\n");
2352 char filename[PATH_MAX] = "";
2354 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2355 for(int i = 0; conffiles[i]; i++) {
2356 if(!strcmp(argv[1], conffiles[i])) {
2357 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2366 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2367 char *dash = strchr(argv[1], '-');
2372 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2373 fprintf(stderr, "Invalid configuration filename.\n");
2381 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ? : getenv("EDITOR") ? : "vi", filename);
2383 xasprintf(&command, "edit \"%s\"", filename);
2385 int result = system(command);
2392 // Silently try notifying a running tincd of changes.
2393 if(connect_tincd(false)) {
2394 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2400 static int export(const char *name, FILE *out) {
2401 char filename[PATH_MAX];
2402 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2403 FILE *in = fopen(filename, "r");
2406 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2410 fprintf(out, "Name = %s\n", name);
2413 while(fgets(buf, sizeof(buf), in)) {
2414 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2420 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2429 static int cmd_export(int argc, char *argv[]) {
2431 fprintf(stderr, "Too many arguments!\n");
2435 char *name = get_my_name(true);
2441 int result = export(name, stdout);
2451 static int cmd_export_all(int argc, char *argv[]) {
2453 fprintf(stderr, "Too many arguments!\n");
2457 DIR *dir = opendir(hosts_dir);
2460 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2468 while((ent = readdir(dir))) {
2469 if(!check_id(ent->d_name)) {
2476 printf("#---------------------------------------------------------------#\n");
2479 result |= export(ent->d_name, stdout);
2491 static int cmd_import(int argc, char *argv[]) {
2493 fprintf(stderr, "Too many arguments!\n");
2502 char filename[PATH_MAX] = "";
2504 bool firstline = true;
2506 while(fgets(buf, sizeof(buf), in)) {
2507 if(sscanf(buf, "Name = %4095s", name) == 1) {
2510 if(!check_id(name)) {
2511 fprintf(stderr, "Invalid Name in input!\n");
2519 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2521 if(!force && !access(filename, F_OK)) {
2522 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2527 out = fopen(filename, "w");
2530 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2536 } else if(firstline) {
2537 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2542 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2547 if(fputs(buf, out) < 0) {
2548 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2559 fprintf(stderr, "Imported %d host configuration files.\n", count);
2562 fprintf(stderr, "No host configuration files imported.\n");
2567 static int cmd_exchange(int argc, char *argv[]) {
2568 return cmd_export(argc, argv) ? : cmd_import(argc, argv);
2571 static int cmd_exchange_all(int argc, char *argv[]) {
2572 return cmd_export_all(argc, argv) ? : cmd_import(argc, argv);
2575 static int switch_network(char *name) {
2576 if(strcmp(name, ".")) {
2577 if(!check_netname(name, false)) {
2578 fprintf(stderr, "Invalid character in netname!\n");
2582 if(!check_netname(name, true)) {
2583 fprintf(stderr, "Warning: unsafe character in netname!\n");
2593 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2600 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2601 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2602 xasprintf(&prompt, "%s> ", identname);
2607 static int cmd_network(int argc, char *argv[]) {
2609 fprintf(stderr, "Too many arguments!\n");
2614 return switch_network(argv[1]);
2617 DIR *dir = opendir(confdir);
2620 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2626 while((ent = readdir(dir))) {
2627 if(*ent->d_name == '.') {
2631 if(!strcmp(ent->d_name, "tinc.conf")) {
2636 char fname[PATH_MAX];
2637 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2639 if(!access(fname, R_OK)) {
2640 printf("%s\n", ent->d_name);
2649 static int cmd_fsck(int argc, char *argv[]) {
2651 fprintf(stderr, "Too many arguments!\n");
2655 return fsck(orig_argv[0]);
2658 static void *readfile(FILE *in, size_t *len) {
2660 size_t alloced = 4096;
2661 char *buf = xmalloc(alloced);
2664 size_t read = fread(buf + count, 1, alloced - count, in);
2672 if(count >= alloced) {
2674 buf = xrealloc(buf, alloced);
2685 static int cmd_sign(int argc, char *argv[]) {
2687 fprintf(stderr, "Too many arguments!\n");
2692 name = get_my_name(true);
2699 char fname[PATH_MAX];
2700 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2701 FILE *fp = fopen(fname, "r");
2704 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2708 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2711 fprintf(stderr, "Could not read private key from %s\n", fname);
2721 in = fopen(argv[1], "rb");
2724 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2733 char *data = readfile(in, &len);
2740 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2745 // Ensure we sign our name and current time as well
2746 long t = time(NULL);
2748 xasprintf(&trailer, " %s %ld", name, t);
2749 int trailer_len = strlen(trailer);
2751 data = xrealloc(data, len + trailer_len);
2752 memcpy(data + len, trailer, trailer_len);
2757 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2758 fprintf(stderr, "Error generating signature\n");
2764 b64encode(sig, sig, 64);
2767 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2768 fwrite(data, len, 1, stdout);
2774 static int cmd_verify(int argc, char *argv[]) {
2776 fprintf(stderr, "Not enough arguments!\n");
2781 fprintf(stderr, "Too many arguments!\n");
2785 char *node = argv[1];
2787 if(!strcmp(node, ".")) {
2789 name = get_my_name(true);
2797 } else if(!strcmp(node, "*")) {
2800 if(!check_id(node)) {
2801 fprintf(stderr, "Invalid node name\n");
2809 in = fopen(argv[2], "rb");
2812 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2820 char *data = readfile(in, &len);
2827 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2831 char *newline = memchr(data, '\n', len);
2833 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2834 fprintf(stderr, "Invalid input\n");
2840 size_t skip = newline - data;
2842 char signer[MAX_STRING_SIZE] = "";
2843 char sig[MAX_STRING_SIZE] = "";
2846 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2847 fprintf(stderr, "Invalid input\n");
2852 if(node && strcmp(node, signer)) {
2853 fprintf(stderr, "Signature is not made by %s\n", node);
2863 xasprintf(&trailer, " %s %ld", signer, t);
2864 int trailer_len = strlen(trailer);
2866 data = xrealloc(data, len + trailer_len);
2867 memcpy(data + len, trailer, trailer_len);
2870 newline = data + skip;
2872 char fname[PATH_MAX];
2873 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2874 FILE *fp = fopen(fname, "r");
2877 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2882 ecdsa_t *key = get_pubkey(fp);
2886 key = ecdsa_read_pem_public_key(fp);
2890 fprintf(stderr, "Could not read public key from %s\n", fname);
2898 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2899 fprintf(stderr, "Invalid signature\n");
2907 fwrite(newline, len - (newline - data), 1, stdout);
2913 static const struct {
2914 const char *command;
2915 int (*function)(int argc, char *argv[]);
2918 {"start", cmd_start},
2920 {"restart", cmd_restart},
2921 {"reload", cmd_reload},
2924 {"purge", cmd_purge},
2925 {"debug", cmd_debug},
2926 {"retry", cmd_retry},
2927 {"connect", cmd_connect},
2928 {"disconnect", cmd_disconnect},
2933 {"config", cmd_config, true},
2934 {"add", cmd_config},
2935 {"del", cmd_config},
2936 {"get", cmd_config},
2937 {"set", cmd_config},
2939 {"generate-keys", cmd_generate_keys},
2940 #ifndef DISABLE_LEGACY
2941 {"generate-rsa-keys", cmd_generate_rsa_keys},
2943 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2945 {"version", cmd_version},
2948 {"export", cmd_export},
2949 {"export-all", cmd_export_all},
2950 {"import", cmd_import},
2951 {"exchange", cmd_exchange},
2952 {"exchange-all", cmd_exchange_all},
2953 {"invite", cmd_invite},
2955 {"network", cmd_network},
2958 {"verify", cmd_verify},
2962 #ifdef HAVE_READLINE
2963 static char *complete_command(const char *text, int state) {
2972 while(commands[i].command) {
2973 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
2974 return xstrdup(commands[i].command);
2983 static char *complete_dump(const char *text, int state) {
2984 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2994 if(!strncasecmp(matches[i], text, strlen(text))) {
2995 return xstrdup(matches[i]);
3004 static char *complete_config(const char *text, int state) {
3013 while(variables[i].name) {
3014 char *dot = strchr(text, '.');
3017 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3019 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3023 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3024 return xstrdup(variables[i].name);
3034 static char *complete_info(const char *text, int state) {
3040 if(!connect_tincd(false)) {
3044 // Check the list of nodes
3045 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3046 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3049 while(recvline(fd, line, sizeof(line))) {
3051 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3064 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3068 if(!strncmp(item, text, strlen(text))) {
3069 return xstrdup(strip_weight(item));
3076 static char *complete_nothing(const char *text, int state) {
3080 static char **completion(const char *text, int start, int end) {
3081 char **matches = NULL;
3084 matches = rl_completion_matches(text, complete_command);
3085 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3086 matches = rl_completion_matches(text, complete_dump);
3087 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3088 matches = rl_completion_matches(text, complete_config);
3089 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3090 matches = rl_completion_matches(text, complete_config);
3091 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3092 matches = rl_completion_matches(text, complete_config);
3093 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3094 matches = rl_completion_matches(text, complete_config);
3095 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3096 matches = rl_completion_matches(text, complete_info);
3103 static int cmd_shell(int argc, char *argv[]) {
3104 xasprintf(&prompt, "%s> ", identname);
3108 int maxargs = argc + 16;
3109 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3111 for(int i = 0; i < argc; i++) {
3115 #ifdef HAVE_READLINE
3116 rl_readline_name = "tinc";
3117 rl_completion_entry_function = complete_nothing;
3118 rl_attempted_completion_function = completion;
3119 rl_filename_completion_desired = 0;
3124 #ifdef HAVE_READLINE
3129 rl_basic_word_break_characters = "\t\n ";
3130 line = readline(prompt);
3133 copy = xstrdup(line);
3136 line = fgets(buf, sizeof(buf), stdin);
3142 fputs(prompt, stdout);
3145 line = fgets(buf, sizeof(buf), stdin);
3152 /* Ignore comments */
3161 char *p = line + strspn(line, " \t\n");
3162 char *next = strtok(p, " \t\n");
3165 if(nargc >= maxargs) {
3167 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3172 next = strtok(NULL, " \t\n");
3179 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3186 for(int i = 0; commands[i].command; i++) {
3187 if(!strcasecmp(nargv[argc], commands[i].command)) {
3188 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3194 #ifdef HAVE_READLINE
3203 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3218 int main(int argc, char *argv[]) {
3219 program_name = argv[0];
3222 tty = isatty(0) && isatty(1);
3224 if(!parse_options(argc, argv)) {
3229 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3230 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3243 static struct WSAData wsa_state;
3245 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3246 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
3255 if(optind >= argc) {
3256 return cmd_shell(argc, argv);
3259 for(int i = 0; commands[i].command; i++) {
3260 if(!strcasecmp(argv[optind], commands[i].command)) {
3261 return commands[i].function(argc - optind, argv + optind);
3265 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);