2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2015 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 = "";
78 static struct option const long_options[] = {
79 {"batch", no_argument, NULL, 'b'},
80 {"config", required_argument, NULL, 'c'},
81 {"net", required_argument, NULL, 'n'},
82 {"help", no_argument, NULL, 1},
83 {"version", no_argument, NULL, 2},
84 {"pidfile", required_argument, NULL, 3},
85 {"force", no_argument, NULL, 4},
89 static void version(void) {
90 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
91 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
92 printf("Copyright (C) 1998-2014 Ivo Timmermans, Guus Sliepen and others.\n"
93 "See the AUTHORS file for a complete list.\n\n"
94 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
95 "and you are welcome to redistribute it under certain conditions;\n"
96 "see the file COPYING for details.\n");
99 static void usage(bool status) {
101 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
103 printf("Usage: %s [options] command\n\n", program_name);
104 printf("Valid options are:\n"
105 " -b, --batch Don't ask for anything (non-interactive mode).\n"
106 " -c, --config=DIR Read configuration options from DIR.\n"
107 " -n, --net=NETNAME Connect to net NETNAME.\n"
108 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
109 " --force Force some commands to work despite warnings.\n"
110 " --help Display this help and exit.\n"
111 " --version Output version information and exit.\n"
113 "Valid commands are:\n"
114 " init [name] Create initial configuration files.\n"
115 " get VARIABLE Print current value of VARIABLE\n"
116 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
117 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
118 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
119 " start [tincd options] Start tincd.\n"
120 " stop Stop tincd.\n"
121 " restart [tincd options] Restart tincd.\n"
122 " reload Partially reload configuration of running tincd.\n"
123 " pid Show PID of currently running tincd.\n"
124 #ifdef DISABLE_LEGACY
125 " generate-keys Generate a new Ed25519 public/private keypair.\n"
127 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
128 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
130 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
131 " dump Dump a list of one of the following things:\n"
132 " [reachable] nodes - all known nodes in the VPN\n"
133 " edges - all known connections in the VPN\n"
134 " subnets - all known subnets in the VPN\n"
135 " connections - all meta connections with ourself\n"
136 " [di]graph - graph of the VPN in dotty format\n"
137 " invitations - outstanding invitations\n"
138 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
139 " purge Purge unreachable nodes\n"
140 " debug N Set debug level\n"
141 " retry Retry all outgoing connections\n"
142 " disconnect NODE Close meta connection with NODE\n"
144 " top Show real-time statistics\n"
146 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
147 " log [level] Dump log output [up to the specified level]\n"
148 " export Export host configuration of local node to standard output\n"
149 " export-all Export all host configuration files to standard output\n"
150 " import Import host configuration file(s) from standard input\n"
151 " exchange Same as export followed by import\n"
152 " exchange-all Same as export-all followed by import\n"
153 " invite NODE [...] Generate an invitation for NODE\n"
154 " join INVITATION Join a VPN using an INVITATION\n"
155 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
156 " fsck Check the configuration files for problems.\n"
158 printf("Report bugs to tinc@tinc-vpn.org.\n");
162 static bool parse_options(int argc, char **argv) {
164 int option_index = 0;
166 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
168 case 0: /* long option */
175 case 'c': /* config file */
176 confbase = xstrdup(optarg);
177 confbasegiven = true;
180 case 'n': /* net name given */
181 netname = xstrdup(optarg);
184 case 1: /* show help */
188 case 2: /* show version */
192 case 3: /* open control socket here */
193 pidfilename = xstrdup(optarg);
200 case '?': /* wrong options */
209 if(!netname && (netname = getenv("NETNAME")))
210 netname = xstrdup(netname);
212 /* netname "." is special: a "top-level name" */
214 if(netname && (!*netname || !strcmp(netname, "."))) {
219 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
220 fprintf(stderr, "Invalid character in netname!\n");
227 /* Open a file with the desired permissions, minus the umask.
228 Also, if we want to create an executable file, we call fchmod()
229 to set the executable bits. */
231 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
232 mode_t mask = umask(0);
235 FILE *f = fopen(filename, mode);
237 if((perms & 0444) && f)
238 fchmod(fileno(f), perms);
244 static void disable_old_keys(const char *filename, const char *what) {
245 char tmpfile[PATH_MAX] = "";
247 bool disabled = false;
252 r = fopen(filename, "r");
256 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
258 struct stat st = {.st_mode = 0600};
259 fstat(fileno(r), &st);
260 w = fopenmask(tmpfile, "w", st.st_mode);
262 while(fgets(buf, sizeof buf, r)) {
263 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
264 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
270 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
276 if(block || ed25519pubkey)
278 if(fputs(buf, w) < 0) {
284 if(block && !strncmp(buf, "-----END ", 9))
291 if(ferror(r) || fclose(r) < 0)
296 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
303 // We cannot atomically replace files on Windows.
304 char bakfile[PATH_MAX] = "";
305 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
306 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
307 rename(bakfile, filename);
309 if(rename(tmpfile, filename)) {
311 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
316 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
323 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
329 /* Check stdin and stdout */
331 /* Ask for a file and/or directory name. */
332 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
334 if(fgets(buf, sizeof buf, stdin) == NULL) {
335 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
339 size_t len = strlen(buf);
348 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
350 if(filename[0] != '/') {
352 /* The directory is a relative path or a filename. */
353 directory = get_current_dir_name();
354 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
358 disable_old_keys(filename, what);
360 /* Open it first to keep the inode busy */
362 r = fopenmask(filename, mode, perms);
365 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
373 Generate a public/private Ed25519 keypair, and ask for a file to store
376 static bool ed25519_keygen(bool ask) {
379 char *pubname, *privname;
381 fprintf(stderr, "Generating Ed25519 keypair:\n");
383 if(!(key = ecdsa_generate())) {
384 fprintf(stderr, "Error during key generation!\n");
387 fprintf(stderr, "Done.\n");
389 xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
390 f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
396 if(!ecdsa_write_pem_private_key(key, f)) {
397 fprintf(stderr, "Error writing private key!\n");
406 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
408 xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
410 f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
416 char *pubkey = ecdsa_get_base64_public_key(key);
417 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
426 #ifndef DISABLE_LEGACY
428 Generate a public/private RSA keypair, and ask for a file to store
431 static bool rsa_keygen(int bits, bool ask) {
434 char *pubname, *privname;
436 // Make sure the key size is a multiple of 8 bits.
439 // Force them to be between 1024 and 8192 bits long.
445 fprintf(stderr, "Generating %d bits keys:\n", bits);
447 if(!(key = rsa_generate(bits, 0x10001))) {
448 fprintf(stderr, "Error during key generation!\n");
451 fprintf(stderr, "Done.\n");
453 xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
454 f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
460 if(!rsa_write_pem_private_key(key, f)) {
461 fprintf(stderr, "Error writing private key!\n");
470 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
472 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
474 f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
480 if(!rsa_write_pem_public_key(key, f)) {
481 fprintf(stderr, "Error writing public key!\n");
497 bool recvline(int fd, char *line, size_t len) {
498 char *newline = NULL;
503 while(!(newline = memchr(buffer, '\n', blen))) {
504 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
505 if(result == -1 && sockerrno == EINTR)
512 if(newline - buffer >= len)
515 len = newline - buffer;
517 memcpy(line, buffer, len);
519 memmove(buffer, newline + 1, blen - len - 1);
525 bool recvdata(int fd, char *data, size_t len) {
530 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
531 if(result == -1 && sockerrno == EINTR)
538 memcpy(data, buffer, len);
539 memmove(buffer, buffer + len, blen - len);
545 bool sendline(int fd, char *format, ...) {
546 static char buffer[4096];
551 va_start(ap, format);
552 blen = vsnprintf(buffer, sizeof buffer, format, ap);
555 if(blen < 1 || blen >= sizeof buffer)
562 int result = send(fd, p, blen, MSG_NOSIGNAL);
563 if(result == -1 && sockerrno == EINTR)
574 static void pcap(int fd, FILE *out, int snaplen) {
575 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
583 uint32_t tz_accuracy;
590 snaplen ?: sizeof data,
603 fwrite(&header, sizeof header, 1, out);
607 while(recvline(fd, line, sizeof line)) {
609 int n = sscanf(line, "%d %d %d", &code, &req, &len);
610 gettimeofday(&tv, NULL);
611 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
613 if(!recvdata(fd, data, len))
615 packet.tv_sec = tv.tv_sec;
616 packet.tv_usec = tv.tv_usec;
618 packet.origlen = len;
619 fwrite(&packet, sizeof packet, 1, out);
620 fwrite(data, len, 1, out);
625 static void logcontrol(int fd, FILE *out, int level) {
626 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
630 while(recvline(fd, line, sizeof line)) {
632 int n = sscanf(line, "%d %d %d", &code, &req, &len);
633 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
635 if(!recvdata(fd, data, len))
637 fwrite(data, len, 1, out);
644 static bool remove_service(void) {
645 SC_HANDLE manager = NULL;
646 SC_HANDLE service = NULL;
647 SERVICE_STATUS status = {0};
649 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
651 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
655 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
658 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
662 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
663 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
665 fprintf(stderr, "%s service stopped\n", identname);
667 if(!DeleteService(service)) {
668 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
672 fprintf(stderr, "%s service removed\n", identname);
678 bool connect_tincd(bool verbose) {
683 struct timeval tv = {0, 0};
684 if(select(fd + 1, &r, NULL, NULL, &tv)) {
685 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
693 FILE *f = fopen(pidfilename, "r");
696 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
703 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
705 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
713 struct sockaddr_un sa;
714 sa.sun_family = AF_UNIX;
715 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
717 fd = socket(AF_UNIX, SOCK_STREAM, 0);
720 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
724 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
726 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
732 struct addrinfo hints = {
733 .ai_family = AF_UNSPEC,
734 .ai_socktype = SOCK_STREAM,
735 .ai_protocol = IPPROTO_TCP,
739 struct addrinfo *res = NULL;
741 if(getaddrinfo(host, port, &hints, &res) || !res) {
743 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
747 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
750 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
755 unsigned long arg = 0;
757 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
759 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
763 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
765 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
775 static const int one = 1;
776 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
782 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
784 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
790 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
792 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
794 fprintf(stderr, "Could not fully establish control socket connection\n");
804 static int cmd_start(int argc, char *argv[]) {
805 if(connect_tincd(false)) {
807 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
809 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
814 char *slash = strrchr(program_name, '/');
817 if ((c = strrchr(program_name, '\\')) > slash)
822 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
827 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
832 Windows has no real concept of an "argv array". A command line is just one string.
833 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
834 it uses quotes to handle spaces in arguments.
835 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
836 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
837 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
839 xasprintf(&arg0, "\"%s\"", arg0);
841 nargv[nargc++] = arg0;
842 for(int i = 1; i < optind; i++)
843 nargv[nargc++] = orig_argv[i];
844 for(int i = 1; i < argc; i++)
845 nargv[nargc++] = argv[i];
848 int status = spawnvp(_P_WAIT, c, nargv);
850 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
857 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
863 exit(execvp(c, nargv));
867 int status = -1, result;
869 signal(SIGINT, SIG_IGN);
871 result = waitpid(pid, &status, 0);
873 signal(SIGINT, SIG_DFL);
876 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
877 fprintf(stderr, "Error starting %s\n", c);
885 static int cmd_stop(int argc, char *argv[]) {
887 fprintf(stderr, "Too many arguments!\n");
892 if(!connect_tincd(true)) {
894 if(kill(pid, SIGTERM)) {
895 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
899 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
900 waitpid(pid, NULL, 0);
907 sendline(fd, "%d %d", CONTROL, REQ_STOP);
909 while(recvline(fd, line, sizeof line)) {
910 // Wait for tincd to close the connection...
913 if(!remove_service())
923 static int cmd_restart(int argc, char *argv[]) {
925 return cmd_start(argc, argv);
928 static int cmd_reload(int argc, char *argv[]) {
930 fprintf(stderr, "Too many arguments!\n");
934 if(!connect_tincd(true))
937 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
938 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
939 fprintf(stderr, "Could not reload configuration.\n");
947 static int dump_invitations(void) {
948 char dname[PATH_MAX];
949 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
950 DIR *dir = opendir(dname);
952 if(errno == ENOENT) {
953 fprintf(stderr, "No outstanding invitations.\n");
957 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
964 while((ent = readdir(dir))) {
965 char buf[MAX_STRING_SIZE];
966 if(b64decode(ent->d_name, buf, 24) != 18)
969 char fname[PATH_MAX];
970 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
971 FILE *f = fopen(fname, "r");
973 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
979 if(!fgets(buf, sizeof buf, f)) {
980 fprintf(stderr, "Invalid invitation file %s", fname);
986 char *eol = buf + strlen(buf);
987 while(strchr("\t \r\n", *--eol))
989 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
990 fprintf(stderr, "Invalid invitation file %s", fname);
995 printf("%s %s\n", ent->d_name, buf + 7);
1001 fprintf(stderr, "No outstanding invitations.\n");
1006 static int cmd_dump(int argc, char *argv[]) {
1007 bool only_reachable = false;
1009 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1010 if(strcasecmp(argv[2], "nodes")) {
1011 fprintf(stderr, "`reachable' only supported for nodes.\n");
1015 only_reachable = true;
1021 fprintf(stderr, "Invalid number of arguments.\n");
1026 if(!strcasecmp(argv[1], "invitations"))
1027 return dump_invitations();
1029 if(!connect_tincd(true))
1034 if(!strcasecmp(argv[1], "nodes"))
1035 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1036 else if(!strcasecmp(argv[1], "edges"))
1037 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1038 else if(!strcasecmp(argv[1], "subnets"))
1039 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1040 else if(!strcasecmp(argv[1], "connections"))
1041 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1042 else if(!strcasecmp(argv[1], "graph")) {
1043 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1044 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1046 } else if(!strcasecmp(argv[1], "digraph")) {
1047 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1048 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1051 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1057 printf("graph {\n");
1058 else if(do_graph == 2)
1059 printf("digraph {\n");
1061 while(recvline(fd, line, sizeof line)) {
1062 char node1[4096], node2[4096];
1063 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1065 if(do_graph && req == REQ_DUMP_NODES)
1083 char local_host[4096];
1084 char local_port[4096];
1087 int cipher, digest, maclength, compression, distance, socket, weight;
1088 short int pmtu, minmtu, maxmtu;
1089 unsigned int options, status_int;
1090 node_status_t status;
1091 long int last_state_change;
1094 case REQ_DUMP_NODES: {
1095 int n = sscanf(line, "%*d %*d %s %s %s port %s %d %d %d %d %x %x %s %s %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);
1097 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1101 memcpy(&status, &status_int, sizeof status);
1104 const char *color = "black";
1105 if(!strcmp(host, "MYSELF"))
1107 else if(!status.reachable)
1109 else if(strcmp(via, node))
1111 else if(!status.validkey)
1115 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1117 if(only_reachable && !status.reachable)
1119 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 %hd (min %hd max %hd)\n",
1120 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1124 case REQ_DUMP_EDGES: {
1125 int n = sscanf(line, "%*d %*d %s %s %s port %s %s port %s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
1127 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1132 float w = 1 + 65536.0 / weight;
1133 if(do_graph == 1 && strcmp(node1, node2) > 0)
1134 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1135 else if(do_graph == 2)
1136 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1138 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);
1142 case REQ_DUMP_SUBNETS: {
1143 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1145 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1148 printf("%s owner %s\n", strip_weight(subnet), node);
1151 case REQ_DUMP_CONNECTIONS: {
1152 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1154 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1157 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1161 fprintf(stderr, "Unable to parse dump from tincd.\n");
1166 fprintf(stderr, "Error receiving dump.\n");
1170 static int cmd_purge(int argc, char *argv[]) {
1172 fprintf(stderr, "Too many arguments!\n");
1176 if(!connect_tincd(true))
1179 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1180 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1181 fprintf(stderr, "Could not purge old information.\n");
1188 static int cmd_debug(int argc, char *argv[]) {
1190 fprintf(stderr, "Invalid number of arguments.\n");
1194 if(!connect_tincd(true))
1197 int debuglevel = atoi(argv[1]);
1200 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1201 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1202 fprintf(stderr, "Could not set debug level.\n");
1206 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1210 static int cmd_retry(int argc, char *argv[]) {
1212 fprintf(stderr, "Too many arguments!\n");
1216 if(!connect_tincd(true))
1219 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1220 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1221 fprintf(stderr, "Could not retry outgoing connections.\n");
1228 static int cmd_connect(int argc, char *argv[]) {
1230 fprintf(stderr, "Invalid number of arguments.\n");
1234 if(!check_id(argv[1])) {
1235 fprintf(stderr, "Invalid name for node.\n");
1239 if(!connect_tincd(true))
1242 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1243 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1244 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1251 static int cmd_disconnect(int argc, char *argv[]) {
1253 fprintf(stderr, "Invalid number of arguments.\n");
1257 if(!check_id(argv[1])) {
1258 fprintf(stderr, "Invalid name for node.\n");
1262 if(!connect_tincd(true))
1265 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1266 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1267 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1274 static int cmd_top(int argc, char *argv[]) {
1276 fprintf(stderr, "Too many arguments!\n");
1281 if(!connect_tincd(true))
1287 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1292 static int cmd_pcap(int argc, char *argv[]) {
1294 fprintf(stderr, "Too many arguments!\n");
1298 if(!connect_tincd(true))
1301 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1306 static void sigint_handler(int sig) {
1307 fprintf(stderr, "\n");
1308 shutdown(fd, SHUT_RDWR);
1312 static int cmd_log(int argc, char *argv[]) {
1314 fprintf(stderr, "Too many arguments!\n");
1318 if(!connect_tincd(true))
1322 signal(SIGINT, sigint_handler);
1325 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1328 signal(SIGINT, SIG_DFL);
1336 static int cmd_pid(int argc, char *argv[]) {
1338 fprintf(stderr, "Too many arguments!\n");
1342 if(!connect_tincd(true) && !pid)
1345 printf("%d\n", pid);
1349 int rstrip(char *value) {
1350 int len = strlen(value);
1351 while(len && strchr("\t\r\n ", value[len - 1]))
1356 char *get_my_name(bool verbose) {
1357 FILE *f = fopen(tinc_conf, "r");
1360 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1366 while(fgets(buf, sizeof buf, f)) {
1367 int len = strcspn(buf, "\t =");
1369 value += strspn(value, "\t ");
1372 value += strspn(value, "\t ");
1377 if(strcasecmp(buf, "Name"))
1381 return replace_name(value);
1387 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1391 const var_t variables[] = {
1392 /* Server configuration */
1393 {"AddressFamily", VAR_SERVER},
1394 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1395 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1396 {"BindToInterface", VAR_SERVER},
1397 {"Broadcast", VAR_SERVER | VAR_SAFE},
1398 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1399 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1400 {"DecrementTTL", VAR_SERVER},
1401 {"Device", VAR_SERVER},
1402 {"DeviceStandby", VAR_SERVER},
1403 {"DeviceType", VAR_SERVER},
1404 {"DirectOnly", VAR_SERVER},
1405 {"Ed25519PrivateKeyFile", VAR_SERVER},
1406 {"ExperimentalProtocol", VAR_SERVER},
1407 {"Forwarding", VAR_SERVER},
1408 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1409 {"Hostnames", VAR_SERVER},
1410 {"IffOneQueue", VAR_SERVER},
1411 {"Interface", VAR_SERVER},
1412 {"KeyExpire", VAR_SERVER},
1413 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1414 {"LocalDiscovery", VAR_SERVER},
1415 {"MACExpire", VAR_SERVER},
1416 {"MaxConnectionBurst", VAR_SERVER},
1417 {"MaxOutputBufferSize", VAR_SERVER},
1418 {"MaxTimeout", VAR_SERVER},
1419 {"Mode", VAR_SERVER | VAR_SAFE},
1420 {"Name", VAR_SERVER},
1421 {"PingInterval", VAR_SERVER},
1422 {"PingTimeout", VAR_SERVER},
1423 {"PriorityInheritance", VAR_SERVER},
1424 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1425 {"PrivateKeyFile", VAR_SERVER},
1426 {"ProcessPriority", VAR_SERVER},
1427 {"Proxy", VAR_SERVER},
1428 {"ReplayWindow", VAR_SERVER},
1429 {"ScriptsExtension", VAR_SERVER},
1430 {"ScriptsInterpreter", VAR_SERVER},
1431 {"StrictSubnets", VAR_SERVER},
1432 {"TunnelServer", VAR_SERVER},
1433 {"UDPDiscovery", VAR_SERVER},
1434 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1435 {"UDPDiscoveryInterval", VAR_SERVER},
1436 {"UDPDiscoveryTimeout", VAR_SERVER},
1437 {"MTUInfoInterval", VAR_SERVER},
1438 {"UDPInfoInterval", VAR_SERVER},
1439 {"UDPRcvBuf", VAR_SERVER},
1440 {"UDPSndBuf", VAR_SERVER},
1441 {"VDEGroup", VAR_SERVER},
1442 {"VDEPort", VAR_SERVER},
1443 /* Host configuration */
1444 {"Address", VAR_HOST | VAR_MULTIPLE},
1445 {"Cipher", VAR_SERVER | VAR_HOST},
1446 {"ClampMSS", VAR_SERVER | VAR_HOST},
1447 {"Compression", VAR_SERVER | VAR_HOST},
1448 {"Digest", VAR_SERVER | VAR_HOST},
1449 {"Ed25519PublicKey", VAR_HOST},
1450 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1451 {"IndirectData", VAR_SERVER | VAR_HOST},
1452 {"MACLength", VAR_SERVER | VAR_HOST},
1453 {"PMTU", VAR_SERVER | VAR_HOST},
1454 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1456 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1457 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1458 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1459 {"TCPOnly", VAR_SERVER | VAR_HOST},
1460 {"Weight", VAR_HOST | VAR_SAFE},
1464 static int cmd_config(int argc, char *argv[]) {
1466 fprintf(stderr, "Invalid number of arguments.\n");
1470 if(strcasecmp(argv[0], "config"))
1474 if(!strcasecmp(argv[1], "get")) {
1476 } else if(!strcasecmp(argv[1], "add")) {
1477 argv++, argc--, action = 1;
1478 } else if(!strcasecmp(argv[1], "del")) {
1479 argv++, argc--, action = -1;
1480 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1481 argv++, argc--, action = 0;
1485 fprintf(stderr, "Invalid number of arguments.\n");
1489 // Concatenate the rest of the command line
1490 strncpy(line, argv[1], sizeof line - 1);
1491 for(int i = 2; i < argc; i++) {
1492 strncat(line, " ", sizeof line - 1 - strlen(line));
1493 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1496 // Liberal parsing into node name, variable name and value.
1502 len = strcspn(line, "\t =");
1504 value += strspn(value, "\t ");
1507 value += strspn(value, "\t ");
1510 variable = strchr(line, '.');
1519 fprintf(stderr, "No variable given.\n");
1523 if(action >= 0 && !*value) {
1524 fprintf(stderr, "No value for variable given.\n");
1528 if(action < -1 && *value)
1531 /* Some simple checks. */
1533 bool warnonremove = false;
1535 for(int i = 0; variables[i].name; i++) {
1536 if(strcasecmp(variables[i].name, variable))
1540 variable = (char *)variables[i].name;
1542 /* Discourage use of obsolete variables. */
1544 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1546 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1548 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1553 /* Don't put server variables in host config files */
1555 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1557 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1559 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1564 /* Should this go into our own host config file? */
1566 if(!node && !(variables[i].type & VAR_SERVER)) {
1567 node = get_my_name(true);
1572 /* Change "add" into "set" for variables that do not allow multiple occurences.
1573 Turn on warnings when it seems variables might be removed unintentionally. */
1575 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1576 warnonremove = true;
1578 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1579 warnonremove = true;
1585 if(node && !check_id(node)) {
1586 fprintf(stderr, "Invalid name for node.\n");
1591 if(force || action < 0) {
1592 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1594 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1599 // Open the right configuration file.
1602 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1604 filename = tinc_conf;
1606 FILE *f = fopen(filename, "r");
1608 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1612 char *tmpfile = NULL;
1616 xasprintf(&tmpfile, "%s.config.tmp", filename);
1617 tf = fopen(tmpfile, "w");
1619 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1625 // Copy the file, making modifications on the fly, unless we are just getting a value.
1629 bool removed = false;
1632 while(fgets(buf1, sizeof buf1, f)) {
1633 buf1[sizeof buf1 - 1] = 0;
1634 strncpy(buf2, buf1, sizeof buf2);
1636 // Parse line in a simple way
1640 len = strcspn(buf2, "\t =");
1641 bvalue = buf2 + len;
1642 bvalue += strspn(bvalue, "\t ");
1643 if(*bvalue == '=') {
1645 bvalue += strspn(bvalue, "\t ");
1651 if(!strcasecmp(buf2, variable)) {
1655 printf("%s\n", bvalue);
1657 } else if(action == -1) {
1658 if(!*value || !strcasecmp(bvalue, value)) {
1663 } else if(action == 0) {
1664 // Warn if "set" was used for variables that can occur multiple times
1665 if(warnonremove && strcasecmp(bvalue, value))
1666 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1668 // Already set? Delete the rest...
1672 // Otherwise, replace.
1673 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1674 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1680 } else if(action > 0) {
1681 // Check if we've already seen this variable with the same value
1682 if(!strcasecmp(bvalue, value))
1688 // Copy original line...
1689 if(fputs(buf1, tf) < 0) {
1690 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1694 // Add newline if it is missing...
1695 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1696 if(fputc('\n', tf) < 0) {
1697 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1704 // Make sure we read everything...
1705 if(ferror(f) || !feof(f)) {
1706 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1711 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1715 // Add new variable if necessary.
1716 if((action > 0 && !found)|| (action == 0 && !set)) {
1717 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1718 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1727 fprintf(stderr, "No matching configuration variables found.\n");
1732 // Make sure we wrote everything...
1734 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1738 // Could we find what we had to remove?
1739 if(action < 0 && !removed) {
1741 fprintf(stderr, "No configuration variables deleted.\n");
1745 // Replace the configuration file with the new one
1747 if(remove(filename)) {
1748 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1752 if(rename(tmpfile, filename)) {
1753 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1757 // Silently try notifying a running tincd of changes.
1758 if(connect_tincd(false))
1759 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1764 static bool try_bind(int port) {
1765 struct addrinfo *ai = NULL;
1766 struct addrinfo hint = {
1767 .ai_flags = AI_PASSIVE,
1768 .ai_family = AF_UNSPEC,
1769 .ai_socktype = SOCK_STREAM,
1770 .ai_protocol = IPPROTO_TCP,
1774 snprintf(portstr, sizeof portstr, "%d", port);
1776 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1780 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1783 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1793 int check_port(char *name) {
1797 fprintf(stderr, "Warning: could not bind to port 655. ");
1799 for(int i = 0; i < 100; i++) {
1800 int port = 0x1000 + (rand() & 0x7fff);
1801 if(try_bind(port)) {
1803 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1804 FILE *f = fopen(filename, "a");
1807 fprintf(stderr, "Please change tinc's Port manually.\n");
1811 fprintf(f, "Port = %d\n", port);
1813 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1818 fprintf(stderr, "Please change tinc's Port manually.\n");
1822 static int cmd_init(int argc, char *argv[]) {
1823 if(!access(tinc_conf, F_OK)) {
1824 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1829 fprintf(stderr, "Too many arguments!\n");
1831 } else if(argc < 2) {
1834 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1835 if(!fgets(buf, sizeof buf, stdin)) {
1836 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1839 int len = rstrip(buf);
1841 fprintf(stderr, "No name given!\n");
1846 fprintf(stderr, "No Name given!\n");
1850 name = strdup(argv[1]);
1852 fprintf(stderr, "No Name given!\n");
1857 if(!check_id(name)) {
1858 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1862 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1863 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1867 if(mkdir(confbase, 0777) && errno != EEXIST) {
1868 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1872 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1873 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1877 FILE *f = fopen(tinc_conf, "w");
1879 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1883 fprintf(f, "Name = %s\n", name);
1886 #ifndef DISABLE_LEGACY
1887 if(!rsa_keygen(2048, false))
1891 if(!ed25519_keygen(false))
1898 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1899 if(access(filename, F_OK)) {
1900 FILE *f = fopenmask(filename, "w", 0777);
1902 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1905 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");
1914 static int cmd_generate_keys(int argc, char *argv[]) {
1915 #ifdef DISABLE_LEGACY
1920 fprintf(stderr, "Too many arguments!\n");
1925 name = get_my_name(false);
1927 #ifndef DISABLE_LEGACY
1928 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1932 if(!ed25519_keygen(true))
1938 #ifndef DISABLE_LEGACY
1939 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1941 fprintf(stderr, "Too many arguments!\n");
1946 name = get_my_name(false);
1948 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1952 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1954 fprintf(stderr, "Too many arguments!\n");
1959 name = get_my_name(false);
1961 return !ed25519_keygen(true);
1964 static int cmd_help(int argc, char *argv[]) {
1969 static int cmd_version(int argc, char *argv[]) {
1971 fprintf(stderr, "Too many arguments!\n");
1979 static int cmd_info(int argc, char *argv[]) {
1981 fprintf(stderr, "Invalid number of arguments.\n");
1985 if(!connect_tincd(true))
1988 return info(fd, argv[1]);
1991 static const char *conffiles[] = {
2002 static int cmd_edit(int argc, char *argv[]) {
2004 fprintf(stderr, "Invalid number of arguments.\n");
2008 char *filename = NULL;
2010 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2011 for(int i = 0; conffiles[i]; i++) {
2012 if(!strcmp(argv[1], conffiles[i])) {
2013 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
2022 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2023 char *dash = strchr(argv[1], '-');
2026 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2027 fprintf(stderr, "Invalid configuration filename.\n");
2035 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2037 xasprintf(&command, "edit \"%s\"", filename);
2039 int result = system(command);
2043 // Silently try notifying a running tincd of changes.
2044 if(connect_tincd(false))
2045 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2050 static int export(const char *name, FILE *out) {
2052 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2053 FILE *in = fopen(filename, "r");
2055 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2059 fprintf(out, "Name = %s\n", name);
2061 while(fgets(buf, sizeof buf, in)) {
2062 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2067 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2076 static int cmd_export(int argc, char *argv[]) {
2078 fprintf(stderr, "Too many arguments!\n");
2082 char *name = get_my_name(true);
2086 int result = export(name, stdout);
2094 static int cmd_export_all(int argc, char *argv[]) {
2096 fprintf(stderr, "Too many arguments!\n");
2100 DIR *dir = opendir(hosts_dir);
2102 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2110 while((ent = readdir(dir))) {
2111 if(!check_id(ent->d_name))
2117 printf("#---------------------------------------------------------------#\n");
2119 result |= export(ent->d_name, stdout);
2128 static int cmd_import(int argc, char *argv[]) {
2130 fprintf(stderr, "Too many arguments!\n");
2139 char *filename = NULL;
2141 bool firstline = true;
2143 while(fgets(buf, sizeof buf, in)) {
2144 if(sscanf(buf, "Name = %s", name) == 1) {
2147 if(!check_id(name)) {
2148 fprintf(stderr, "Invalid Name in input!\n");
2156 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2158 if(!force && !access(filename, F_OK)) {
2159 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2164 out = fopen(filename, "w");
2166 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2172 } else if(firstline) {
2173 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2178 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2182 if(fputs(buf, out) < 0) {
2183 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2193 fprintf(stderr, "Imported %d host configuration files.\n", count);
2196 fprintf(stderr, "No host configuration files imported.\n");
2201 static int cmd_exchange(int argc, char *argv[]) {
2202 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2205 static int cmd_exchange_all(int argc, char *argv[]) {
2206 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2209 static int switch_network(char *name) {
2221 free(unixsocketname);
2222 unixsocketname = NULL;
2228 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2230 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2231 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2232 xasprintf(&prompt, "%s> ", identname);
2237 static int cmd_network(int argc, char *argv[]) {
2239 fprintf(stderr, "Too many arguments!\n");
2244 return switch_network(argv[1]);
2246 DIR *dir = opendir(confdir);
2248 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2253 while((ent = readdir(dir))) {
2254 if(*ent->d_name == '.')
2257 if(!strcmp(ent->d_name, "tinc.conf")) {
2263 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2264 if(!access(fname, R_OK))
2265 printf("%s\n", ent->d_name);
2274 static int cmd_fsck(int argc, char *argv[]) {
2276 fprintf(stderr, "Too many arguments!\n");
2280 return fsck(orig_argv[0]);
2283 static const struct {
2284 const char *command;
2285 int (*function)(int argc, char *argv[]);
2288 {"start", cmd_start},
2290 {"restart", cmd_restart},
2291 {"reload", cmd_reload},
2294 {"purge", cmd_purge},
2295 {"debug", cmd_debug},
2296 {"retry", cmd_retry},
2297 {"connect", cmd_connect},
2298 {"disconnect", cmd_disconnect},
2303 {"config", cmd_config, true},
2304 {"add", cmd_config},
2305 {"del", cmd_config},
2306 {"get", cmd_config},
2307 {"set", cmd_config},
2309 {"generate-keys", cmd_generate_keys},
2310 #ifndef DISABLE_LEGACY
2311 {"generate-rsa-keys", cmd_generate_rsa_keys},
2313 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2315 {"version", cmd_version},
2318 {"export", cmd_export},
2319 {"export-all", cmd_export_all},
2320 {"import", cmd_import},
2321 {"exchange", cmd_exchange},
2322 {"exchange-all", cmd_exchange_all},
2323 {"invite", cmd_invite},
2325 {"network", cmd_network},
2330 #ifdef HAVE_READLINE
2331 static char *complete_command(const char *text, int state) {
2339 while(commands[i].command) {
2340 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2341 return xstrdup(commands[i].command);
2348 static char *complete_dump(const char *text, int state) {
2349 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2358 if(!strncasecmp(matches[i], text, strlen(text)))
2359 return xstrdup(matches[i]);
2366 static char *complete_config(const char *text, int state) {
2374 while(variables[i].name) {
2375 char *dot = strchr(text, '.');
2377 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2379 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2383 if(!strncasecmp(variables[i].name, text, strlen(text)))
2384 return xstrdup(variables[i].name);
2392 static char *complete_info(const char *text, int state) {
2396 if(!connect_tincd(false))
2398 // Check the list of nodes
2399 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2400 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2403 while(recvline(fd, line, sizeof line)) {
2405 int n = sscanf(line, "%d %d %s", &code, &req, item);
2415 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2419 if(!strncmp(item, text, strlen(text)))
2420 return xstrdup(strip_weight(item));
2426 static char *complete_nothing(const char *text, int state) {
2430 static char **completion (const char *text, int start, int end) {
2431 char **matches = NULL;
2434 matches = rl_completion_matches(text, complete_command);
2435 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2436 matches = rl_completion_matches(text, complete_dump);
2437 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2438 matches = rl_completion_matches(text, complete_config);
2439 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2440 matches = rl_completion_matches(text, complete_config);
2441 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2442 matches = rl_completion_matches(text, complete_config);
2443 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2444 matches = rl_completion_matches(text, complete_config);
2445 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2446 matches = rl_completion_matches(text, complete_info);
2452 static int cmd_shell(int argc, char *argv[]) {
2453 xasprintf(&prompt, "%s> ", identname);
2457 int maxargs = argc + 16;
2458 char **nargv = xmalloc(maxargs * sizeof *nargv);
2460 for(int i = 0; i < argc; i++)
2463 #ifdef HAVE_READLINE
2464 rl_readline_name = "tinc";
2465 rl_completion_entry_function = complete_nothing;
2466 rl_attempted_completion_function = completion;
2467 rl_filename_completion_desired = 0;
2472 #ifdef HAVE_READLINE
2476 rl_basic_word_break_characters = "\t\n ";
2477 line = readline(prompt);
2479 copy = xstrdup(line);
2481 line = fgets(buf, sizeof buf, stdin);
2485 fputs(prompt, stdout);
2487 line = fgets(buf, sizeof buf, stdin);
2493 /* Ignore comments */
2501 char *p = line + strspn(line, " \t\n");
2502 char *next = strtok(p, " \t\n");
2505 if(nargc >= maxargs) {
2506 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2509 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2514 next = strtok(NULL, " \t\n");
2520 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2525 for(int i = 0; commands[i].command; i++) {
2526 if(!strcasecmp(nargv[argc], commands[i].command)) {
2527 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2533 #ifdef HAVE_READLINE
2539 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2552 int main(int argc, char *argv[]) {
2553 program_name = argv[0];
2556 tty = isatty(0) && isatty(1);
2558 if(!parse_options(argc, argv))
2562 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2563 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2576 static struct WSAData wsa_state;
2578 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2579 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2588 return cmd_shell(argc, argv);
2590 for(int i = 0; commands[i].command; i++) {
2591 if(!strcasecmp(argv[optind], commands[i].command))
2592 return commands[i].function(argc - optind, argv + optind);
2595 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);