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);
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 fname[PATH_MAX];
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 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.priv", confbase);
390 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
395 if(!ecdsa_write_pem_private_key(key, f)) {
396 fprintf(stderr, "Error writing private key!\n");
403 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
405 snprintf(fname, sizeof fname, "%s" SLASH "ed25519_key.pub", confbase);
407 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
412 char *pubkey = ecdsa_get_base64_public_key(key);
413 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
427 #ifndef DISABLE_LEGACY
429 Generate a public/private RSA keypair, and ask for a file to store
432 static bool rsa_keygen(int bits, bool ask) {
435 char fname[PATH_MAX];
437 // Make sure the key size is a multiple of 8 bits.
440 // Force them to be between 1024 and 8192 bits long.
446 fprintf(stderr, "Generating %d bits keys:\n", bits);
448 if(!(key = rsa_generate(bits, 0x10001))) {
449 fprintf(stderr, "Error during key generation!\n");
452 fprintf(stderr, "Done.\n");
454 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.priv", confbase);
455 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
460 if(!rsa_write_pem_private_key(key, f)) {
461 fprintf(stderr, "Error writing private key!\n");
468 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
470 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.pub", confbase);
472 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
477 if(!rsa_write_pem_public_key(key, f)) {
478 fprintf(stderr, "Error writing public key!\n");
498 bool recvline(int fd, char *line, size_t len) {
499 char *newline = NULL;
504 while(!(newline = memchr(buffer, '\n', blen))) {
505 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
506 if(result == -1 && sockerrno == EINTR)
513 if(newline - buffer >= len)
516 len = newline - buffer;
518 memcpy(line, buffer, len);
520 memmove(buffer, newline + 1, blen - len - 1);
526 bool recvdata(int fd, char *data, size_t len) {
531 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
532 if(result == -1 && sockerrno == EINTR)
539 memcpy(data, buffer, len);
540 memmove(buffer, buffer + len, blen - len);
546 bool sendline(int fd, char *format, ...) {
547 static char buffer[4096];
552 va_start(ap, format);
553 blen = vsnprintf(buffer, sizeof buffer, format, ap);
556 if(blen < 1 || blen >= sizeof buffer)
563 int result = send(fd, p, blen, MSG_NOSIGNAL);
564 if(result == -1 && sockerrno == EINTR)
575 static void pcap(int fd, FILE *out, int snaplen) {
576 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
584 uint32_t tz_accuracy;
591 snaplen ?: sizeof data,
604 fwrite(&header, sizeof header, 1, out);
608 while(recvline(fd, line, sizeof line)) {
610 int n = sscanf(line, "%d %d %d", &code, &req, &len);
611 gettimeofday(&tv, NULL);
612 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
614 if(!recvdata(fd, data, len))
616 packet.tv_sec = tv.tv_sec;
617 packet.tv_usec = tv.tv_usec;
619 packet.origlen = len;
620 fwrite(&packet, sizeof packet, 1, out);
621 fwrite(data, len, 1, out);
626 static void logcontrol(int fd, FILE *out, int level) {
627 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
631 while(recvline(fd, line, sizeof line)) {
633 int n = sscanf(line, "%d %d %d", &code, &req, &len);
634 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
636 if(!recvdata(fd, data, len))
638 fwrite(data, len, 1, out);
645 static bool remove_service(void) {
646 SC_HANDLE manager = NULL;
647 SC_HANDLE service = NULL;
648 SERVICE_STATUS status = {0};
650 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
652 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
656 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
659 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
663 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
664 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
666 fprintf(stderr, "%s service stopped\n", identname);
668 if(!DeleteService(service)) {
669 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
673 fprintf(stderr, "%s service removed\n", identname);
679 bool connect_tincd(bool verbose) {
684 struct timeval tv = {0, 0};
685 if(select(fd + 1, &r, NULL, NULL, &tv)) {
686 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
694 FILE *f = fopen(pidfilename, "r");
697 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
704 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
706 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
714 struct sockaddr_un sa;
715 sa.sun_family = AF_UNIX;
716 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
718 fd = socket(AF_UNIX, SOCK_STREAM, 0);
721 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
725 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
727 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
733 struct addrinfo hints = {
734 .ai_family = AF_UNSPEC,
735 .ai_socktype = SOCK_STREAM,
736 .ai_protocol = IPPROTO_TCP,
740 struct addrinfo *res = NULL;
742 if(getaddrinfo(host, port, &hints, &res) || !res) {
744 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
748 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
751 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
756 unsigned long arg = 0;
758 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
760 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
764 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
766 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
776 static const int one = 1;
777 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
783 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
785 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
791 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
793 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
795 fprintf(stderr, "Could not fully establish control socket connection\n");
805 static int cmd_start(int argc, char *argv[]) {
806 if(connect_tincd(false)) {
808 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
810 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
815 char *slash = strrchr(program_name, '/');
818 if ((c = strrchr(program_name, '\\')) > slash)
823 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
828 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
833 Windows has no real concept of an "argv array". A command line is just one string.
834 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
835 it uses quotes to handle spaces in arguments.
836 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
837 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
838 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
840 xasprintf(&arg0, "\"%s\"", arg0);
842 nargv[nargc++] = arg0;
843 for(int i = 1; i < optind; i++)
844 nargv[nargc++] = orig_argv[i];
845 for(int i = 1; i < argc; i++)
846 nargv[nargc++] = argv[i];
849 int status = spawnvp(_P_WAIT, c, nargv);
851 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
856 int pfd[2] = {-1, -1};
857 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
858 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
865 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
873 snprintf(buf, sizeof buf, "%d", pfd[1]);
874 setenv("TINC_UMBILICAL", buf, true);
875 exit(execvp(c, nargv));
882 int status = -1, result;
884 signal(SIGINT, SIG_IGN);
887 // Pass all log messages from the umbilical to stderr.
888 // A nul-byte right before closure means tincd started succesfully.
893 while((len = read(pfd[0], buf, sizeof buf)) > 0) {
894 failure = buf[len - 1];
905 // Make sure the child process is really gone.
906 result = waitpid(pid, &status, 0);
909 signal(SIGINT, SIG_DFL);
912 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
913 fprintf(stderr, "Error starting %s\n", c);
921 static int cmd_stop(int argc, char *argv[]) {
923 fprintf(stderr, "Too many arguments!\n");
928 if(!connect_tincd(true)) {
930 if(kill(pid, SIGTERM)) {
931 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
935 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
936 waitpid(pid, NULL, 0);
943 sendline(fd, "%d %d", CONTROL, REQ_STOP);
945 while(recvline(fd, line, sizeof line)) {
946 // Wait for tincd to close the connection...
949 if(!remove_service())
959 static int cmd_restart(int argc, char *argv[]) {
961 return cmd_start(argc, argv);
964 static int cmd_reload(int argc, char *argv[]) {
966 fprintf(stderr, "Too many arguments!\n");
970 if(!connect_tincd(true))
973 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
974 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
975 fprintf(stderr, "Could not reload configuration.\n");
983 static int dump_invitations(void) {
984 char dname[PATH_MAX];
985 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
986 DIR *dir = opendir(dname);
988 if(errno == ENOENT) {
989 fprintf(stderr, "No outstanding invitations.\n");
993 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1000 while((ent = readdir(dir))) {
1001 char buf[MAX_STRING_SIZE];
1002 if(b64decode(ent->d_name, buf, 24) != 18)
1005 char fname[PATH_MAX];
1006 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
1007 FILE *f = fopen(fname, "r");
1009 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1015 if(!fgets(buf, sizeof buf, f)) {
1016 fprintf(stderr, "Invalid invitation file %s", fname);
1022 char *eol = buf + strlen(buf);
1023 while(strchr("\t \r\n", *--eol))
1025 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1026 fprintf(stderr, "Invalid invitation file %s", fname);
1031 printf("%s %s\n", ent->d_name, buf + 7);
1037 fprintf(stderr, "No outstanding invitations.\n");
1042 static int cmd_dump(int argc, char *argv[]) {
1043 bool only_reachable = false;
1045 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1046 if(strcasecmp(argv[2], "nodes")) {
1047 fprintf(stderr, "`reachable' only supported for nodes.\n");
1051 only_reachable = true;
1057 fprintf(stderr, "Invalid number of arguments.\n");
1062 if(!strcasecmp(argv[1], "invitations"))
1063 return dump_invitations();
1065 if(!connect_tincd(true))
1070 if(!strcasecmp(argv[1], "nodes"))
1071 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1072 else if(!strcasecmp(argv[1], "edges"))
1073 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1074 else if(!strcasecmp(argv[1], "subnets"))
1075 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1076 else if(!strcasecmp(argv[1], "connections"))
1077 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1078 else if(!strcasecmp(argv[1], "graph")) {
1079 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1080 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1082 } else if(!strcasecmp(argv[1], "digraph")) {
1083 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1084 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1087 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1093 printf("graph {\n");
1094 else if(do_graph == 2)
1095 printf("digraph {\n");
1097 while(recvline(fd, line, sizeof line)) {
1098 char node1[4096], node2[4096];
1099 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1101 if(do_graph && req == REQ_DUMP_NODES)
1119 char local_host[4096];
1120 char local_port[4096];
1123 int cipher, digest, maclength, compression, distance, socket, weight;
1124 short int pmtu, minmtu, maxmtu;
1125 unsigned int options, status_int;
1126 node_status_t status;
1127 long int last_state_change;
1130 case REQ_DUMP_NODES: {
1131 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);
1133 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1137 memcpy(&status, &status_int, sizeof status);
1140 const char *color = "black";
1141 if(!strcmp(host, "MYSELF"))
1143 else if(!status.reachable)
1145 else if(strcmp(via, node))
1147 else if(!status.validkey)
1151 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1153 if(only_reachable && !status.reachable)
1155 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",
1156 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1160 case REQ_DUMP_EDGES: {
1161 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);
1163 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1168 float w = 1 + 65536.0 / weight;
1169 if(do_graph == 1 && strcmp(node1, node2) > 0)
1170 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1171 else if(do_graph == 2)
1172 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1174 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);
1178 case REQ_DUMP_SUBNETS: {
1179 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1181 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1184 printf("%s owner %s\n", strip_weight(subnet), node);
1187 case REQ_DUMP_CONNECTIONS: {
1188 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1190 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1193 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1197 fprintf(stderr, "Unable to parse dump from tincd.\n");
1202 fprintf(stderr, "Error receiving dump.\n");
1206 static int cmd_purge(int argc, char *argv[]) {
1208 fprintf(stderr, "Too many arguments!\n");
1212 if(!connect_tincd(true))
1215 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1216 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1217 fprintf(stderr, "Could not purge old information.\n");
1224 static int cmd_debug(int argc, char *argv[]) {
1226 fprintf(stderr, "Invalid number of arguments.\n");
1230 if(!connect_tincd(true))
1233 int debuglevel = atoi(argv[1]);
1236 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1237 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1238 fprintf(stderr, "Could not set debug level.\n");
1242 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1246 static int cmd_retry(int argc, char *argv[]) {
1248 fprintf(stderr, "Too many arguments!\n");
1252 if(!connect_tincd(true))
1255 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1256 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1257 fprintf(stderr, "Could not retry outgoing connections.\n");
1264 static int cmd_connect(int argc, char *argv[]) {
1266 fprintf(stderr, "Invalid number of arguments.\n");
1270 if(!check_id(argv[1])) {
1271 fprintf(stderr, "Invalid name for node.\n");
1275 if(!connect_tincd(true))
1278 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1279 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1280 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1287 static int cmd_disconnect(int argc, char *argv[]) {
1289 fprintf(stderr, "Invalid number of arguments.\n");
1293 if(!check_id(argv[1])) {
1294 fprintf(stderr, "Invalid name for node.\n");
1298 if(!connect_tincd(true))
1301 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1302 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1303 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1310 static int cmd_top(int argc, char *argv[]) {
1312 fprintf(stderr, "Too many arguments!\n");
1317 if(!connect_tincd(true))
1323 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1328 static int cmd_pcap(int argc, char *argv[]) {
1330 fprintf(stderr, "Too many arguments!\n");
1334 if(!connect_tincd(true))
1337 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1342 static void sigint_handler(int sig) {
1343 fprintf(stderr, "\n");
1344 shutdown(fd, SHUT_RDWR);
1348 static int cmd_log(int argc, char *argv[]) {
1350 fprintf(stderr, "Too many arguments!\n");
1354 if(!connect_tincd(true))
1358 signal(SIGINT, sigint_handler);
1361 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1364 signal(SIGINT, SIG_DFL);
1372 static int cmd_pid(int argc, char *argv[]) {
1374 fprintf(stderr, "Too many arguments!\n");
1378 if(!connect_tincd(true) && !pid)
1381 printf("%d\n", pid);
1385 int rstrip(char *value) {
1386 int len = strlen(value);
1387 while(len && strchr("\t\r\n ", value[len - 1]))
1392 char *get_my_name(bool verbose) {
1393 FILE *f = fopen(tinc_conf, "r");
1396 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1402 while(fgets(buf, sizeof buf, f)) {
1403 int len = strcspn(buf, "\t =");
1405 value += strspn(value, "\t ");
1408 value += strspn(value, "\t ");
1413 if(strcasecmp(buf, "Name"))
1417 return replace_name(value);
1423 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1427 const var_t variables[] = {
1428 /* Server configuration */
1429 {"AddressFamily", VAR_SERVER},
1430 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1431 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1432 {"BindToInterface", VAR_SERVER},
1433 {"Broadcast", VAR_SERVER | VAR_SAFE},
1434 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1435 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1436 {"DecrementTTL", VAR_SERVER},
1437 {"Device", VAR_SERVER},
1438 {"DeviceStandby", VAR_SERVER},
1439 {"DeviceType", VAR_SERVER},
1440 {"DirectOnly", VAR_SERVER},
1441 {"Ed25519PrivateKeyFile", VAR_SERVER},
1442 {"ExperimentalProtocol", VAR_SERVER},
1443 {"Forwarding", VAR_SERVER},
1444 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1445 {"Hostnames", VAR_SERVER},
1446 {"IffOneQueue", VAR_SERVER},
1447 {"Interface", VAR_SERVER},
1448 {"KeyExpire", VAR_SERVER},
1449 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1450 {"LocalDiscovery", VAR_SERVER},
1451 {"MACExpire", VAR_SERVER},
1452 {"MaxConnectionBurst", VAR_SERVER},
1453 {"MaxOutputBufferSize", VAR_SERVER},
1454 {"MaxTimeout", VAR_SERVER},
1455 {"Mode", VAR_SERVER | VAR_SAFE},
1456 {"Name", VAR_SERVER},
1457 {"PingInterval", VAR_SERVER},
1458 {"PingTimeout", VAR_SERVER},
1459 {"PriorityInheritance", VAR_SERVER},
1460 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1461 {"PrivateKeyFile", VAR_SERVER},
1462 {"ProcessPriority", VAR_SERVER},
1463 {"Proxy", VAR_SERVER},
1464 {"ReplayWindow", VAR_SERVER},
1465 {"ScriptsExtension", VAR_SERVER},
1466 {"ScriptsInterpreter", VAR_SERVER},
1467 {"StrictSubnets", VAR_SERVER},
1468 {"TunnelServer", VAR_SERVER},
1469 {"UDPDiscovery", VAR_SERVER},
1470 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1471 {"UDPDiscoveryInterval", VAR_SERVER},
1472 {"UDPDiscoveryTimeout", VAR_SERVER},
1473 {"MTUInfoInterval", VAR_SERVER},
1474 {"UDPInfoInterval", VAR_SERVER},
1475 {"UDPRcvBuf", VAR_SERVER},
1476 {"UDPSndBuf", VAR_SERVER},
1477 {"VDEGroup", VAR_SERVER},
1478 {"VDEPort", VAR_SERVER},
1479 /* Host configuration */
1480 {"Address", VAR_HOST | VAR_MULTIPLE},
1481 {"Cipher", VAR_SERVER | VAR_HOST},
1482 {"ClampMSS", VAR_SERVER | VAR_HOST},
1483 {"Compression", VAR_SERVER | VAR_HOST},
1484 {"Digest", VAR_SERVER | VAR_HOST},
1485 {"Ed25519PublicKey", VAR_HOST},
1486 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1487 {"IndirectData", VAR_SERVER | VAR_HOST},
1488 {"MACLength", VAR_SERVER | VAR_HOST},
1489 {"PMTU", VAR_SERVER | VAR_HOST},
1490 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1492 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1493 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1494 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1495 {"TCPOnly", VAR_SERVER | VAR_HOST},
1496 {"Weight", VAR_HOST | VAR_SAFE},
1500 static int cmd_config(int argc, char *argv[]) {
1502 fprintf(stderr, "Invalid number of arguments.\n");
1506 if(strcasecmp(argv[0], "config"))
1510 if(!strcasecmp(argv[1], "get")) {
1512 } else if(!strcasecmp(argv[1], "add")) {
1513 argv++, argc--, action = 1;
1514 } else if(!strcasecmp(argv[1], "del")) {
1515 argv++, argc--, action = -1;
1516 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1517 argv++, argc--, action = 0;
1521 fprintf(stderr, "Invalid number of arguments.\n");
1525 // Concatenate the rest of the command line
1526 strncpy(line, argv[1], sizeof line - 1);
1527 for(int i = 2; i < argc; i++) {
1528 strncat(line, " ", sizeof line - 1 - strlen(line));
1529 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1532 // Liberal parsing into node name, variable name and value.
1538 len = strcspn(line, "\t =");
1540 value += strspn(value, "\t ");
1543 value += strspn(value, "\t ");
1546 variable = strchr(line, '.');
1555 fprintf(stderr, "No variable given.\n");
1559 if(action >= 0 && !*value) {
1560 fprintf(stderr, "No value for variable given.\n");
1564 if(action < -1 && *value)
1567 /* Some simple checks. */
1569 bool warnonremove = false;
1571 for(int i = 0; variables[i].name; i++) {
1572 if(strcasecmp(variables[i].name, variable))
1576 variable = (char *)variables[i].name;
1578 /* Discourage use of obsolete variables. */
1580 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1582 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1584 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1589 /* Don't put server variables in host config files */
1591 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1593 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1595 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1600 /* Should this go into our own host config file? */
1602 if(!node && !(variables[i].type & VAR_SERVER)) {
1603 node = get_my_name(true);
1608 /* Change "add" into "set" for variables that do not allow multiple occurences.
1609 Turn on warnings when it seems variables might be removed unintentionally. */
1611 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1612 warnonremove = true;
1614 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1615 warnonremove = true;
1621 if(node && !check_id(node)) {
1622 fprintf(stderr, "Invalid name for node.\n");
1627 if(force || action < 0) {
1628 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1630 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1635 // Open the right configuration file.
1636 char filename[PATH_MAX];
1638 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1640 snprintf(filename, sizeof filename, "%s", tinc_conf);
1642 FILE *f = fopen(filename, "r");
1644 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1648 char tmpfile[PATH_MAX];
1652 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1653 tf = fopen(tmpfile, "w");
1655 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1661 // Copy the file, making modifications on the fly, unless we are just getting a value.
1665 bool removed = false;
1668 while(fgets(buf1, sizeof buf1, f)) {
1669 buf1[sizeof buf1 - 1] = 0;
1670 strncpy(buf2, buf1, sizeof buf2);
1672 // Parse line in a simple way
1676 len = strcspn(buf2, "\t =");
1677 bvalue = buf2 + len;
1678 bvalue += strspn(bvalue, "\t ");
1679 if(*bvalue == '=') {
1681 bvalue += strspn(bvalue, "\t ");
1687 if(!strcasecmp(buf2, variable)) {
1691 printf("%s\n", bvalue);
1693 } else if(action == -1) {
1694 if(!*value || !strcasecmp(bvalue, value)) {
1699 } else if(action == 0) {
1700 // Warn if "set" was used for variables that can occur multiple times
1701 if(warnonremove && strcasecmp(bvalue, value))
1702 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1704 // Already set? Delete the rest...
1708 // Otherwise, replace.
1709 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1710 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1716 } else if(action > 0) {
1717 // Check if we've already seen this variable with the same value
1718 if(!strcasecmp(bvalue, value))
1724 // Copy original line...
1725 if(fputs(buf1, tf) < 0) {
1726 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1730 // Add newline if it is missing...
1731 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1732 if(fputc('\n', tf) < 0) {
1733 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1740 // Make sure we read everything...
1741 if(ferror(f) || !feof(f)) {
1742 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1747 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1751 // Add new variable if necessary.
1752 if((action > 0 && !found)|| (action == 0 && !set)) {
1753 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1754 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1763 fprintf(stderr, "No matching configuration variables found.\n");
1768 // Make sure we wrote everything...
1770 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1774 // Could we find what we had to remove?
1775 if(action < 0 && !removed) {
1777 fprintf(stderr, "No configuration variables deleted.\n");
1781 // Replace the configuration file with the new one
1783 if(remove(filename)) {
1784 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1788 if(rename(tmpfile, filename)) {
1789 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1793 // Silently try notifying a running tincd of changes.
1794 if(connect_tincd(false))
1795 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1800 static bool try_bind(int port) {
1801 struct addrinfo *ai = NULL;
1802 struct addrinfo hint = {
1803 .ai_flags = AI_PASSIVE,
1804 .ai_family = AF_UNSPEC,
1805 .ai_socktype = SOCK_STREAM,
1806 .ai_protocol = IPPROTO_TCP,
1810 snprintf(portstr, sizeof portstr, "%d", port);
1812 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1816 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1819 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1829 int check_port(char *name) {
1833 fprintf(stderr, "Warning: could not bind to port 655. ");
1835 for(int i = 0; i < 100; i++) {
1836 int port = 0x1000 + (rand() & 0x7fff);
1837 if(try_bind(port)) {
1838 char filename[PATH_MAX];
1839 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1840 FILE *f = fopen(filename, "a");
1842 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1843 fprintf(stderr, "Please change tinc's Port manually.\n");
1847 fprintf(f, "Port = %d\n", port);
1849 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1854 fprintf(stderr, "Please change tinc's Port manually.\n");
1858 static int cmd_init(int argc, char *argv[]) {
1859 if(!access(tinc_conf, F_OK)) {
1860 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1865 fprintf(stderr, "Too many arguments!\n");
1867 } else if(argc < 2) {
1870 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1871 if(!fgets(buf, sizeof buf, stdin)) {
1872 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1875 int len = rstrip(buf);
1877 fprintf(stderr, "No name given!\n");
1882 fprintf(stderr, "No Name given!\n");
1886 name = strdup(argv[1]);
1888 fprintf(stderr, "No Name given!\n");
1893 if(!check_id(name)) {
1894 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1898 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1899 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1903 if(mkdir(confbase, 0777) && errno != EEXIST) {
1904 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1908 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1909 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1913 FILE *f = fopen(tinc_conf, "w");
1915 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1919 fprintf(f, "Name = %s\n", name);
1922 #ifndef DISABLE_LEGACY
1923 if(!rsa_keygen(2048, false))
1927 if(!ed25519_keygen(false))
1933 char filename[PATH_MAX];
1934 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1935 if(access(filename, F_OK)) {
1936 FILE *f = fopenmask(filename, "w", 0777);
1938 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1941 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");
1950 static int cmd_generate_keys(int argc, char *argv[]) {
1951 #ifdef DISABLE_LEGACY
1956 fprintf(stderr, "Too many arguments!\n");
1961 name = get_my_name(false);
1963 #ifndef DISABLE_LEGACY
1964 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1968 if(!ed25519_keygen(true))
1974 #ifndef DISABLE_LEGACY
1975 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1977 fprintf(stderr, "Too many arguments!\n");
1982 name = get_my_name(false);
1984 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1988 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1990 fprintf(stderr, "Too many arguments!\n");
1995 name = get_my_name(false);
1997 return !ed25519_keygen(true);
2000 static int cmd_help(int argc, char *argv[]) {
2005 static int cmd_version(int argc, char *argv[]) {
2007 fprintf(stderr, "Too many arguments!\n");
2015 static int cmd_info(int argc, char *argv[]) {
2017 fprintf(stderr, "Invalid number of arguments.\n");
2021 if(!connect_tincd(true))
2024 return info(fd, argv[1]);
2027 static const char *conffiles[] = {
2038 static int cmd_edit(int argc, char *argv[]) {
2040 fprintf(stderr, "Invalid number of arguments.\n");
2044 char filename[PATH_MAX] = "";
2046 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2047 for(int i = 0; conffiles[i]; i++) {
2048 if(!strcmp(argv[1], conffiles[i])) {
2049 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2058 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2059 char *dash = strchr(argv[1], '-');
2062 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2063 fprintf(stderr, "Invalid configuration filename.\n");
2071 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2073 xasprintf(&command, "edit \"%s\"", filename);
2075 int result = system(command);
2080 // Silently try notifying a running tincd of changes.
2081 if(connect_tincd(false))
2082 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2087 static int export(const char *name, FILE *out) {
2088 char filename[PATH_MAX];
2089 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2090 FILE *in = fopen(filename, "r");
2092 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2096 fprintf(out, "Name = %s\n", name);
2098 while(fgets(buf, sizeof buf, in)) {
2099 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2104 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2113 static int cmd_export(int argc, char *argv[]) {
2115 fprintf(stderr, "Too many arguments!\n");
2119 char *name = get_my_name(true);
2123 int result = export(name, stdout);
2131 static int cmd_export_all(int argc, char *argv[]) {
2133 fprintf(stderr, "Too many arguments!\n");
2137 DIR *dir = opendir(hosts_dir);
2139 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2147 while((ent = readdir(dir))) {
2148 if(!check_id(ent->d_name))
2154 printf("#---------------------------------------------------------------#\n");
2156 result |= export(ent->d_name, stdout);
2165 static int cmd_import(int argc, char *argv[]) {
2167 fprintf(stderr, "Too many arguments!\n");
2176 char filename[PATH_MAX] = "";
2178 bool firstline = true;
2180 while(fgets(buf, sizeof buf, in)) {
2181 if(sscanf(buf, "Name = %s", name) == 1) {
2184 if(!check_id(name)) {
2185 fprintf(stderr, "Invalid Name in input!\n");
2192 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2194 if(!force && !access(filename, F_OK)) {
2195 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2200 out = fopen(filename, "w");
2202 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2208 } else if(firstline) {
2209 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2214 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2218 if(fputs(buf, out) < 0) {
2219 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2229 fprintf(stderr, "Imported %d host configuration files.\n", count);
2232 fprintf(stderr, "No host configuration files imported.\n");
2237 static int cmd_exchange(int argc, char *argv[]) {
2238 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2241 static int cmd_exchange_all(int argc, char *argv[]) {
2242 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2245 static int switch_network(char *name) {
2257 free(unixsocketname);
2258 unixsocketname = NULL;
2264 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2266 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2267 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2268 xasprintf(&prompt, "%s> ", identname);
2273 static int cmd_network(int argc, char *argv[]) {
2275 fprintf(stderr, "Too many arguments!\n");
2280 return switch_network(argv[1]);
2282 DIR *dir = opendir(confdir);
2284 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2289 while((ent = readdir(dir))) {
2290 if(*ent->d_name == '.')
2293 if(!strcmp(ent->d_name, "tinc.conf")) {
2298 char fname[PATH_MAX];
2299 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2300 if(!access(fname, R_OK))
2301 printf("%s\n", ent->d_name);
2309 static int cmd_fsck(int argc, char *argv[]) {
2311 fprintf(stderr, "Too many arguments!\n");
2315 return fsck(orig_argv[0]);
2318 static const struct {
2319 const char *command;
2320 int (*function)(int argc, char *argv[]);
2323 {"start", cmd_start},
2325 {"restart", cmd_restart},
2326 {"reload", cmd_reload},
2329 {"purge", cmd_purge},
2330 {"debug", cmd_debug},
2331 {"retry", cmd_retry},
2332 {"connect", cmd_connect},
2333 {"disconnect", cmd_disconnect},
2338 {"config", cmd_config, true},
2339 {"add", cmd_config},
2340 {"del", cmd_config},
2341 {"get", cmd_config},
2342 {"set", cmd_config},
2344 {"generate-keys", cmd_generate_keys},
2345 #ifndef DISABLE_LEGACY
2346 {"generate-rsa-keys", cmd_generate_rsa_keys},
2348 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2350 {"version", cmd_version},
2353 {"export", cmd_export},
2354 {"export-all", cmd_export_all},
2355 {"import", cmd_import},
2356 {"exchange", cmd_exchange},
2357 {"exchange-all", cmd_exchange_all},
2358 {"invite", cmd_invite},
2360 {"network", cmd_network},
2365 #ifdef HAVE_READLINE
2366 static char *complete_command(const char *text, int state) {
2374 while(commands[i].command) {
2375 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2376 return xstrdup(commands[i].command);
2383 static char *complete_dump(const char *text, int state) {
2384 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2393 if(!strncasecmp(matches[i], text, strlen(text)))
2394 return xstrdup(matches[i]);
2401 static char *complete_config(const char *text, int state) {
2409 while(variables[i].name) {
2410 char *dot = strchr(text, '.');
2412 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2414 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2418 if(!strncasecmp(variables[i].name, text, strlen(text)))
2419 return xstrdup(variables[i].name);
2427 static char *complete_info(const char *text, int state) {
2431 if(!connect_tincd(false))
2433 // Check the list of nodes
2434 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2435 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2438 while(recvline(fd, line, sizeof line)) {
2440 int n = sscanf(line, "%d %d %s", &code, &req, item);
2450 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2454 if(!strncmp(item, text, strlen(text)))
2455 return xstrdup(strip_weight(item));
2461 static char *complete_nothing(const char *text, int state) {
2465 static char **completion (const char *text, int start, int end) {
2466 char **matches = NULL;
2469 matches = rl_completion_matches(text, complete_command);
2470 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2471 matches = rl_completion_matches(text, complete_dump);
2472 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2473 matches = rl_completion_matches(text, complete_config);
2474 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2475 matches = rl_completion_matches(text, complete_config);
2476 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2477 matches = rl_completion_matches(text, complete_config);
2478 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2479 matches = rl_completion_matches(text, complete_config);
2480 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2481 matches = rl_completion_matches(text, complete_info);
2487 static int cmd_shell(int argc, char *argv[]) {
2488 xasprintf(&prompt, "%s> ", identname);
2492 int maxargs = argc + 16;
2493 char **nargv = xmalloc(maxargs * sizeof *nargv);
2495 for(int i = 0; i < argc; i++)
2498 #ifdef HAVE_READLINE
2499 rl_readline_name = "tinc";
2500 rl_completion_entry_function = complete_nothing;
2501 rl_attempted_completion_function = completion;
2502 rl_filename_completion_desired = 0;
2507 #ifdef HAVE_READLINE
2511 rl_basic_word_break_characters = "\t\n ";
2512 line = readline(prompt);
2514 copy = xstrdup(line);
2516 line = fgets(buf, sizeof buf, stdin);
2520 fputs(prompt, stdout);
2522 line = fgets(buf, sizeof buf, stdin);
2528 /* Ignore comments */
2536 char *p = line + strspn(line, " \t\n");
2537 char *next = strtok(p, " \t\n");
2540 if(nargc >= maxargs) {
2541 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2544 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2549 next = strtok(NULL, " \t\n");
2555 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2560 for(int i = 0; commands[i].command; i++) {
2561 if(!strcasecmp(nargv[argc], commands[i].command)) {
2562 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2568 #ifdef HAVE_READLINE
2574 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2587 int main(int argc, char *argv[]) {
2588 program_name = argv[0];
2591 tty = isatty(0) && isatty(1);
2593 if(!parse_options(argc, argv))
2597 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2598 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2611 static struct WSAData wsa_state;
2613 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2614 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2623 return cmd_shell(argc, argv);
2625 for(int i = 0; commands[i].command; i++) {
2626 if(!strcasecmp(argv[optind], commands[i].command))
2627 return commands[i].function(argc - optind, argv + optind);
2630 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);