2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2021 Guus Sliepen <guus@tinc-vpn.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "readline/readline.h"
26 #include "readline/history.h"
31 #include "control_common.h"
36 #include "invitation.h"
47 #define MSG_NOSIGNAL 0
50 static char **orig_argv;
53 /* If nonzero, display usage information and exit. */
54 static bool show_help = false;
56 /* If nonzero, print the version on standard output and exit. */
57 static bool show_version = false;
59 static char *name = NULL;
60 static char controlcookie[1025];
61 char *tinc_conf = NULL;
62 char *hosts_dir = NULL;
65 // Horrible global variables...
74 bool confbasegiven = false;
75 bool netnamegiven = false;
76 char *scriptinterpreter = NULL;
77 char *scriptextension = "";
83 static struct option const long_options[] = {
84 {"batch", no_argument, NULL, 'b'},
85 {"config", required_argument, NULL, 'c'},
86 {"net", required_argument, NULL, 'n'},
87 {"help", no_argument, NULL, 1},
88 {"version", no_argument, NULL, 2},
89 {"pidfile", required_argument, NULL, 3},
90 {"force", no_argument, NULL, 4},
94 static void version(void) {
95 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
96 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
104 #ifndef DISABLE_LEGACY
108 printf("Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
109 "See the AUTHORS file for a complete list.\n\n"
110 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
111 "and you are welcome to redistribute it under certain conditions;\n"
112 "see the file COPYING for details.\n");
115 static void usage(bool status) {
117 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
119 printf("Usage: %s [options] command\n\n", program_name);
120 printf("Valid options are:\n"
121 " -b, --batch Don't ask for anything (non-interactive mode).\n"
122 " -c, --config=DIR Read configuration options from DIR.\n"
123 " -n, --net=NETNAME Connect to net NETNAME.\n"
124 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
125 " --force Force some commands to work despite warnings.\n"
126 " --help Display this help and exit.\n"
127 " --version Output version information and exit.\n"
129 "Valid commands are:\n"
130 " init [name] Create initial configuration files.\n"
131 " get VARIABLE Print current value of VARIABLE\n"
132 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
133 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
134 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
135 " start [tincd options] Start tincd.\n"
136 " stop Stop tincd.\n"
137 " restart [tincd options] Restart tincd.\n"
138 " reload Partially reload configuration of running tincd.\n"
139 " pid Show PID of currently running tincd.\n"
140 #ifdef DISABLE_LEGACY
141 " generate-keys Generate a new Ed25519 public/private key pair.\n"
143 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
144 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
146 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
147 " dump Dump a list of one of the following things:\n"
148 " [reachable] nodes - all known nodes in the VPN\n"
149 " edges - all known connections in the VPN\n"
150 " subnets - all known subnets in the VPN\n"
151 " connections - all meta connections with ourself\n"
152 " [di]graph - graph of the VPN in dotty format\n"
153 " invitations - outstanding invitations\n"
154 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
155 " purge Purge unreachable nodes\n"
156 " debug N Set debug level\n"
157 " retry Retry all outgoing connections\n"
158 " disconnect NODE Close meta connection with NODE\n"
160 " top Show real-time statistics\n"
162 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
163 " log [level] Dump log output [up to the specified level]\n"
164 " export Export host configuration of local node to standard output\n"
165 " export-all Export all host configuration files to standard output\n"
166 " import Import host configuration file(s) from standard input\n"
167 " exchange Same as export followed by import\n"
168 " exchange-all Same as export-all followed by import\n"
169 " invite NODE [...] Generate an invitation for NODE\n"
170 " join INVITATION Join a VPN using an INVITATION\n"
171 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
172 " fsck Check the configuration files for problems.\n"
173 " sign [FILE] Generate a signed version of a file.\n"
174 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
176 printf("Report bugs to tinc@tinc-vpn.org.\n");
180 static bool parse_options(int argc, char **argv) {
182 int option_index = 0;
184 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
186 case 0: /* long option */
193 case 'c': /* config file */
195 confbase = xstrdup(optarg);
196 confbasegiven = true;
199 case 'n': /* net name given */
201 netname = xstrdup(optarg);
204 case 1: /* show help */
208 case 2: /* show version */
212 case 3: /* open control socket here */
214 pidfilename = xstrdup(optarg);
221 case '?': /* wrong options */
231 if(!netname && (netname = getenv("NETNAME"))) {
232 netname = xstrdup(netname);
235 /* netname "." is special: a "top-level name" */
237 if(netname && (!*netname || !strcmp(netname, "."))) {
242 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
243 fprintf(stderr, "Invalid character in netname!\n");
251 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
253 char directory[PATH_MAX] = ".";
259 /* Check stdin and stdout */
261 /* Ask for a file and/or directory name. */
262 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
264 if(fgets(buf, sizeof(buf), stdin) == NULL) {
265 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
269 size_t len = strlen(buf);
282 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
285 if(filename[0] != '/') {
287 /* The directory is a relative path or a filename. */
288 getcwd(directory, sizeof(directory));
290 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
291 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
303 disable_old_keys(filename, what);
305 /* Open it first to keep the inode busy */
307 r = fopenmask(filename, mode, perms);
310 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
318 Generate a public/private Ed25519 key pair, and ask for a file to store
321 static bool ed25519_keygen(bool ask) {
324 char fname[PATH_MAX];
326 fprintf(stderr, "Generating Ed25519 key pair:\n");
328 if(!(key = ecdsa_generate())) {
329 fprintf(stderr, "Error during key generation!\n");
332 fprintf(stderr, "Done.\n");
335 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
336 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
342 if(!ecdsa_write_pem_private_key(key, f)) {
343 fprintf(stderr, "Error writing private key!\n");
350 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
352 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
355 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
361 char *pubkey = ecdsa_get_base64_public_key(key);
362 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
380 #ifndef DISABLE_LEGACY
382 Generate a public/private RSA key pair, and ask for a file to store
385 static bool rsa_keygen(int bits, bool ask) {
388 char fname[PATH_MAX];
390 // Make sure the key size is a multiple of 8 bits.
393 // Make sure that a valid key size is used.
394 if(bits < 1024 || bits > 8192) {
395 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
397 } else if(bits < 2048) {
398 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
401 fprintf(stderr, "Generating %d bits keys:\n", bits);
403 if(!(key = rsa_generate(bits, 0x10001))) {
404 fprintf(stderr, "Error during key generation!\n");
407 fprintf(stderr, "Done.\n");
410 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
411 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
417 if(!rsa_write_pem_private_key(key, f)) {
418 fprintf(stderr, "Error writing private key!\n");
425 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
427 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
430 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
436 if(!rsa_write_pem_public_key(key, f)) {
437 fprintf(stderr, "Error writing public key!\n");
460 bool recvline(int fd, char *line, size_t len) {
461 char *newline = NULL;
467 while(!(newline = memchr(buffer, '\n', blen))) {
468 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
470 if(result == -1 && sockerrno == EINTR) {
472 } else if(result <= 0) {
479 if((size_t)(newline - buffer) >= len) {
483 len = newline - buffer;
485 memcpy(line, buffer, len);
487 memmove(buffer, newline + 1, blen - len - 1);
493 static bool recvdata(int fd, char *data, size_t len) {
495 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
497 if(result == -1 && sockerrno == EINTR) {
499 } else if(result <= 0) {
506 memcpy(data, buffer, len);
507 memmove(buffer, buffer + len, blen - len);
513 bool sendline(int fd, char *format, ...) {
514 static char buffer[4096];
519 va_start(ap, format);
520 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
521 buffer[sizeof(buffer) - 1] = 0;
524 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
532 int result = send(fd, p, blen, MSG_NOSIGNAL);
534 if(result == -1 && sockerrno == EINTR) {
536 } else if(result <= 0) {
547 static void pcap(int fd, FILE *out, uint32_t snaplen) {
548 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
556 uint32_t tz_accuracy;
563 snaplen ? snaplen : sizeof(data),
576 fwrite(&header, sizeof(header), 1, out);
581 while(recvline(fd, line, sizeof(line))) {
583 int n = sscanf(line, "%d %d %d", &code, &req, &len);
584 gettimeofday(&tv, NULL);
586 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || (size_t)len > sizeof(data)) {
590 if(!recvdata(fd, data, len)) {
594 packet.tv_sec = tv.tv_sec;
595 packet.tv_usec = tv.tv_usec;
597 packet.origlen = len;
598 fwrite(&packet, sizeof(packet), 1, out);
599 fwrite(data, len, 1, out);
604 static void logcontrol(int fd, FILE *out, int level) {
605 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
609 while(recvline(fd, line, sizeof(line))) {
611 int n = sscanf(line, "%d %d %d", &code, &req, &len);
613 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
617 if(!recvdata(fd, data, len)) {
621 fwrite(data, len, 1, out);
627 static bool stop_tincd(void) {
628 if(!connect_tincd(true)) {
632 sendline(fd, "%d %d", CONTROL, REQ_STOP);
634 while(recvline(fd, line, sizeof(line))) {
635 // wait for tincd to close the connection...
646 static bool remove_service(void) {
647 SC_HANDLE manager = NULL;
648 SC_HANDLE service = NULL;
649 SERVICE_STATUS status = {0};
650 bool success = false;
652 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
655 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
659 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
662 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
663 success = stop_tincd();
665 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
671 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
672 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
674 fprintf(stderr, "%s service stopped\n", identname);
677 if(!DeleteService(service)) {
678 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
687 CloseServiceHandle(service);
691 CloseServiceHandle(manager);
695 fprintf(stderr, "%s service removed\n", identname);
702 bool connect_tincd(bool verbose) {
707 struct timeval tv = {0, 0};
709 if(select(fd + 1, &r, NULL, NULL, &tv)) {
710 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
718 FILE *f = fopen(pidfilename, "r");
722 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
731 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
733 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
744 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
745 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
746 /* clean up the stale socket and pid file */
748 unlink(unixsocketname);
752 struct sockaddr_un sa = {
753 .sun_family = AF_UNIX,
756 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
757 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
761 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
763 fd = socket(AF_UNIX, SOCK_STREAM, 0);
767 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
773 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
775 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
784 struct addrinfo hints = {
785 .ai_family = AF_UNSPEC,
786 .ai_socktype = SOCK_STREAM,
787 .ai_protocol = IPPROTO_TCP,
791 struct addrinfo *res = NULL;
793 if(getaddrinfo(host, port, &hints, &res) || !res) {
795 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
801 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
805 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
811 unsigned long arg = 0;
813 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
815 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
819 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
821 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
833 static const int one = 1;
834 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
837 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
842 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
844 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
852 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
854 fprintf(stderr, "Could not fully establish control socket connection\n");
866 static int cmd_start(int argc, char *argv[]) {
867 if(connect_tincd(false)) {
869 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
871 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
878 char *slash = strrchr(program_name, '/');
882 if((c = strrchr(program_name, '\\')) > slash) {
888 char *default_c = "tincd";
891 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
897 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
902 Windows has no real concept of an "argv array". A command line is just one string.
903 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
904 it uses quotes to handle spaces in arguments.
905 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
906 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
907 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
909 xasprintf(&arg0, "\"%s\"", arg0);
911 nargv[nargc++] = arg0;
913 for(int i = 1; i < optind; i++) {
914 nargv[nargc++] = orig_argv[i];
917 for(int i = 1; i < argc; i++) {
918 nargv[nargc++] = argv[i];
922 int status = spawnvp(_P_WAIT, c, nargv);
931 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
937 int pfd[2] = {-1, -1};
939 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
940 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
953 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
966 snprintf(buf, sizeof(buf), "%d", pfd[1]);
967 setenv("TINC_UMBILICAL", buf, true);
968 exit(execvp(c, nargv));
975 int status = -1, result;
977 signal(SIGINT, SIG_IGN);
980 // Pass all log messages from the umbilical to stderr.
981 // A nul-byte right before closure means tincd started successfully.
986 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
987 failure = buf[len - 1];
1002 // Make sure the child process is really gone.
1003 result = waitpid(pid, &status, 0);
1006 signal(SIGINT, SIG_DFL);
1009 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1012 fprintf(stderr, "Error starting %s\n", c);
1015 if(c != default_c) {
1019 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1023 static int cmd_stop(int argc, char *argv[]) {
1027 fprintf(stderr, "Too many arguments!\n");
1032 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1037 if(kill(pid, SIGTERM)) {
1038 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1042 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1043 waitpid(pid, NULL, 0);
1054 static int cmd_restart(int argc, char *argv[]) {
1056 return cmd_start(argc, argv);
1059 static int cmd_reload(int argc, char *argv[]) {
1063 fprintf(stderr, "Too many arguments!\n");
1067 if(!connect_tincd(true)) {
1071 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1073 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1074 fprintf(stderr, "Could not reload configuration.\n");
1082 static int dump_invitations(void) {
1083 char dname[PATH_MAX];
1084 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1085 DIR *dir = opendir(dname);
1088 if(errno == ENOENT) {
1089 fprintf(stderr, "No outstanding invitations.\n");
1093 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1101 while((ent = readdir(dir))) {
1102 char buf[MAX_STRING_SIZE];
1104 if(b64decode(ent->d_name, buf, 24) != 18) {
1108 char fname[PATH_MAX];
1110 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1111 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1115 FILE *f = fopen(fname, "r");
1118 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1124 if(!fgets(buf, sizeof(buf), f)) {
1125 fprintf(stderr, "Invalid invitation file %s\n", fname);
1132 char *eol = buf + strlen(buf);
1134 while(strchr("\t \r\n", *--eol)) {
1138 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1139 fprintf(stderr, "Invalid invitation file %s\n", fname);
1144 printf("%s %s\n", ent->d_name, buf + 7);
1150 fprintf(stderr, "No outstanding invitations.\n");
1156 static int cmd_dump(int argc, char *argv[]) {
1157 bool only_reachable = false;
1159 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1160 if(strcasecmp(argv[2], "nodes")) {
1161 fprintf(stderr, "`reachable' only supported for nodes.\n");
1166 only_reachable = true;
1172 fprintf(stderr, "Invalid number of arguments.\n");
1177 if(!strcasecmp(argv[1], "invitations")) {
1178 return dump_invitations();
1181 if(!connect_tincd(true)) {
1187 if(!strcasecmp(argv[1], "nodes")) {
1188 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1189 } else if(!strcasecmp(argv[1], "edges")) {
1190 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1191 } else if(!strcasecmp(argv[1], "subnets")) {
1192 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1193 } else if(!strcasecmp(argv[1], "connections")) {
1194 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1195 } else if(!strcasecmp(argv[1], "graph")) {
1196 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1197 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1199 } else if(!strcasecmp(argv[1], "digraph")) {
1200 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1201 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1204 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1210 printf("graph {\n");
1211 } else if(do_graph == 2) {
1212 printf("digraph {\n");
1215 while(recvline(fd, line, sizeof(line))) {
1216 char node1[4096], node2[4096];
1217 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1220 if(do_graph && req == REQ_DUMP_NODES) {
1242 char local_host[4096];
1243 char local_port[4096];
1246 int cipher, digest, maclength, compression, distance, socket, weight;
1247 short int pmtu, minmtu, maxmtu;
1248 unsigned int options, status_int;
1249 node_status_t status;
1250 long int last_state_change;
1252 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1255 case REQ_DUMP_NODES: {
1256 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);
1259 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1263 memcpy(&status, &status_int, sizeof(status));
1266 const char *color = "black";
1268 if(!strcmp(host, "MYSELF")) {
1270 } else if(!status.reachable) {
1272 } else if(strcmp(via, node)) {
1274 } else if(!status.validkey) {
1276 } else if(minmtu > 0) {
1280 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1282 if(only_reachable && !status.reachable) {
1286 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,
1287 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);
1289 if(udp_ping_rtt != -1) {
1290 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1298 case REQ_DUMP_EDGES: {
1299 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);
1302 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1307 float w = 1 + 65536.0 / weight;
1309 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1310 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1311 } else if(do_graph == 2) {
1312 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1315 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);
1320 case REQ_DUMP_SUBNETS: {
1321 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1324 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1328 printf("%s owner %s\n", strip_weight(subnet), node);
1332 case REQ_DUMP_CONNECTIONS: {
1333 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1336 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1340 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1345 fprintf(stderr, "Unable to parse dump from tincd.\n");
1350 fprintf(stderr, "Error receiving dump.\n");
1354 static int cmd_purge(int argc, char *argv[]) {
1358 fprintf(stderr, "Too many arguments!\n");
1362 if(!connect_tincd(true)) {
1366 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1368 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1369 fprintf(stderr, "Could not purge old information.\n");
1376 static int cmd_debug(int argc, char *argv[]) {
1378 fprintf(stderr, "Invalid number of arguments.\n");
1382 if(!connect_tincd(true)) {
1386 int debuglevel = atoi(argv[1]);
1389 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1391 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1392 fprintf(stderr, "Could not set debug level.\n");
1396 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1400 static int cmd_retry(int argc, char *argv[]) {
1404 fprintf(stderr, "Too many arguments!\n");
1408 if(!connect_tincd(true)) {
1412 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1414 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1415 fprintf(stderr, "Could not retry outgoing connections.\n");
1422 static int cmd_connect(int argc, char *argv[]) {
1424 fprintf(stderr, "Invalid number of arguments.\n");
1428 if(!check_id(argv[1])) {
1429 fprintf(stderr, "Invalid name for node.\n");
1433 if(!connect_tincd(true)) {
1437 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1439 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1440 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1447 static int cmd_disconnect(int argc, char *argv[]) {
1449 fprintf(stderr, "Invalid number of arguments.\n");
1453 if(!check_id(argv[1])) {
1454 fprintf(stderr, "Invalid name for node.\n");
1458 if(!connect_tincd(true)) {
1462 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1464 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1465 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1472 static int cmd_top(int argc, char *argv[]) {
1476 fprintf(stderr, "Too many arguments!\n");
1482 if(!connect_tincd(true)) {
1489 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1494 static int cmd_pcap(int argc, char *argv[]) {
1496 fprintf(stderr, "Too many arguments!\n");
1500 if(!connect_tincd(true)) {
1504 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1509 static void sigint_handler(int sig) {
1512 fprintf(stderr, "\n");
1513 shutdown(fd, SHUT_RDWR);
1517 static int cmd_log(int argc, char *argv[]) {
1519 fprintf(stderr, "Too many arguments!\n");
1523 if(!connect_tincd(true)) {
1528 signal(SIGINT, sigint_handler);
1531 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1534 signal(SIGINT, SIG_DFL);
1542 static int cmd_pid(int argc, char *argv[]) {
1546 fprintf(stderr, "Too many arguments!\n");
1550 if(!connect_tincd(true) || !pid) {
1554 printf("%d\n", pid);
1558 int rstrip(char *value) {
1559 int len = strlen(value);
1561 while(len && strchr("\t\r\n ", value[len - 1])) {
1568 char *get_my_name(bool verbose) {
1569 FILE *f = fopen(tinc_conf, "r");
1573 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1582 while(fgets(buf, sizeof(buf), f)) {
1583 int len = strcspn(buf, "\t =");
1585 value += strspn(value, "\t ");
1589 value += strspn(value, "\t ");
1592 if(!rstrip(value)) {
1598 if(strcasecmp(buf, "Name")) {
1604 return replace_name(value);
1611 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1617 ecdsa_t *get_pubkey(FILE *f) {
1621 while(fgets(buf, sizeof(buf), f)) {
1622 int len = strcspn(buf, "\t =");
1624 value += strspn(value, "\t ");
1628 value += strspn(value, "\t ");
1631 if(!rstrip(value)) {
1637 if(strcasecmp(buf, "Ed25519PublicKey")) {
1642 return ecdsa_set_base64_public_key(value);
1649 const var_t variables[] = {
1650 /* Server configuration */
1651 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1652 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1653 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1654 {"BindToInterface", VAR_SERVER},
1655 {"Broadcast", VAR_SERVER | VAR_SAFE},
1656 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1657 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1658 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1659 {"Device", VAR_SERVER},
1660 {"DeviceStandby", VAR_SERVER},
1661 {"DeviceType", VAR_SERVER},
1662 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1663 {"Ed25519PrivateKeyFile", VAR_SERVER},
1664 {"ExperimentalProtocol", VAR_SERVER},
1665 {"Forwarding", VAR_SERVER},
1666 {"FWMark", VAR_SERVER},
1667 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1668 {"Hostnames", VAR_SERVER},
1669 {"IffOneQueue", VAR_SERVER},
1670 {"Interface", VAR_SERVER},
1671 {"InvitationExpire", VAR_SERVER},
1672 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1673 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1674 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1675 {"LogLevel", VAR_SERVER},
1676 {"MACExpire", VAR_SERVER | VAR_SAFE},
1677 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1678 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1679 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1680 {"Mode", VAR_SERVER | VAR_SAFE},
1681 {"Name", VAR_SERVER},
1682 {"PingInterval", VAR_SERVER | VAR_SAFE},
1683 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1684 {"PriorityInheritance", VAR_SERVER},
1685 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1686 {"PrivateKeyFile", VAR_SERVER},
1687 {"ProcessPriority", VAR_SERVER},
1688 {"Proxy", VAR_SERVER},
1689 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1690 {"ScriptsExtension", VAR_SERVER},
1691 {"ScriptsInterpreter", VAR_SERVER},
1692 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1693 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1694 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1695 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1696 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1697 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1698 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1699 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1700 {"UDPRcvBuf", VAR_SERVER},
1701 {"UDPSndBuf", VAR_SERVER},
1702 {"UPnP", VAR_SERVER},
1703 {"UPnPDiscoverWait", VAR_SERVER},
1704 {"UPnPRefreshPeriod", VAR_SERVER},
1705 {"VDEGroup", VAR_SERVER},
1706 {"VDEPort", VAR_SERVER},
1707 /* Host configuration */
1708 {"Address", VAR_HOST | VAR_MULTIPLE},
1709 {"Cipher", VAR_SERVER | VAR_HOST},
1710 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1711 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1712 {"Digest", VAR_SERVER | VAR_HOST},
1713 {"Ed25519PublicKey", VAR_HOST},
1714 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1715 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1716 {"MACLength", VAR_SERVER | VAR_HOST},
1717 {"PMTU", VAR_SERVER | VAR_HOST},
1718 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1720 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1721 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1722 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1723 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1724 {"Weight", VAR_HOST | VAR_SAFE},
1728 static int cmd_config(int argc, char *argv[]) {
1730 fprintf(stderr, "Invalid number of arguments.\n");
1734 if(strcasecmp(argv[0], "config")) {
1740 if(!strcasecmp(argv[1], "get")) {
1742 } else if(!strcasecmp(argv[1], "add")) {
1743 argv++, argc--, action = 1;
1744 } else if(!strcasecmp(argv[1], "del")) {
1745 argv++, argc--, action = -1;
1746 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1747 argv++, argc--, action = 0;
1751 fprintf(stderr, "Invalid number of arguments.\n");
1755 // Concatenate the rest of the command line
1756 strncpy(line, argv[1], sizeof(line) - 1);
1758 for(int i = 2; i < argc; i++) {
1759 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1760 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1763 // Liberal parsing into node name, variable name and value.
1769 len = strcspn(line, "\t =");
1771 value += strspn(value, "\t ");
1775 value += strspn(value, "\t ");
1779 variable = strchr(line, '.');
1789 fprintf(stderr, "No variable given.\n");
1793 if(action >= 0 && !*value) {
1794 fprintf(stderr, "No value for variable given.\n");
1798 if(action < -1 && *value) {
1802 /* Some simple checks. */
1804 bool warnonremove = false;
1806 for(int i = 0; variables[i].name; i++) {
1807 if(strcasecmp(variables[i].name, variable)) {
1812 variable = (char *)variables[i].name;
1814 if(!strcasecmp(variable, "Subnet") && *value) {
1817 if(!str2net(&s, value)) {
1818 fprintf(stderr, "Malformed subnet definition %s\n", value);
1822 if(!subnetcheck(s)) {
1823 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1828 /* Discourage use of obsolete variables. */
1830 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1832 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1834 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1839 /* Don't put server variables in host config files */
1841 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1843 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1845 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1850 /* Should this go into our own host config file? */
1852 if(!node && !(variables[i].type & VAR_SERVER)) {
1853 node = get_my_name(true);
1860 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1861 Turn on warnings when it seems variables might be removed unintentionally. */
1863 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1864 warnonremove = true;
1866 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1867 warnonremove = true;
1873 if(node && !check_id(node)) {
1874 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);
1889 if(node && node != line) {
1897 // Open the right configuration file.
1898 char filename[PATH_MAX];
1901 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1908 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1911 FILE *f = fopen(filename, "r");
1914 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1918 char tmpfile[PATH_MAX];
1922 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1923 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1927 tf = fopen(tmpfile, "w");
1930 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1936 // Copy the file, making modifications on the fly, unless we are just getting a value.
1940 bool removed = false;
1943 while(fgets(buf1, sizeof(buf1), f)) {
1944 buf1[sizeof(buf1) - 1] = 0;
1945 strncpy(buf2, buf1, sizeof(buf2));
1947 // Parse line in a simple way
1951 len = strcspn(buf2, "\t =");
1952 bvalue = buf2 + len;
1953 bvalue += strspn(bvalue, "\t ");
1955 if(*bvalue == '=') {
1957 bvalue += strspn(bvalue, "\t ");
1964 if(!strcasecmp(buf2, variable)) {
1968 printf("%s\n", bvalue);
1970 } else if(action == -1) {
1971 if(!*value || !strcasecmp(bvalue, value)) {
1977 } else if(action == 0) {
1978 // Warn if "set" was used for variables that can occur multiple times
1979 if(warnonremove && strcasecmp(bvalue, value)) {
1980 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1983 // Already set? Delete the rest...
1988 // Otherwise, replace.
1989 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1990 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1997 } else if(action > 0) {
1998 // Check if we've already seen this variable with the same value
1999 if(!strcasecmp(bvalue, value)) {
2006 // Copy original line...
2007 if(fputs(buf1, tf) < 0) {
2008 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2012 // Add newline if it is missing...
2013 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2014 if(fputc('\n', tf) < 0) {
2015 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2022 // Make sure we read everything...
2023 if(ferror(f) || !feof(f)) {
2024 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2029 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2033 // Add new variable if necessary.
2034 if((action > 0 && !found) || (action == 0 && !set)) {
2035 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2036 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2045 fprintf(stderr, "No matching configuration variables found.\n");
2050 // Make sure we wrote everything...
2052 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2056 // Could we find what we had to remove?
2057 if(action < 0 && !removed) {
2059 fprintf(stderr, "No configuration variables deleted.\n");
2063 // Replace the configuration file with the new one
2066 if(remove(filename)) {
2067 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2073 if(rename(tmpfile, filename)) {
2074 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2078 // Silently try notifying a running tincd of changes.
2079 if(connect_tincd(false)) {
2080 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2086 static bool try_bind(int port) {
2087 struct addrinfo *ai = NULL, *aip;
2088 struct addrinfo hint = {
2089 .ai_flags = AI_PASSIVE,
2090 .ai_family = AF_UNSPEC,
2091 .ai_socktype = SOCK_STREAM,
2092 .ai_protocol = IPPROTO_TCP,
2095 bool success = true;
2097 snprintf(portstr, sizeof(portstr), "%d", port);
2099 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2103 for(aip = ai; aip; aip = aip->ai_next) {
2104 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2111 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2124 int check_port(const char *name) {
2129 fprintf(stderr, "Warning: could not bind to port 655. ");
2131 for(int i = 0; i < 100; i++) {
2132 int port = 0x1000 + (rand() & 0x7fff);
2134 if(try_bind(port)) {
2135 char filename[PATH_MAX];
2136 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2137 FILE *f = fopen(filename, "a");
2140 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2141 fprintf(stderr, "Please change tinc's Port manually.\n");
2145 fprintf(f, "Port = %d\n", port);
2147 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2152 fprintf(stderr, "Please change tinc's Port manually.\n");
2156 static int cmd_init(int argc, char *argv[]) {
2157 if(!access(tinc_conf, F_OK)) {
2158 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2163 fprintf(stderr, "Too many arguments!\n");
2165 } else if(argc < 2) {
2168 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2170 if(!fgets(buf, sizeof(buf), stdin)) {
2171 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2175 int len = rstrip(buf);
2178 fprintf(stderr, "No name given!\n");
2184 fprintf(stderr, "No Name given!\n");
2188 name = strdup(argv[1]);
2191 fprintf(stderr, "No Name given!\n");
2196 if(!check_id(name)) {
2197 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2201 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2202 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2206 if(mkdir(confbase, 0777) && errno != EEXIST) {
2207 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2211 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2212 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2216 FILE *f = fopen(tinc_conf, "w");
2219 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2223 fprintf(f, "Name = %s\n", name);
2226 #ifndef DISABLE_LEGACY
2228 if(!rsa_keygen(2048, false)) {
2234 if(!ed25519_keygen(false)) {
2241 char filename[PATH_MAX];
2242 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2244 if(access(filename, F_OK)) {
2245 FILE *f = fopenmask(filename, "w", 0777);
2248 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2252 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");
2262 static int cmd_generate_keys(int argc, char *argv[]) {
2263 #ifdef DISABLE_LEGACY
2271 fprintf(stderr, "Too many arguments!\n");
2276 name = get_my_name(false);
2279 #ifndef DISABLE_LEGACY
2281 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2287 if(!ed25519_keygen(true)) {
2294 #ifndef DISABLE_LEGACY
2295 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2297 fprintf(stderr, "Too many arguments!\n");
2302 name = get_my_name(false);
2305 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2309 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2313 fprintf(stderr, "Too many arguments!\n");
2318 name = get_my_name(false);
2321 return !ed25519_keygen(true);
2324 static int cmd_help(int argc, char *argv[]) {
2332 static int cmd_version(int argc, char *argv[]) {
2336 fprintf(stderr, "Too many arguments!\n");
2344 static int cmd_info(int argc, char *argv[]) {
2346 fprintf(stderr, "Invalid number of arguments.\n");
2350 if(!connect_tincd(true)) {
2354 return info(fd, argv[1]);
2357 static const char *conffiles[] = {
2368 static int cmd_edit(int argc, char *argv[]) {
2370 fprintf(stderr, "Invalid number of arguments.\n");
2374 char filename[PATH_MAX] = "";
2376 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2377 for(int i = 0; conffiles[i]; i++) {
2378 if(!strcmp(argv[1], conffiles[i])) {
2379 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2388 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2389 char *dash = strchr(argv[1], '-');
2394 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2395 fprintf(stderr, "Invalid configuration filename.\n");
2403 const char *editor = getenv("VISUAL");
2406 editor = getenv("EDITOR");
2413 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2415 xasprintf(&command, "edit \"%s\"", filename);
2417 int result = system(command);
2424 // Silently try notifying a running tincd of changes.
2425 if(connect_tincd(false)) {
2426 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2432 static int export(const char *name, FILE *out) {
2433 char filename[PATH_MAX];
2434 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2435 FILE *in = fopen(filename, "r");
2438 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2442 fprintf(out, "Name = %s\n", name);
2445 while(fgets(buf, sizeof(buf), in)) {
2446 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2452 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2461 static int cmd_export(int argc, char *argv[]) {
2465 fprintf(stderr, "Too many arguments!\n");
2469 char *name = get_my_name(true);
2475 int result = export(name, stdout);
2485 static int cmd_export_all(int argc, char *argv[]) {
2489 fprintf(stderr, "Too many arguments!\n");
2493 DIR *dir = opendir(hosts_dir);
2496 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2504 while((ent = readdir(dir))) {
2505 if(!check_id(ent->d_name)) {
2512 printf("#---------------------------------------------------------------#\n");
2515 result |= export(ent->d_name, stdout);
2527 static int cmd_import(int argc, char *argv[]) {
2531 fprintf(stderr, "Too many arguments!\n");
2540 char filename[PATH_MAX] = "";
2542 bool firstline = true;
2544 while(fgets(buf, sizeof(buf), in)) {
2545 if(sscanf(buf, "Name = %4095s", name) == 1) {
2548 if(!check_id(name)) {
2549 fprintf(stderr, "Invalid Name in input!\n");
2557 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2558 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2562 if(!force && !access(filename, F_OK)) {
2563 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2568 out = fopen(filename, "w");
2571 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2577 } else if(firstline) {
2578 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2583 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2588 if(fputs(buf, out) < 0) {
2589 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2600 fprintf(stderr, "Imported %d host configuration files.\n", count);
2603 fprintf(stderr, "No host configuration files imported.\n");
2608 static int cmd_exchange(int argc, char *argv[]) {
2609 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2612 static int cmd_exchange_all(int argc, char *argv[]) {
2613 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2616 static int switch_network(char *name) {
2617 if(strcmp(name, ".")) {
2618 if(!check_netname(name, false)) {
2619 fprintf(stderr, "Invalid character in netname!\n");
2623 if(!check_netname(name, true)) {
2624 fprintf(stderr, "Warning: unsafe character in netname!\n");
2634 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2641 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2642 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2643 xasprintf(&prompt, "%s> ", identname);
2648 static int cmd_network(int argc, char *argv[]) {
2650 fprintf(stderr, "Too many arguments!\n");
2655 return switch_network(argv[1]);
2658 DIR *dir = opendir(confdir);
2661 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2667 while((ent = readdir(dir))) {
2668 if(*ent->d_name == '.') {
2672 if(!strcmp(ent->d_name, "tinc.conf")) {
2677 char fname[PATH_MAX];
2678 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2680 if(!access(fname, R_OK)) {
2681 printf("%s\n", ent->d_name);
2690 static int cmd_fsck(int argc, char *argv[]) {
2694 fprintf(stderr, "Too many arguments!\n");
2698 return fsck(orig_argv[0]);
2701 static void *readfile(FILE *in, size_t *len) {
2703 size_t bufsize = 4096;
2704 char *buf = xmalloc(bufsize);
2707 size_t read = fread(buf + count, 1, bufsize - count, in);
2715 if(count >= bufsize) {
2717 buf = xrealloc(buf, bufsize);
2728 static int cmd_sign(int argc, char *argv[]) {
2730 fprintf(stderr, "Too many arguments!\n");
2735 name = get_my_name(true);
2742 char fname[PATH_MAX];
2743 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2744 FILE *fp = fopen(fname, "r");
2747 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2751 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2754 fprintf(stderr, "Could not read private key from %s\n", fname);
2764 in = fopen(argv[1], "rb");
2767 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2776 char *data = readfile(in, &len);
2783 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2788 // Ensure we sign our name and current time as well
2789 long t = time(NULL);
2791 xasprintf(&trailer, " %s %ld", name, t);
2792 int trailer_len = strlen(trailer);
2794 data = xrealloc(data, len + trailer_len);
2795 memcpy(data + len, trailer, trailer_len);
2800 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2801 fprintf(stderr, "Error generating signature\n");
2807 b64encode(sig, sig, 64);
2810 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2811 fwrite(data, len, 1, stdout);
2817 static int cmd_verify(int argc, char *argv[]) {
2819 fprintf(stderr, "Not enough arguments!\n");
2824 fprintf(stderr, "Too many arguments!\n");
2828 char *node = argv[1];
2830 if(!strcmp(node, ".")) {
2832 name = get_my_name(true);
2840 } else if(!strcmp(node, "*")) {
2843 if(!check_id(node)) {
2844 fprintf(stderr, "Invalid node name\n");
2852 in = fopen(argv[2], "rb");
2855 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2863 char *data = readfile(in, &len);
2870 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2874 char *newline = memchr(data, '\n', len);
2876 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2877 fprintf(stderr, "Invalid input\n");
2883 size_t skip = newline - data;
2885 char signer[MAX_STRING_SIZE] = "";
2886 char sig[MAX_STRING_SIZE] = "";
2889 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2890 fprintf(stderr, "Invalid input\n");
2895 if(node && strcmp(node, signer)) {
2896 fprintf(stderr, "Signature is not made by %s\n", node);
2906 xasprintf(&trailer, " %s %ld", signer, t);
2907 int trailer_len = strlen(trailer);
2909 data = xrealloc(data, len + trailer_len);
2910 memcpy(data + len, trailer, trailer_len);
2913 newline = data + skip;
2915 char fname[PATH_MAX];
2916 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2917 FILE *fp = fopen(fname, "r");
2920 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2925 ecdsa_t *key = get_pubkey(fp);
2929 key = ecdsa_read_pem_public_key(fp);
2933 fprintf(stderr, "Could not read public key from %s\n", fname);
2941 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2942 fprintf(stderr, "Invalid signature\n");
2950 fwrite(newline, len - (newline - data), 1, stdout);
2956 static const struct {
2957 const char *command;
2958 int (*function)(int argc, char *argv[]);
2961 {"start", cmd_start, false},
2962 {"stop", cmd_stop, false},
2963 {"restart", cmd_restart, false},
2964 {"reload", cmd_reload, false},
2965 {"dump", cmd_dump, false},
2966 {"list", cmd_dump, false},
2967 {"purge", cmd_purge, false},
2968 {"debug", cmd_debug, false},
2969 {"retry", cmd_retry, false},
2970 {"connect", cmd_connect, false},
2971 {"disconnect", cmd_disconnect, false},
2972 {"top", cmd_top, false},
2973 {"pcap", cmd_pcap, false},
2974 {"log", cmd_log, false},
2975 {"pid", cmd_pid, false},
2976 {"config", cmd_config, true},
2977 {"add", cmd_config, false},
2978 {"del", cmd_config, false},
2979 {"get", cmd_config, false},
2980 {"set", cmd_config, false},
2981 {"init", cmd_init, false},
2982 {"generate-keys", cmd_generate_keys, false},
2983 #ifndef DISABLE_LEGACY
2984 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
2986 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
2987 {"help", cmd_help, false},
2988 {"version", cmd_version, false},
2989 {"info", cmd_info, false},
2990 {"edit", cmd_edit, false},
2991 {"export", cmd_export, false},
2992 {"export-all", cmd_export_all, false},
2993 {"import", cmd_import, false},
2994 {"exchange", cmd_exchange, false},
2995 {"exchange-all", cmd_exchange_all, false},
2996 {"invite", cmd_invite, false},
2997 {"join", cmd_join, false},
2998 {"network", cmd_network, false},
2999 {"fsck", cmd_fsck, false},
3000 {"sign", cmd_sign, false},
3001 {"verify", cmd_verify, false},
3002 {NULL, NULL, false},
3005 #ifdef HAVE_READLINE
3006 static char *complete_command(const char *text, int state) {
3015 while(commands[i].command) {
3016 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3017 return xstrdup(commands[i].command);
3026 static char *complete_dump(const char *text, int state) {
3027 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3037 if(!strncasecmp(matches[i], text, strlen(text))) {
3038 return xstrdup(matches[i]);
3047 static char *complete_config(const char *text, int state) {
3056 while(variables[i].name) {
3057 char *dot = strchr(text, '.');
3060 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3062 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3066 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3067 return xstrdup(variables[i].name);
3077 static char *complete_info(const char *text, int state) {
3083 if(!connect_tincd(false)) {
3087 // Check the list of nodes
3088 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3089 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3092 while(recvline(fd, line, sizeof(line))) {
3094 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3107 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3111 if(!strncmp(item, text, strlen(text))) {
3112 return xstrdup(strip_weight(item));
3119 static char *complete_nothing(const char *text, int state) {
3125 static char **completion(const char *text, int start, int end) {
3127 char **matches = NULL;
3130 matches = rl_completion_matches(text, complete_command);
3131 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3132 matches = rl_completion_matches(text, complete_dump);
3133 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3134 matches = rl_completion_matches(text, complete_config);
3135 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3136 matches = rl_completion_matches(text, complete_config);
3137 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3138 matches = rl_completion_matches(text, complete_config);
3139 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3140 matches = rl_completion_matches(text, complete_config);
3141 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3142 matches = rl_completion_matches(text, complete_info);
3149 static int cmd_shell(int argc, char *argv[]) {
3150 xasprintf(&prompt, "%s> ", identname);
3154 int maxargs = argc + 16;
3155 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3157 for(int i = 0; i < argc; i++) {
3161 #ifdef HAVE_READLINE
3162 rl_readline_name = "tinc";
3163 rl_completion_entry_function = complete_nothing;
3164 rl_attempted_completion_function = completion;
3165 rl_filename_completion_desired = 0;
3170 #ifdef HAVE_READLINE
3175 rl_basic_word_break_characters = "\t\n ";
3176 line = readline(prompt);
3177 copy = line ? xstrdup(line) : NULL;
3179 line = fgets(buf, sizeof(buf), stdin);
3185 fputs(prompt, stdout);
3188 line = fgets(buf, sizeof(buf), stdin);
3195 /* Ignore comments */
3204 char *p = line + strspn(line, " \t\n");
3205 char *next = strtok(p, " \t\n");
3208 if(nargc >= maxargs) {
3210 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3215 next = strtok(NULL, " \t\n");
3222 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3223 #ifdef HAVE_READLINE
3232 for(int i = 0; commands[i].command; i++) {
3233 if(!strcasecmp(nargv[argc], commands[i].command)) {
3234 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3240 #ifdef HAVE_READLINE
3249 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3254 #ifdef HAVE_READLINE
3266 static void cleanup() {
3272 int main(int argc, char *argv[]) {
3273 program_name = argv[0];
3276 tty = isatty(0) && isatty(1);
3278 if(!parse_options(argc, argv)) {
3283 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3284 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3298 static struct WSAData wsa_state;
3300 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3301 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3307 gettimeofday(&now, NULL);
3308 srand(now.tv_sec + now.tv_usec);
3311 if(optind >= argc) {
3312 return cmd_shell(argc, argv);
3315 for(int i = 0; commands[i].command; i++) {
3316 if(!strcasecmp(argv[optind], commands[i].command)) {
3317 return commands[i].function(argc - optind, argv + optind);
3321 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);