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-2015 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);
238 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
243 if((perms & 0444) && f)
244 fchmod(fileno(f), perms);
250 static void disable_old_keys(const char *filename, const char *what) {
251 char tmpfile[PATH_MAX] = "";
253 bool disabled = false;
258 r = fopen(filename, "r");
262 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
264 struct stat st = {.st_mode = 0600};
265 fstat(fileno(r), &st);
266 w = fopenmask(tmpfile, "w", st.st_mode);
268 while(fgets(buf, sizeof buf, r)) {
269 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
270 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
276 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
282 if(block || ed25519pubkey)
284 if(fputs(buf, w) < 0) {
290 if(block && !strncmp(buf, "-----END ", 9))
297 if(ferror(r) || fclose(r) < 0)
302 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
309 // We cannot atomically replace files on Windows.
310 char bakfile[PATH_MAX] = "";
311 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
312 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
313 rename(bakfile, filename);
315 if(rename(tmpfile, filename)) {
317 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
322 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
329 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
335 /* Check stdin and stdout */
337 /* Ask for a file and/or directory name. */
338 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
340 if(fgets(buf, sizeof buf, stdin) == NULL) {
341 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
345 size_t len = strlen(buf);
354 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
356 if(filename[0] != '/') {
358 /* The directory is a relative path or a filename. */
359 directory = get_current_dir_name();
360 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
364 disable_old_keys(filename, what);
366 /* Open it first to keep the inode busy */
368 r = fopenmask(filename, mode, perms);
371 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
379 Generate a public/private Ed25519 keypair, and ask for a file to store
382 static bool ed25519_keygen(bool ask) {
385 char fname[PATH_MAX];
387 fprintf(stderr, "Generating Ed25519 keypair:\n");
389 if(!(key = ecdsa_generate())) {
390 fprintf(stderr, "Error during key generation!\n");
393 fprintf(stderr, "Done.\n");
395 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
396 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
401 if(!ecdsa_write_pem_private_key(key, f)) {
402 fprintf(stderr, "Error writing private key!\n");
409 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
411 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.pub", confbase);
413 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
418 char *pubkey = ecdsa_get_base64_public_key(key);
419 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
434 #ifndef DISABLE_LEGACY
436 Generate a public/private RSA keypair, and ask for a file to store
439 static bool rsa_keygen(int bits, bool ask) {
442 char fname[PATH_MAX];
444 // Make sure the key size is a multiple of 8 bits.
447 // Force them to be between 1024 and 8192 bits long.
453 fprintf(stderr, "Generating %d bits keys:\n", bits);
455 if(!(key = rsa_generate(bits, 0x10001))) {
456 fprintf(stderr, "Error during key generation!\n");
459 fprintf(stderr, "Done.\n");
461 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.priv", confbase);
462 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
467 if(!rsa_write_pem_private_key(key, f)) {
468 fprintf(stderr, "Error writing private key!\n");
475 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
477 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.pub", confbase);
479 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
484 if(!rsa_write_pem_public_key(key, f)) {
485 fprintf(stderr, "Error writing public key!\n");
505 bool recvline(int fd, char *line, size_t len) {
506 char *newline = NULL;
511 while(!(newline = memchr(buffer, '\n', blen))) {
512 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
513 if(result == -1 && sockerrno == EINTR)
520 if(newline - buffer >= len)
523 len = newline - buffer;
525 memcpy(line, buffer, len);
527 memmove(buffer, newline + 1, blen - len - 1);
533 bool recvdata(int fd, char *data, size_t len) {
538 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
539 if(result == -1 && sockerrno == EINTR)
546 memcpy(data, buffer, len);
547 memmove(buffer, buffer + len, blen - len);
553 bool sendline(int fd, char *format, ...) {
554 static char buffer[4096];
559 va_start(ap, format);
560 blen = vsnprintf(buffer, sizeof buffer, format, ap);
563 if(blen < 1 || blen >= sizeof buffer)
570 int result = send(fd, p, blen, MSG_NOSIGNAL);
571 if(result == -1 && sockerrno == EINTR)
582 static void pcap(int fd, FILE *out, int snaplen) {
583 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
591 uint32_t tz_accuracy;
598 snaplen ?: sizeof data,
611 fwrite(&header, sizeof header, 1, out);
615 while(recvline(fd, line, sizeof line)) {
617 int n = sscanf(line, "%d %d %d", &code, &req, &len);
618 gettimeofday(&tv, NULL);
619 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
621 if(!recvdata(fd, data, len))
623 packet.tv_sec = tv.tv_sec;
624 packet.tv_usec = tv.tv_usec;
626 packet.origlen = len;
627 fwrite(&packet, sizeof packet, 1, out);
628 fwrite(data, len, 1, out);
633 static void logcontrol(int fd, FILE *out, int level) {
634 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
638 while(recvline(fd, line, sizeof line)) {
640 int n = sscanf(line, "%d %d %d", &code, &req, &len);
641 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
643 if(!recvdata(fd, data, len))
645 fwrite(data, len, 1, out);
652 static bool remove_service(void) {
653 SC_HANDLE manager = NULL;
654 SC_HANDLE service = NULL;
655 SERVICE_STATUS status = {0};
657 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
659 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
663 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
666 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
670 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
671 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
673 fprintf(stderr, "%s service stopped\n", identname);
675 if(!DeleteService(service)) {
676 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
680 fprintf(stderr, "%s service removed\n", identname);
686 bool connect_tincd(bool verbose) {
691 struct timeval tv = {0, 0};
692 if(select(fd + 1, &r, NULL, NULL, &tv)) {
693 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
701 FILE *f = fopen(pidfilename, "r");
704 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
711 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
713 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
721 struct sockaddr_un sa;
722 sa.sun_family = AF_UNIX;
723 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
725 fd = socket(AF_UNIX, SOCK_STREAM, 0);
728 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
732 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
734 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
740 struct addrinfo hints = {
741 .ai_family = AF_UNSPEC,
742 .ai_socktype = SOCK_STREAM,
743 .ai_protocol = IPPROTO_TCP,
747 struct addrinfo *res = NULL;
749 if(getaddrinfo(host, port, &hints, &res) || !res) {
751 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
755 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
758 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
763 unsigned long arg = 0;
765 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
767 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
771 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
773 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
783 static const int one = 1;
784 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
790 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
792 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
798 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
800 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
802 fprintf(stderr, "Could not fully establish control socket connection\n");
812 static int cmd_start(int argc, char *argv[]) {
813 if(connect_tincd(false)) {
815 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
817 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
822 char *slash = strrchr(program_name, '/');
825 if ((c = strrchr(program_name, '\\')) > slash)
830 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
835 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
840 Windows has no real concept of an "argv array". A command line is just one string.
841 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
842 it uses quotes to handle spaces in arguments.
843 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
844 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
845 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
847 xasprintf(&arg0, "\"%s\"", arg0);
849 nargv[nargc++] = arg0;
850 for(int i = 1; i < optind; i++)
851 nargv[nargc++] = orig_argv[i];
852 for(int i = 1; i < argc; i++)
853 nargv[nargc++] = argv[i];
856 int status = spawnvp(_P_WAIT, c, nargv);
858 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
863 int pfd[2] = {-1, -1};
864 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
865 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
872 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
880 snprintf(buf, sizeof buf, "%d", pfd[1]);
881 setenv("TINC_UMBILICAL", buf, true);
882 exit(execvp(c, nargv));
889 int status = -1, result;
891 signal(SIGINT, SIG_IGN);
894 // Pass all log messages from the umbilical to stderr.
895 // A nul-byte right before closure means tincd started succesfully.
900 while((len = read(pfd[0], buf, sizeof buf)) > 0) {
901 failure = buf[len - 1];
912 // Make sure the child process is really gone.
913 result = waitpid(pid, &status, 0);
916 signal(SIGINT, SIG_DFL);
919 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
920 fprintf(stderr, "Error starting %s\n", c);
928 static int cmd_stop(int argc, char *argv[]) {
930 fprintf(stderr, "Too many arguments!\n");
935 if(!connect_tincd(true)) {
937 if(kill(pid, SIGTERM)) {
938 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
942 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
943 waitpid(pid, NULL, 0);
950 sendline(fd, "%d %d", CONTROL, REQ_STOP);
952 while(recvline(fd, line, sizeof line)) {
953 // Wait for tincd to close the connection...
956 if(!remove_service())
966 static int cmd_restart(int argc, char *argv[]) {
968 return cmd_start(argc, argv);
971 static int cmd_reload(int argc, char *argv[]) {
973 fprintf(stderr, "Too many arguments!\n");
977 if(!connect_tincd(true))
980 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
981 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
982 fprintf(stderr, "Could not reload configuration.\n");
990 static int dump_invitations(void) {
991 char dname[PATH_MAX];
992 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
993 DIR *dir = opendir(dname);
995 if(errno == ENOENT) {
996 fprintf(stderr, "No outstanding invitations.\n");
1000 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1007 while((ent = readdir(dir))) {
1008 char buf[MAX_STRING_SIZE];
1009 if(b64decode(ent->d_name, buf, 24) != 18)
1012 char fname[PATH_MAX];
1013 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
1014 FILE *f = fopen(fname, "r");
1016 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1022 if(!fgets(buf, sizeof buf, f)) {
1023 fprintf(stderr, "Invalid invitation file %s", fname);
1029 char *eol = buf + strlen(buf);
1030 while(strchr("\t \r\n", *--eol))
1032 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1033 fprintf(stderr, "Invalid invitation file %s", fname);
1038 printf("%s %s\n", ent->d_name, buf + 7);
1044 fprintf(stderr, "No outstanding invitations.\n");
1049 static int cmd_dump(int argc, char *argv[]) {
1050 bool only_reachable = false;
1052 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1053 if(strcasecmp(argv[2], "nodes")) {
1054 fprintf(stderr, "`reachable' only supported for nodes.\n");
1058 only_reachable = true;
1064 fprintf(stderr, "Invalid number of arguments.\n");
1069 if(!strcasecmp(argv[1], "invitations"))
1070 return dump_invitations();
1072 if(!connect_tincd(true))
1077 if(!strcasecmp(argv[1], "nodes"))
1078 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1079 else if(!strcasecmp(argv[1], "edges"))
1080 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1081 else if(!strcasecmp(argv[1], "subnets"))
1082 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1083 else if(!strcasecmp(argv[1], "connections"))
1084 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1085 else if(!strcasecmp(argv[1], "graph")) {
1086 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1087 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1089 } else if(!strcasecmp(argv[1], "digraph")) {
1090 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1091 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1094 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1100 printf("graph {\n");
1101 else if(do_graph == 2)
1102 printf("digraph {\n");
1104 while(recvline(fd, line, sizeof line)) {
1105 char node1[4096], node2[4096];
1106 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1108 if(do_graph && req == REQ_DUMP_NODES)
1126 char local_host[4096];
1127 char local_port[4096];
1130 int cipher, digest, maclength, compression, distance, socket, weight;
1131 short int pmtu, minmtu, maxmtu;
1132 unsigned int options, status_int;
1133 node_status_t status;
1134 long int last_state_change;
1137 case REQ_DUMP_NODES: {
1138 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);
1140 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1144 memcpy(&status, &status_int, sizeof status);
1147 const char *color = "black";
1148 if(!strcmp(host, "MYSELF"))
1150 else if(!status.reachable)
1152 else if(strcmp(via, node))
1154 else if(!status.validkey)
1158 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1160 if(only_reachable && !status.reachable)
1162 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",
1163 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1167 case REQ_DUMP_EDGES: {
1168 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);
1170 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1175 float w = 1 + 65536.0 / weight;
1176 if(do_graph == 1 && strcmp(node1, node2) > 0)
1177 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1178 else if(do_graph == 2)
1179 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1181 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);
1185 case REQ_DUMP_SUBNETS: {
1186 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1188 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1191 printf("%s owner %s\n", strip_weight(subnet), node);
1194 case REQ_DUMP_CONNECTIONS: {
1195 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1197 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1200 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1204 fprintf(stderr, "Unable to parse dump from tincd.\n");
1209 fprintf(stderr, "Error receiving dump.\n");
1213 static int cmd_purge(int argc, char *argv[]) {
1215 fprintf(stderr, "Too many arguments!\n");
1219 if(!connect_tincd(true))
1222 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1223 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1224 fprintf(stderr, "Could not purge old information.\n");
1231 static int cmd_debug(int argc, char *argv[]) {
1233 fprintf(stderr, "Invalid number of arguments.\n");
1237 if(!connect_tincd(true))
1240 int debuglevel = atoi(argv[1]);
1243 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1244 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1245 fprintf(stderr, "Could not set debug level.\n");
1249 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1253 static int cmd_retry(int argc, char *argv[]) {
1255 fprintf(stderr, "Too many arguments!\n");
1259 if(!connect_tincd(true))
1262 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1263 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1264 fprintf(stderr, "Could not retry outgoing connections.\n");
1271 static int cmd_connect(int argc, char *argv[]) {
1273 fprintf(stderr, "Invalid number of arguments.\n");
1277 if(!check_id(argv[1])) {
1278 fprintf(stderr, "Invalid name for node.\n");
1282 if(!connect_tincd(true))
1285 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1286 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1287 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1294 static int cmd_disconnect(int argc, char *argv[]) {
1296 fprintf(stderr, "Invalid number of arguments.\n");
1300 if(!check_id(argv[1])) {
1301 fprintf(stderr, "Invalid name for node.\n");
1305 if(!connect_tincd(true))
1308 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1309 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1310 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1317 static int cmd_top(int argc, char *argv[]) {
1319 fprintf(stderr, "Too many arguments!\n");
1324 if(!connect_tincd(true))
1330 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1335 static int cmd_pcap(int argc, char *argv[]) {
1337 fprintf(stderr, "Too many arguments!\n");
1341 if(!connect_tincd(true))
1344 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1349 static void sigint_handler(int sig) {
1350 fprintf(stderr, "\n");
1351 shutdown(fd, SHUT_RDWR);
1355 static int cmd_log(int argc, char *argv[]) {
1357 fprintf(stderr, "Too many arguments!\n");
1361 if(!connect_tincd(true))
1365 signal(SIGINT, sigint_handler);
1368 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1371 signal(SIGINT, SIG_DFL);
1379 static int cmd_pid(int argc, char *argv[]) {
1381 fprintf(stderr, "Too many arguments!\n");
1385 if(!connect_tincd(true) && !pid)
1388 printf("%d\n", pid);
1392 int rstrip(char *value) {
1393 int len = strlen(value);
1394 while(len && strchr("\t\r\n ", value[len - 1]))
1399 char *get_my_name(bool verbose) {
1400 FILE *f = fopen(tinc_conf, "r");
1403 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1409 while(fgets(buf, sizeof buf, f)) {
1410 int len = strcspn(buf, "\t =");
1412 value += strspn(value, "\t ");
1415 value += strspn(value, "\t ");
1420 if(strcasecmp(buf, "Name"))
1424 return replace_name(value);
1430 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1434 const var_t variables[] = {
1435 /* Server configuration */
1436 {"AddressFamily", VAR_SERVER},
1437 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1438 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1439 {"BindToInterface", VAR_SERVER},
1440 {"Broadcast", VAR_SERVER | VAR_SAFE},
1441 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1442 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1443 {"DecrementTTL", VAR_SERVER},
1444 {"Device", VAR_SERVER},
1445 {"DeviceStandby", VAR_SERVER},
1446 {"DeviceType", VAR_SERVER},
1447 {"DirectOnly", VAR_SERVER},
1448 {"Ed25519PrivateKeyFile", VAR_SERVER},
1449 {"ExperimentalProtocol", VAR_SERVER},
1450 {"Forwarding", VAR_SERVER},
1451 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1452 {"Hostnames", VAR_SERVER},
1453 {"IffOneQueue", VAR_SERVER},
1454 {"Interface", VAR_SERVER},
1455 {"KeyExpire", VAR_SERVER},
1456 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1457 {"LocalDiscovery", VAR_SERVER},
1458 {"MACExpire", VAR_SERVER},
1459 {"MaxConnectionBurst", VAR_SERVER},
1460 {"MaxOutputBufferSize", VAR_SERVER},
1461 {"MaxTimeout", VAR_SERVER},
1462 {"Mode", VAR_SERVER | VAR_SAFE},
1463 {"Name", VAR_SERVER},
1464 {"PingInterval", VAR_SERVER},
1465 {"PingTimeout", VAR_SERVER},
1466 {"PriorityInheritance", VAR_SERVER},
1467 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1468 {"PrivateKeyFile", VAR_SERVER},
1469 {"ProcessPriority", VAR_SERVER},
1470 {"Proxy", VAR_SERVER},
1471 {"ReplayWindow", VAR_SERVER},
1472 {"ScriptsExtension", VAR_SERVER},
1473 {"ScriptsInterpreter", VAR_SERVER},
1474 {"StrictSubnets", VAR_SERVER},
1475 {"TunnelServer", VAR_SERVER},
1476 {"UDPDiscovery", VAR_SERVER},
1477 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1478 {"UDPDiscoveryInterval", VAR_SERVER},
1479 {"UDPDiscoveryTimeout", VAR_SERVER},
1480 {"MTUInfoInterval", VAR_SERVER},
1481 {"UDPInfoInterval", VAR_SERVER},
1482 {"UDPRcvBuf", VAR_SERVER},
1483 {"UDPSndBuf", VAR_SERVER},
1484 {"UPnP", VAR_SERVER},
1485 {"UPnPDiscoverWait", VAR_SERVER},
1486 {"UPnPRefreshPeriod", VAR_SERVER},
1487 {"VDEGroup", VAR_SERVER},
1488 {"VDEPort", VAR_SERVER},
1489 /* Host configuration */
1490 {"Address", VAR_HOST | VAR_MULTIPLE},
1491 {"Cipher", VAR_SERVER | VAR_HOST},
1492 {"ClampMSS", VAR_SERVER | VAR_HOST},
1493 {"Compression", VAR_SERVER | VAR_HOST},
1494 {"Digest", VAR_SERVER | VAR_HOST},
1495 {"Ed25519PublicKey", VAR_HOST},
1496 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1497 {"IndirectData", VAR_SERVER | VAR_HOST},
1498 {"MACLength", VAR_SERVER | VAR_HOST},
1499 {"PMTU", VAR_SERVER | VAR_HOST},
1500 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1502 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1503 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1504 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1505 {"TCPOnly", VAR_SERVER | VAR_HOST},
1506 {"Weight", VAR_HOST | VAR_SAFE},
1510 static int cmd_config(int argc, char *argv[]) {
1512 fprintf(stderr, "Invalid number of arguments.\n");
1516 if(strcasecmp(argv[0], "config"))
1520 if(!strcasecmp(argv[1], "get")) {
1522 } else if(!strcasecmp(argv[1], "add")) {
1523 argv++, argc--, action = 1;
1524 } else if(!strcasecmp(argv[1], "del")) {
1525 argv++, argc--, action = -1;
1526 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1527 argv++, argc--, action = 0;
1531 fprintf(stderr, "Invalid number of arguments.\n");
1535 // Concatenate the rest of the command line
1536 strncpy(line, argv[1], sizeof line - 1);
1537 for(int i = 2; i < argc; i++) {
1538 strncat(line, " ", sizeof line - 1 - strlen(line));
1539 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1542 // Liberal parsing into node name, variable name and value.
1548 len = strcspn(line, "\t =");
1550 value += strspn(value, "\t ");
1553 value += strspn(value, "\t ");
1556 variable = strchr(line, '.');
1565 fprintf(stderr, "No variable given.\n");
1569 if(action >= 0 && !*value) {
1570 fprintf(stderr, "No value for variable given.\n");
1574 if(action < -1 && *value)
1577 /* Some simple checks. */
1579 bool warnonremove = false;
1581 for(int i = 0; variables[i].name; i++) {
1582 if(strcasecmp(variables[i].name, variable))
1586 variable = (char *)variables[i].name;
1588 /* Discourage use of obsolete variables. */
1590 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1592 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1594 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1599 /* Don't put server variables in host config files */
1601 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1603 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1605 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1610 /* Should this go into our own host config file? */
1612 if(!node && !(variables[i].type & VAR_SERVER)) {
1613 node = get_my_name(true);
1618 /* Change "add" into "set" for variables that do not allow multiple occurences.
1619 Turn on warnings when it seems variables might be removed unintentionally. */
1621 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1622 warnonremove = true;
1624 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1625 warnonremove = true;
1631 if(node && !check_id(node)) {
1632 fprintf(stderr, "Invalid name for node.\n");
1637 if(force || action < 0) {
1638 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1640 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1645 // Open the right configuration file.
1646 char filename[PATH_MAX];
1648 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1650 snprintf(filename, sizeof filename, "%s", tinc_conf);
1652 FILE *f = fopen(filename, "r");
1654 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1658 char tmpfile[PATH_MAX];
1662 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1663 tf = fopen(tmpfile, "w");
1665 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1671 // Copy the file, making modifications on the fly, unless we are just getting a value.
1675 bool removed = false;
1678 while(fgets(buf1, sizeof buf1, f)) {
1679 buf1[sizeof buf1 - 1] = 0;
1680 strncpy(buf2, buf1, sizeof buf2);
1682 // Parse line in a simple way
1686 len = strcspn(buf2, "\t =");
1687 bvalue = buf2 + len;
1688 bvalue += strspn(bvalue, "\t ");
1689 if(*bvalue == '=') {
1691 bvalue += strspn(bvalue, "\t ");
1697 if(!strcasecmp(buf2, variable)) {
1701 printf("%s\n", bvalue);
1703 } else if(action == -1) {
1704 if(!*value || !strcasecmp(bvalue, value)) {
1709 } else if(action == 0) {
1710 // Warn if "set" was used for variables that can occur multiple times
1711 if(warnonremove && strcasecmp(bvalue, value))
1712 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1714 // Already set? Delete the rest...
1718 // Otherwise, replace.
1719 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1720 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1726 } else if(action > 0) {
1727 // Check if we've already seen this variable with the same value
1728 if(!strcasecmp(bvalue, value))
1734 // Copy original line...
1735 if(fputs(buf1, tf) < 0) {
1736 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1740 // Add newline if it is missing...
1741 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1742 if(fputc('\n', tf) < 0) {
1743 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1750 // Make sure we read everything...
1751 if(ferror(f) || !feof(f)) {
1752 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1757 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1761 // Add new variable if necessary.
1762 if((action > 0 && !found)|| (action == 0 && !set)) {
1763 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1764 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1773 fprintf(stderr, "No matching configuration variables found.\n");
1778 // Make sure we wrote everything...
1780 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1784 // Could we find what we had to remove?
1785 if(action < 0 && !removed) {
1787 fprintf(stderr, "No configuration variables deleted.\n");
1791 // Replace the configuration file with the new one
1793 if(remove(filename)) {
1794 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1798 if(rename(tmpfile, filename)) {
1799 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1803 // Silently try notifying a running tincd of changes.
1804 if(connect_tincd(false))
1805 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1810 static bool try_bind(int port) {
1811 struct addrinfo *ai = NULL, *aip;
1812 struct addrinfo hint = {
1813 .ai_flags = AI_PASSIVE,
1814 .ai_family = AF_UNSPEC,
1815 .ai_socktype = SOCK_STREAM,
1816 .ai_protocol = IPPROTO_TCP,
1819 bool success = true;
1821 snprintf(portstr, sizeof portstr, "%d", port);
1823 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1826 for(aip = ai; aip; aip = aip->ai_next) {
1827 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1833 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1845 int check_port(char *name) {
1849 fprintf(stderr, "Warning: could not bind to port 655. ");
1851 for(int i = 0; i < 100; i++) {
1852 int port = 0x1000 + (rand() & 0x7fff);
1853 if(try_bind(port)) {
1854 char filename[PATH_MAX];
1855 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1856 FILE *f = fopen(filename, "a");
1858 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1859 fprintf(stderr, "Please change tinc's Port manually.\n");
1863 fprintf(f, "Port = %d\n", port);
1865 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1870 fprintf(stderr, "Please change tinc's Port manually.\n");
1874 static int cmd_init(int argc, char *argv[]) {
1875 if(!access(tinc_conf, F_OK)) {
1876 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1881 fprintf(stderr, "Too many arguments!\n");
1883 } else if(argc < 2) {
1886 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1887 if(!fgets(buf, sizeof buf, stdin)) {
1888 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1891 int len = rstrip(buf);
1893 fprintf(stderr, "No name given!\n");
1898 fprintf(stderr, "No Name given!\n");
1902 name = strdup(argv[1]);
1904 fprintf(stderr, "No Name given!\n");
1909 if(!check_id(name)) {
1910 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1914 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1915 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1919 if(mkdir(confbase, 0777) && errno != EEXIST) {
1920 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1924 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1925 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1929 FILE *f = fopen(tinc_conf, "w");
1931 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1935 fprintf(f, "Name = %s\n", name);
1938 #ifndef DISABLE_LEGACY
1939 if(!rsa_keygen(2048, false))
1943 if(!ed25519_keygen(false))
1949 char filename[PATH_MAX];
1950 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1951 if(access(filename, F_OK)) {
1952 FILE *f = fopenmask(filename, "w", 0777);
1954 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1957 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");
1966 static int cmd_generate_keys(int argc, char *argv[]) {
1967 #ifdef DISABLE_LEGACY
1972 fprintf(stderr, "Too many arguments!\n");
1977 name = get_my_name(false);
1979 #ifndef DISABLE_LEGACY
1980 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1984 if(!ed25519_keygen(true))
1990 #ifndef DISABLE_LEGACY
1991 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1993 fprintf(stderr, "Too many arguments!\n");
1998 name = get_my_name(false);
2000 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2004 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2006 fprintf(stderr, "Too many arguments!\n");
2011 name = get_my_name(false);
2013 return !ed25519_keygen(true);
2016 static int cmd_help(int argc, char *argv[]) {
2021 static int cmd_version(int argc, char *argv[]) {
2023 fprintf(stderr, "Too many arguments!\n");
2031 static int cmd_info(int argc, char *argv[]) {
2033 fprintf(stderr, "Invalid number of arguments.\n");
2037 if(!connect_tincd(true))
2040 return info(fd, argv[1]);
2043 static const char *conffiles[] = {
2054 static int cmd_edit(int argc, char *argv[]) {
2056 fprintf(stderr, "Invalid number of arguments.\n");
2060 char filename[PATH_MAX] = "";
2062 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2063 for(int i = 0; conffiles[i]; i++) {
2064 if(!strcmp(argv[1], conffiles[i])) {
2065 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2074 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2075 char *dash = strchr(argv[1], '-');
2078 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2079 fprintf(stderr, "Invalid configuration filename.\n");
2087 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2089 xasprintf(&command, "edit \"%s\"", filename);
2091 int result = system(command);
2096 // Silently try notifying a running tincd of changes.
2097 if(connect_tincd(false))
2098 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2103 static int export(const char *name, FILE *out) {
2104 char filename[PATH_MAX];
2105 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2106 FILE *in = fopen(filename, "r");
2108 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2112 fprintf(out, "Name = %s\n", name);
2114 while(fgets(buf, sizeof buf, in)) {
2115 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2120 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2129 static int cmd_export(int argc, char *argv[]) {
2131 fprintf(stderr, "Too many arguments!\n");
2135 char *name = get_my_name(true);
2139 int result = export(name, stdout);
2147 static int cmd_export_all(int argc, char *argv[]) {
2149 fprintf(stderr, "Too many arguments!\n");
2153 DIR *dir = opendir(hosts_dir);
2155 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2163 while((ent = readdir(dir))) {
2164 if(!check_id(ent->d_name))
2170 printf("#---------------------------------------------------------------#\n");
2172 result |= export(ent->d_name, stdout);
2181 static int cmd_import(int argc, char *argv[]) {
2183 fprintf(stderr, "Too many arguments!\n");
2192 char filename[PATH_MAX] = "";
2194 bool firstline = true;
2196 while(fgets(buf, sizeof buf, in)) {
2197 if(sscanf(buf, "Name = %s", name) == 1) {
2200 if(!check_id(name)) {
2201 fprintf(stderr, "Invalid Name in input!\n");
2208 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2210 if(!force && !access(filename, F_OK)) {
2211 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2216 out = fopen(filename, "w");
2218 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2224 } else if(firstline) {
2225 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2230 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2234 if(fputs(buf, out) < 0) {
2235 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2245 fprintf(stderr, "Imported %d host configuration files.\n", count);
2248 fprintf(stderr, "No host configuration files imported.\n");
2253 static int cmd_exchange(int argc, char *argv[]) {
2254 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2257 static int cmd_exchange_all(int argc, char *argv[]) {
2258 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2261 static int switch_network(char *name) {
2273 free(unixsocketname);
2274 unixsocketname = NULL;
2280 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2282 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2283 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2284 xasprintf(&prompt, "%s> ", identname);
2289 static int cmd_network(int argc, char *argv[]) {
2291 fprintf(stderr, "Too many arguments!\n");
2296 return switch_network(argv[1]);
2298 DIR *dir = opendir(confdir);
2300 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2305 while((ent = readdir(dir))) {
2306 if(*ent->d_name == '.')
2309 if(!strcmp(ent->d_name, "tinc.conf")) {
2314 char fname[PATH_MAX];
2315 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2316 if(!access(fname, R_OK))
2317 printf("%s\n", ent->d_name);
2325 static int cmd_fsck(int argc, char *argv[]) {
2327 fprintf(stderr, "Too many arguments!\n");
2331 return fsck(orig_argv[0]);
2334 static const struct {
2335 const char *command;
2336 int (*function)(int argc, char *argv[]);
2339 {"start", cmd_start},
2341 {"restart", cmd_restart},
2342 {"reload", cmd_reload},
2345 {"purge", cmd_purge},
2346 {"debug", cmd_debug},
2347 {"retry", cmd_retry},
2348 {"connect", cmd_connect},
2349 {"disconnect", cmd_disconnect},
2354 {"config", cmd_config, true},
2355 {"add", cmd_config},
2356 {"del", cmd_config},
2357 {"get", cmd_config},
2358 {"set", cmd_config},
2360 {"generate-keys", cmd_generate_keys},
2361 #ifndef DISABLE_LEGACY
2362 {"generate-rsa-keys", cmd_generate_rsa_keys},
2364 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2366 {"version", cmd_version},
2369 {"export", cmd_export},
2370 {"export-all", cmd_export_all},
2371 {"import", cmd_import},
2372 {"exchange", cmd_exchange},
2373 {"exchange-all", cmd_exchange_all},
2374 {"invite", cmd_invite},
2376 {"network", cmd_network},
2381 #ifdef HAVE_READLINE
2382 static char *complete_command(const char *text, int state) {
2390 while(commands[i].command) {
2391 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2392 return xstrdup(commands[i].command);
2399 static char *complete_dump(const char *text, int state) {
2400 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2409 if(!strncasecmp(matches[i], text, strlen(text)))
2410 return xstrdup(matches[i]);
2417 static char *complete_config(const char *text, int state) {
2425 while(variables[i].name) {
2426 char *dot = strchr(text, '.');
2428 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2430 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2434 if(!strncasecmp(variables[i].name, text, strlen(text)))
2435 return xstrdup(variables[i].name);
2443 static char *complete_info(const char *text, int state) {
2447 if(!connect_tincd(false))
2449 // Check the list of nodes
2450 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2451 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2454 while(recvline(fd, line, sizeof line)) {
2456 int n = sscanf(line, "%d %d %s", &code, &req, item);
2466 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2470 if(!strncmp(item, text, strlen(text)))
2471 return xstrdup(strip_weight(item));
2477 static char *complete_nothing(const char *text, int state) {
2481 static char **completion (const char *text, int start, int end) {
2482 char **matches = NULL;
2485 matches = rl_completion_matches(text, complete_command);
2486 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2487 matches = rl_completion_matches(text, complete_dump);
2488 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2489 matches = rl_completion_matches(text, complete_config);
2490 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2491 matches = rl_completion_matches(text, complete_config);
2492 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2493 matches = rl_completion_matches(text, complete_config);
2494 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2495 matches = rl_completion_matches(text, complete_config);
2496 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2497 matches = rl_completion_matches(text, complete_info);
2503 static int cmd_shell(int argc, char *argv[]) {
2504 xasprintf(&prompt, "%s> ", identname);
2508 int maxargs = argc + 16;
2509 char **nargv = xmalloc(maxargs * sizeof *nargv);
2511 for(int i = 0; i < argc; i++)
2514 #ifdef HAVE_READLINE
2515 rl_readline_name = "tinc";
2516 rl_completion_entry_function = complete_nothing;
2517 rl_attempted_completion_function = completion;
2518 rl_filename_completion_desired = 0;
2523 #ifdef HAVE_READLINE
2527 rl_basic_word_break_characters = "\t\n ";
2528 line = readline(prompt);
2530 copy = xstrdup(line);
2532 line = fgets(buf, sizeof buf, stdin);
2536 fputs(prompt, stdout);
2538 line = fgets(buf, sizeof buf, stdin);
2544 /* Ignore comments */
2552 char *p = line + strspn(line, " \t\n");
2553 char *next = strtok(p, " \t\n");
2556 if(nargc >= maxargs) {
2557 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2560 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2565 next = strtok(NULL, " \t\n");
2571 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2576 for(int i = 0; commands[i].command; i++) {
2577 if(!strcasecmp(nargv[argc], commands[i].command)) {
2578 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2584 #ifdef HAVE_READLINE
2590 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2603 int main(int argc, char *argv[]) {
2604 program_name = argv[0];
2607 tty = isatty(0) && isatty(1);
2609 if(!parse_options(argc, argv))
2613 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2614 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2627 static struct WSAData wsa_state;
2629 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2630 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2639 return cmd_shell(argc, argv);
2641 for(int i = 0; commands[i].command; i++) {
2642 if(!strcasecmp(argv[optind], commands[i].command))
2643 return commands[i].function(argc - optind, argv + optind);
2646 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);