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);
433 #ifndef DISABLE_LEGACY
435 Generate a public/private RSA keypair, and ask for a file to store
438 static bool rsa_keygen(int bits, bool ask) {
441 char fname[PATH_MAX];
443 // Make sure the key size is a multiple of 8 bits.
446 // Force them to be between 1024 and 8192 bits long.
452 fprintf(stderr, "Generating %d bits keys:\n", bits);
454 if(!(key = rsa_generate(bits, 0x10001))) {
455 fprintf(stderr, "Error during key generation!\n");
458 fprintf(stderr, "Done.\n");
460 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.priv", confbase);
461 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
466 if(!rsa_write_pem_private_key(key, f)) {
467 fprintf(stderr, "Error writing private key!\n");
474 snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
476 snprintf(fname, sizeof fname, "%s" SLASH "rsa_key.pub", confbase);
478 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
483 if(!rsa_write_pem_public_key(key, f)) {
484 fprintf(stderr, "Error writing public key!\n");
504 bool recvline(int fd, char *line, size_t len) {
505 char *newline = NULL;
510 while(!(newline = memchr(buffer, '\n', blen))) {
511 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
512 if(result == -1 && sockerrno == EINTR)
519 if(newline - buffer >= len)
522 len = newline - buffer;
524 memcpy(line, buffer, len);
526 memmove(buffer, newline + 1, blen - len - 1);
532 bool recvdata(int fd, char *data, size_t len) {
537 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
538 if(result == -1 && sockerrno == EINTR)
545 memcpy(data, buffer, len);
546 memmove(buffer, buffer + len, blen - len);
552 bool sendline(int fd, char *format, ...) {
553 static char buffer[4096];
558 va_start(ap, format);
559 blen = vsnprintf(buffer, sizeof buffer, format, ap);
562 if(blen < 1 || blen >= sizeof buffer)
569 int result = send(fd, p, blen, MSG_NOSIGNAL);
570 if(result == -1 && sockerrno == EINTR)
581 static void pcap(int fd, FILE *out, int snaplen) {
582 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
590 uint32_t tz_accuracy;
597 snaplen ?: sizeof data,
610 fwrite(&header, sizeof header, 1, out);
614 while(recvline(fd, line, sizeof line)) {
616 int n = sscanf(line, "%d %d %d", &code, &req, &len);
617 gettimeofday(&tv, NULL);
618 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
620 if(!recvdata(fd, data, len))
622 packet.tv_sec = tv.tv_sec;
623 packet.tv_usec = tv.tv_usec;
625 packet.origlen = len;
626 fwrite(&packet, sizeof packet, 1, out);
627 fwrite(data, len, 1, out);
632 static void logcontrol(int fd, FILE *out, int level) {
633 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
637 while(recvline(fd, line, sizeof line)) {
639 int n = sscanf(line, "%d %d %d", &code, &req, &len);
640 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
642 if(!recvdata(fd, data, len))
644 fwrite(data, len, 1, out);
651 static bool remove_service(void) {
652 SC_HANDLE manager = NULL;
653 SC_HANDLE service = NULL;
654 SERVICE_STATUS status = {0};
656 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
658 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
662 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
665 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
669 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
670 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
672 fprintf(stderr, "%s service stopped\n", identname);
674 if(!DeleteService(service)) {
675 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
679 fprintf(stderr, "%s service removed\n", identname);
685 bool connect_tincd(bool verbose) {
690 struct timeval tv = {0, 0};
691 if(select(fd + 1, &r, NULL, NULL, &tv)) {
692 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
700 FILE *f = fopen(pidfilename, "r");
703 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
710 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
712 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
720 struct sockaddr_un sa;
721 sa.sun_family = AF_UNIX;
722 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
724 fd = socket(AF_UNIX, SOCK_STREAM, 0);
727 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
731 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
733 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
739 struct addrinfo hints = {
740 .ai_family = AF_UNSPEC,
741 .ai_socktype = SOCK_STREAM,
742 .ai_protocol = IPPROTO_TCP,
746 struct addrinfo *res = NULL;
748 if(getaddrinfo(host, port, &hints, &res) || !res) {
750 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
754 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
757 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
762 unsigned long arg = 0;
764 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
766 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
770 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
772 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
782 static const int one = 1;
783 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
789 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
791 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
797 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
799 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
801 fprintf(stderr, "Could not fully establish control socket connection\n");
811 static int cmd_start(int argc, char *argv[]) {
812 if(connect_tincd(false)) {
814 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
816 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
821 char *slash = strrchr(program_name, '/');
824 if ((c = strrchr(program_name, '\\')) > slash)
829 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
834 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
839 Windows has no real concept of an "argv array". A command line is just one string.
840 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
841 it uses quotes to handle spaces in arguments.
842 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
843 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
844 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
846 xasprintf(&arg0, "\"%s\"", arg0);
848 nargv[nargc++] = arg0;
849 for(int i = 1; i < optind; i++)
850 nargv[nargc++] = orig_argv[i];
851 for(int i = 1; i < argc; i++)
852 nargv[nargc++] = argv[i];
855 int status = spawnvp(_P_WAIT, c, nargv);
857 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
862 int pfd[2] = {-1, -1};
863 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
864 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
871 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
879 snprintf(buf, sizeof buf, "%d", pfd[1]);
880 setenv("TINC_UMBILICAL", buf, true);
881 exit(execvp(c, nargv));
888 int status = -1, result;
890 signal(SIGINT, SIG_IGN);
893 // Pass all log messages from the umbilical to stderr.
894 // A nul-byte right before closure means tincd started succesfully.
899 while((len = read(pfd[0], buf, sizeof buf)) > 0) {
900 failure = buf[len - 1];
911 // Make sure the child process is really gone.
912 result = waitpid(pid, &status, 0);
915 signal(SIGINT, SIG_DFL);
918 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
919 fprintf(stderr, "Error starting %s\n", c);
927 static int cmd_stop(int argc, char *argv[]) {
929 fprintf(stderr, "Too many arguments!\n");
934 if(!connect_tincd(true)) {
936 if(kill(pid, SIGTERM)) {
937 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
941 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
942 waitpid(pid, NULL, 0);
949 sendline(fd, "%d %d", CONTROL, REQ_STOP);
951 while(recvline(fd, line, sizeof line)) {
952 // Wait for tincd to close the connection...
955 if(!remove_service())
965 static int cmd_restart(int argc, char *argv[]) {
967 return cmd_start(argc, argv);
970 static int cmd_reload(int argc, char *argv[]) {
972 fprintf(stderr, "Too many arguments!\n");
976 if(!connect_tincd(true))
979 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
980 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
981 fprintf(stderr, "Could not reload configuration.\n");
989 static int dump_invitations(void) {
990 char dname[PATH_MAX];
991 snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
992 DIR *dir = opendir(dname);
994 if(errno == ENOENT) {
995 fprintf(stderr, "No outstanding invitations.\n");
999 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1006 while((ent = readdir(dir))) {
1007 char buf[MAX_STRING_SIZE];
1008 if(b64decode(ent->d_name, buf, 24) != 18)
1011 char fname[PATH_MAX];
1012 snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
1013 FILE *f = fopen(fname, "r");
1015 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1021 if(!fgets(buf, sizeof buf, f)) {
1022 fprintf(stderr, "Invalid invitation file %s", fname);
1028 char *eol = buf + strlen(buf);
1029 while(strchr("\t \r\n", *--eol))
1031 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1032 fprintf(stderr, "Invalid invitation file %s", fname);
1037 printf("%s %s\n", ent->d_name, buf + 7);
1043 fprintf(stderr, "No outstanding invitations.\n");
1048 static int cmd_dump(int argc, char *argv[]) {
1049 bool only_reachable = false;
1051 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1052 if(strcasecmp(argv[2], "nodes")) {
1053 fprintf(stderr, "`reachable' only supported for nodes.\n");
1057 only_reachable = true;
1063 fprintf(stderr, "Invalid number of arguments.\n");
1068 if(!strcasecmp(argv[1], "invitations"))
1069 return dump_invitations();
1071 if(!connect_tincd(true))
1076 if(!strcasecmp(argv[1], "nodes"))
1077 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1078 else if(!strcasecmp(argv[1], "edges"))
1079 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1080 else if(!strcasecmp(argv[1], "subnets"))
1081 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1082 else if(!strcasecmp(argv[1], "connections"))
1083 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1084 else if(!strcasecmp(argv[1], "graph")) {
1085 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1086 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1088 } else if(!strcasecmp(argv[1], "digraph")) {
1089 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1090 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1093 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1099 printf("graph {\n");
1100 else if(do_graph == 2)
1101 printf("digraph {\n");
1103 while(recvline(fd, line, sizeof line)) {
1104 char node1[4096], node2[4096];
1105 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
1107 if(do_graph && req == REQ_DUMP_NODES)
1125 char local_host[4096];
1126 char local_port[4096];
1129 int cipher, digest, maclength, compression, distance, socket, weight;
1130 short int pmtu, minmtu, maxmtu;
1131 unsigned int options, status_int;
1132 node_status_t status;
1133 long int last_state_change;
1136 case REQ_DUMP_NODES: {
1137 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);
1139 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1143 memcpy(&status, &status_int, sizeof status);
1146 const char *color = "black";
1147 if(!strcmp(host, "MYSELF"))
1149 else if(!status.reachable)
1151 else if(strcmp(via, node))
1153 else if(!status.validkey)
1157 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1159 if(only_reachable && !status.reachable)
1161 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",
1162 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1166 case REQ_DUMP_EDGES: {
1167 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);
1169 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1174 float w = 1 + 65536.0 / weight;
1175 if(do_graph == 1 && strcmp(node1, node2) > 0)
1176 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1177 else if(do_graph == 2)
1178 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1180 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);
1184 case REQ_DUMP_SUBNETS: {
1185 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1187 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1190 printf("%s owner %s\n", strip_weight(subnet), node);
1193 case REQ_DUMP_CONNECTIONS: {
1194 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1196 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1199 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1203 fprintf(stderr, "Unable to parse dump from tincd.\n");
1208 fprintf(stderr, "Error receiving dump.\n");
1212 static int cmd_purge(int argc, char *argv[]) {
1214 fprintf(stderr, "Too many arguments!\n");
1218 if(!connect_tincd(true))
1221 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1222 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1223 fprintf(stderr, "Could not purge old information.\n");
1230 static int cmd_debug(int argc, char *argv[]) {
1232 fprintf(stderr, "Invalid number of arguments.\n");
1236 if(!connect_tincd(true))
1239 int debuglevel = atoi(argv[1]);
1242 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1243 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1244 fprintf(stderr, "Could not set debug level.\n");
1248 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1252 static int cmd_retry(int argc, char *argv[]) {
1254 fprintf(stderr, "Too many arguments!\n");
1258 if(!connect_tincd(true))
1261 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1262 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1263 fprintf(stderr, "Could not retry outgoing connections.\n");
1270 static int cmd_connect(int argc, char *argv[]) {
1272 fprintf(stderr, "Invalid number of arguments.\n");
1276 if(!check_id(argv[1])) {
1277 fprintf(stderr, "Invalid name for node.\n");
1281 if(!connect_tincd(true))
1284 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1285 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1286 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1293 static int cmd_disconnect(int argc, char *argv[]) {
1295 fprintf(stderr, "Invalid number of arguments.\n");
1299 if(!check_id(argv[1])) {
1300 fprintf(stderr, "Invalid name for node.\n");
1304 if(!connect_tincd(true))
1307 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1308 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1309 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1316 static int cmd_top(int argc, char *argv[]) {
1318 fprintf(stderr, "Too many arguments!\n");
1323 if(!connect_tincd(true))
1329 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1334 static int cmd_pcap(int argc, char *argv[]) {
1336 fprintf(stderr, "Too many arguments!\n");
1340 if(!connect_tincd(true))
1343 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1348 static void sigint_handler(int sig) {
1349 fprintf(stderr, "\n");
1350 shutdown(fd, SHUT_RDWR);
1354 static int cmd_log(int argc, char *argv[]) {
1356 fprintf(stderr, "Too many arguments!\n");
1360 if(!connect_tincd(true))
1364 signal(SIGINT, sigint_handler);
1367 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1370 signal(SIGINT, SIG_DFL);
1378 static int cmd_pid(int argc, char *argv[]) {
1380 fprintf(stderr, "Too many arguments!\n");
1384 if(!connect_tincd(true) && !pid)
1387 printf("%d\n", pid);
1391 int rstrip(char *value) {
1392 int len = strlen(value);
1393 while(len && strchr("\t\r\n ", value[len - 1]))
1398 char *get_my_name(bool verbose) {
1399 FILE *f = fopen(tinc_conf, "r");
1402 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1408 while(fgets(buf, sizeof buf, f)) {
1409 int len = strcspn(buf, "\t =");
1411 value += strspn(value, "\t ");
1414 value += strspn(value, "\t ");
1419 if(strcasecmp(buf, "Name"))
1423 return replace_name(value);
1429 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1433 const var_t variables[] = {
1434 /* Server configuration */
1435 {"AddressFamily", VAR_SERVER},
1436 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1437 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1438 {"BindToInterface", VAR_SERVER},
1439 {"Broadcast", VAR_SERVER | VAR_SAFE},
1440 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1441 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1442 {"DecrementTTL", VAR_SERVER},
1443 {"Device", VAR_SERVER},
1444 {"DeviceStandby", VAR_SERVER},
1445 {"DeviceType", VAR_SERVER},
1446 {"DirectOnly", VAR_SERVER},
1447 {"Ed25519PrivateKeyFile", VAR_SERVER},
1448 {"ExperimentalProtocol", VAR_SERVER},
1449 {"Forwarding", VAR_SERVER},
1450 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1451 {"Hostnames", VAR_SERVER},
1452 {"IffOneQueue", VAR_SERVER},
1453 {"Interface", VAR_SERVER},
1454 {"KeyExpire", VAR_SERVER},
1455 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1456 {"LocalDiscovery", VAR_SERVER},
1457 {"MACExpire", VAR_SERVER},
1458 {"MaxConnectionBurst", VAR_SERVER},
1459 {"MaxOutputBufferSize", VAR_SERVER},
1460 {"MaxTimeout", VAR_SERVER},
1461 {"Mode", VAR_SERVER | VAR_SAFE},
1462 {"Name", VAR_SERVER},
1463 {"PingInterval", VAR_SERVER},
1464 {"PingTimeout", VAR_SERVER},
1465 {"PriorityInheritance", VAR_SERVER},
1466 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1467 {"PrivateKeyFile", VAR_SERVER},
1468 {"ProcessPriority", VAR_SERVER},
1469 {"Proxy", VAR_SERVER},
1470 {"ReplayWindow", VAR_SERVER},
1471 {"ScriptsExtension", VAR_SERVER},
1472 {"ScriptsInterpreter", VAR_SERVER},
1473 {"StrictSubnets", VAR_SERVER},
1474 {"TunnelServer", VAR_SERVER},
1475 {"UDPDiscovery", VAR_SERVER},
1476 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1477 {"UDPDiscoveryInterval", VAR_SERVER},
1478 {"UDPDiscoveryTimeout", VAR_SERVER},
1479 {"MTUInfoInterval", VAR_SERVER},
1480 {"UDPInfoInterval", VAR_SERVER},
1481 {"UDPRcvBuf", VAR_SERVER},
1482 {"UDPSndBuf", VAR_SERVER},
1483 {"VDEGroup", VAR_SERVER},
1484 {"VDEPort", VAR_SERVER},
1485 /* Host configuration */
1486 {"Address", VAR_HOST | VAR_MULTIPLE},
1487 {"Cipher", VAR_SERVER | VAR_HOST},
1488 {"ClampMSS", VAR_SERVER | VAR_HOST},
1489 {"Compression", VAR_SERVER | VAR_HOST},
1490 {"Digest", VAR_SERVER | VAR_HOST},
1491 {"Ed25519PublicKey", VAR_HOST},
1492 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1493 {"IndirectData", VAR_SERVER | VAR_HOST},
1494 {"MACLength", VAR_SERVER | VAR_HOST},
1495 {"PMTU", VAR_SERVER | VAR_HOST},
1496 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1498 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1499 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1500 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1501 {"TCPOnly", VAR_SERVER | VAR_HOST},
1502 {"Weight", VAR_HOST | VAR_SAFE},
1506 static int cmd_config(int argc, char *argv[]) {
1508 fprintf(stderr, "Invalid number of arguments.\n");
1512 if(strcasecmp(argv[0], "config"))
1516 if(!strcasecmp(argv[1], "get")) {
1518 } else if(!strcasecmp(argv[1], "add")) {
1519 argv++, argc--, action = 1;
1520 } else if(!strcasecmp(argv[1], "del")) {
1521 argv++, argc--, action = -1;
1522 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1523 argv++, argc--, action = 0;
1527 fprintf(stderr, "Invalid number of arguments.\n");
1531 // Concatenate the rest of the command line
1532 strncpy(line, argv[1], sizeof line - 1);
1533 for(int i = 2; i < argc; i++) {
1534 strncat(line, " ", sizeof line - 1 - strlen(line));
1535 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1538 // Liberal parsing into node name, variable name and value.
1544 len = strcspn(line, "\t =");
1546 value += strspn(value, "\t ");
1549 value += strspn(value, "\t ");
1552 variable = strchr(line, '.');
1561 fprintf(stderr, "No variable given.\n");
1565 if(action >= 0 && !*value) {
1566 fprintf(stderr, "No value for variable given.\n");
1570 if(action < -1 && *value)
1573 /* Some simple checks. */
1575 bool warnonremove = false;
1577 for(int i = 0; variables[i].name; i++) {
1578 if(strcasecmp(variables[i].name, variable))
1582 variable = (char *)variables[i].name;
1584 /* Discourage use of obsolete variables. */
1586 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1588 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1590 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1595 /* Don't put server variables in host config files */
1597 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1599 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1601 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1606 /* Should this go into our own host config file? */
1608 if(!node && !(variables[i].type & VAR_SERVER)) {
1609 node = get_my_name(true);
1614 /* Change "add" into "set" for variables that do not allow multiple occurences.
1615 Turn on warnings when it seems variables might be removed unintentionally. */
1617 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1618 warnonremove = true;
1620 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1621 warnonremove = true;
1627 if(node && !check_id(node)) {
1628 fprintf(stderr, "Invalid name for node.\n");
1633 if(force || action < 0) {
1634 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1636 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1641 // Open the right configuration file.
1642 char filename[PATH_MAX];
1644 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, node);
1646 snprintf(filename, sizeof filename, "%s", tinc_conf);
1648 FILE *f = fopen(filename, "r");
1650 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1654 char tmpfile[PATH_MAX];
1658 snprintf(tmpfile, sizeof tmpfile, "%s.config.tmp", filename);
1659 tf = fopen(tmpfile, "w");
1661 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1667 // Copy the file, making modifications on the fly, unless we are just getting a value.
1671 bool removed = false;
1674 while(fgets(buf1, sizeof buf1, f)) {
1675 buf1[sizeof buf1 - 1] = 0;
1676 strncpy(buf2, buf1, sizeof buf2);
1678 // Parse line in a simple way
1682 len = strcspn(buf2, "\t =");
1683 bvalue = buf2 + len;
1684 bvalue += strspn(bvalue, "\t ");
1685 if(*bvalue == '=') {
1687 bvalue += strspn(bvalue, "\t ");
1693 if(!strcasecmp(buf2, variable)) {
1697 printf("%s\n", bvalue);
1699 } else if(action == -1) {
1700 if(!*value || !strcasecmp(bvalue, value)) {
1705 } else if(action == 0) {
1706 // Warn if "set" was used for variables that can occur multiple times
1707 if(warnonremove && strcasecmp(bvalue, value))
1708 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1710 // Already set? Delete the rest...
1714 // Otherwise, replace.
1715 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1716 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1722 } else if(action > 0) {
1723 // Check if we've already seen this variable with the same value
1724 if(!strcasecmp(bvalue, value))
1730 // Copy original line...
1731 if(fputs(buf1, tf) < 0) {
1732 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1736 // Add newline if it is missing...
1737 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1738 if(fputc('\n', tf) < 0) {
1739 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1746 // Make sure we read everything...
1747 if(ferror(f) || !feof(f)) {
1748 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1753 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1757 // Add new variable if necessary.
1758 if((action > 0 && !found)|| (action == 0 && !set)) {
1759 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1760 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1769 fprintf(stderr, "No matching configuration variables found.\n");
1774 // Make sure we wrote everything...
1776 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1780 // Could we find what we had to remove?
1781 if(action < 0 && !removed) {
1783 fprintf(stderr, "No configuration variables deleted.\n");
1787 // Replace the configuration file with the new one
1789 if(remove(filename)) {
1790 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1794 if(rename(tmpfile, filename)) {
1795 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1799 // Silently try notifying a running tincd of changes.
1800 if(connect_tincd(false))
1801 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1806 static bool try_bind(int port) {
1807 struct addrinfo *ai = NULL;
1808 struct addrinfo hint = {
1809 .ai_flags = AI_PASSIVE,
1810 .ai_family = AF_UNSPEC,
1811 .ai_socktype = SOCK_STREAM,
1812 .ai_protocol = IPPROTO_TCP,
1816 snprintf(portstr, sizeof portstr, "%d", port);
1818 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1822 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1825 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1835 int check_port(char *name) {
1839 fprintf(stderr, "Warning: could not bind to port 655. ");
1841 for(int i = 0; i < 100; i++) {
1842 int port = 0x1000 + (rand() & 0x7fff);
1843 if(try_bind(port)) {
1844 char filename[PATH_MAX];
1845 snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1846 FILE *f = fopen(filename, "a");
1848 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
1849 fprintf(stderr, "Please change tinc's Port manually.\n");
1853 fprintf(f, "Port = %d\n", port);
1855 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1860 fprintf(stderr, "Please change tinc's Port manually.\n");
1864 static int cmd_init(int argc, char *argv[]) {
1865 if(!access(tinc_conf, F_OK)) {
1866 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1871 fprintf(stderr, "Too many arguments!\n");
1873 } else if(argc < 2) {
1876 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1877 if(!fgets(buf, sizeof buf, stdin)) {
1878 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1881 int len = rstrip(buf);
1883 fprintf(stderr, "No name given!\n");
1888 fprintf(stderr, "No Name given!\n");
1892 name = strdup(argv[1]);
1894 fprintf(stderr, "No Name given!\n");
1899 if(!check_id(name)) {
1900 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1904 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1905 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1909 if(mkdir(confbase, 0777) && errno != EEXIST) {
1910 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1914 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1915 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1919 FILE *f = fopen(tinc_conf, "w");
1921 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1925 fprintf(f, "Name = %s\n", name);
1928 #ifndef DISABLE_LEGACY
1929 if(!rsa_keygen(2048, false))
1933 if(!ed25519_keygen(false))
1939 char filename[PATH_MAX];
1940 snprintf(filename, sizeof filename, "%s" SLASH "tinc-up", confbase);
1941 if(access(filename, F_OK)) {
1942 FILE *f = fopenmask(filename, "w", 0777);
1944 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1947 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");
1956 static int cmd_generate_keys(int argc, char *argv[]) {
1957 #ifdef DISABLE_LEGACY
1962 fprintf(stderr, "Too many arguments!\n");
1967 name = get_my_name(false);
1969 #ifndef DISABLE_LEGACY
1970 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1974 if(!ed25519_keygen(true))
1980 #ifndef DISABLE_LEGACY
1981 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1983 fprintf(stderr, "Too many arguments!\n");
1988 name = get_my_name(false);
1990 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1994 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1996 fprintf(stderr, "Too many arguments!\n");
2001 name = get_my_name(false);
2003 return !ed25519_keygen(true);
2006 static int cmd_help(int argc, char *argv[]) {
2011 static int cmd_version(int argc, char *argv[]) {
2013 fprintf(stderr, "Too many arguments!\n");
2021 static int cmd_info(int argc, char *argv[]) {
2023 fprintf(stderr, "Invalid number of arguments.\n");
2027 if(!connect_tincd(true))
2030 return info(fd, argv[1]);
2033 static const char *conffiles[] = {
2044 static int cmd_edit(int argc, char *argv[]) {
2046 fprintf(stderr, "Invalid number of arguments.\n");
2050 char filename[PATH_MAX] = "";
2052 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2053 for(int i = 0; conffiles[i]; i++) {
2054 if(!strcmp(argv[1], conffiles[i])) {
2055 snprintf(filename, sizeof filename, "%s" SLASH "%s", confbase, argv[1]);
2064 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, argv[1]);
2065 char *dash = strchr(argv[1], '-');
2068 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2069 fprintf(stderr, "Invalid configuration filename.\n");
2077 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
2079 xasprintf(&command, "edit \"%s\"", filename);
2081 int result = system(command);
2086 // Silently try notifying a running tincd of changes.
2087 if(connect_tincd(false))
2088 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2093 static int export(const char *name, FILE *out) {
2094 char filename[PATH_MAX];
2095 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2096 FILE *in = fopen(filename, "r");
2098 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2102 fprintf(out, "Name = %s\n", name);
2104 while(fgets(buf, sizeof buf, in)) {
2105 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
2110 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2119 static int cmd_export(int argc, char *argv[]) {
2121 fprintf(stderr, "Too many arguments!\n");
2125 char *name = get_my_name(true);
2129 int result = export(name, stdout);
2137 static int cmd_export_all(int argc, char *argv[]) {
2139 fprintf(stderr, "Too many arguments!\n");
2143 DIR *dir = opendir(hosts_dir);
2145 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2153 while((ent = readdir(dir))) {
2154 if(!check_id(ent->d_name))
2160 printf("#---------------------------------------------------------------#\n");
2162 result |= export(ent->d_name, stdout);
2171 static int cmd_import(int argc, char *argv[]) {
2173 fprintf(stderr, "Too many arguments!\n");
2182 char filename[PATH_MAX] = "";
2184 bool firstline = true;
2186 while(fgets(buf, sizeof buf, in)) {
2187 if(sscanf(buf, "Name = %s", name) == 1) {
2190 if(!check_id(name)) {
2191 fprintf(stderr, "Invalid Name in input!\n");
2198 snprintf(filename, sizeof filename, "%s" SLASH "%s", hosts_dir, name);
2200 if(!force && !access(filename, F_OK)) {
2201 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2206 out = fopen(filename, "w");
2208 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2214 } else if(firstline) {
2215 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2220 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2224 if(fputs(buf, out) < 0) {
2225 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2235 fprintf(stderr, "Imported %d host configuration files.\n", count);
2238 fprintf(stderr, "No host configuration files imported.\n");
2243 static int cmd_exchange(int argc, char *argv[]) {
2244 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2247 static int cmd_exchange_all(int argc, char *argv[]) {
2248 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2251 static int switch_network(char *name) {
2263 free(unixsocketname);
2264 unixsocketname = NULL;
2270 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2272 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2273 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2274 xasprintf(&prompt, "%s> ", identname);
2279 static int cmd_network(int argc, char *argv[]) {
2281 fprintf(stderr, "Too many arguments!\n");
2286 return switch_network(argv[1]);
2288 DIR *dir = opendir(confdir);
2290 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2295 while((ent = readdir(dir))) {
2296 if(*ent->d_name == '.')
2299 if(!strcmp(ent->d_name, "tinc.conf")) {
2304 char fname[PATH_MAX];
2305 snprintf(fname, sizeof fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2306 if(!access(fname, R_OK))
2307 printf("%s\n", ent->d_name);
2315 static int cmd_fsck(int argc, char *argv[]) {
2317 fprintf(stderr, "Too many arguments!\n");
2321 return fsck(orig_argv[0]);
2324 static const struct {
2325 const char *command;
2326 int (*function)(int argc, char *argv[]);
2329 {"start", cmd_start},
2331 {"restart", cmd_restart},
2332 {"reload", cmd_reload},
2335 {"purge", cmd_purge},
2336 {"debug", cmd_debug},
2337 {"retry", cmd_retry},
2338 {"connect", cmd_connect},
2339 {"disconnect", cmd_disconnect},
2344 {"config", cmd_config, true},
2345 {"add", cmd_config},
2346 {"del", cmd_config},
2347 {"get", cmd_config},
2348 {"set", cmd_config},
2350 {"generate-keys", cmd_generate_keys},
2351 #ifndef DISABLE_LEGACY
2352 {"generate-rsa-keys", cmd_generate_rsa_keys},
2354 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2356 {"version", cmd_version},
2359 {"export", cmd_export},
2360 {"export-all", cmd_export_all},
2361 {"import", cmd_import},
2362 {"exchange", cmd_exchange},
2363 {"exchange-all", cmd_exchange_all},
2364 {"invite", cmd_invite},
2366 {"network", cmd_network},
2371 #ifdef HAVE_READLINE
2372 static char *complete_command(const char *text, int state) {
2380 while(commands[i].command) {
2381 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2382 return xstrdup(commands[i].command);
2389 static char *complete_dump(const char *text, int state) {
2390 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2399 if(!strncasecmp(matches[i], text, strlen(text)))
2400 return xstrdup(matches[i]);
2407 static char *complete_config(const char *text, int state) {
2415 while(variables[i].name) {
2416 char *dot = strchr(text, '.');
2418 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2420 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2424 if(!strncasecmp(variables[i].name, text, strlen(text)))
2425 return xstrdup(variables[i].name);
2433 static char *complete_info(const char *text, int state) {
2437 if(!connect_tincd(false))
2439 // Check the list of nodes
2440 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2441 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2444 while(recvline(fd, line, sizeof line)) {
2446 int n = sscanf(line, "%d %d %s", &code, &req, item);
2456 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2460 if(!strncmp(item, text, strlen(text)))
2461 return xstrdup(strip_weight(item));
2467 static char *complete_nothing(const char *text, int state) {
2471 static char **completion (const char *text, int start, int end) {
2472 char **matches = NULL;
2475 matches = rl_completion_matches(text, complete_command);
2476 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2477 matches = rl_completion_matches(text, complete_dump);
2478 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2479 matches = rl_completion_matches(text, complete_config);
2480 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2481 matches = rl_completion_matches(text, complete_config);
2482 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2483 matches = rl_completion_matches(text, complete_config);
2484 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2485 matches = rl_completion_matches(text, complete_config);
2486 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2487 matches = rl_completion_matches(text, complete_info);
2493 static int cmd_shell(int argc, char *argv[]) {
2494 xasprintf(&prompt, "%s> ", identname);
2498 int maxargs = argc + 16;
2499 char **nargv = xmalloc(maxargs * sizeof *nargv);
2501 for(int i = 0; i < argc; i++)
2504 #ifdef HAVE_READLINE
2505 rl_readline_name = "tinc";
2506 rl_completion_entry_function = complete_nothing;
2507 rl_attempted_completion_function = completion;
2508 rl_filename_completion_desired = 0;
2513 #ifdef HAVE_READLINE
2517 rl_basic_word_break_characters = "\t\n ";
2518 line = readline(prompt);
2520 copy = xstrdup(line);
2522 line = fgets(buf, sizeof buf, stdin);
2526 fputs(prompt, stdout);
2528 line = fgets(buf, sizeof buf, stdin);
2534 /* Ignore comments */
2542 char *p = line + strspn(line, " \t\n");
2543 char *next = strtok(p, " \t\n");
2546 if(nargc >= maxargs) {
2547 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2550 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2555 next = strtok(NULL, " \t\n");
2561 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2566 for(int i = 0; commands[i].command; i++) {
2567 if(!strcasecmp(nargv[argc], commands[i].command)) {
2568 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2574 #ifdef HAVE_READLINE
2580 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2593 int main(int argc, char *argv[]) {
2594 program_name = argv[0];
2597 tty = isatty(0) && isatty(1);
2599 if(!parse_options(argc, argv))
2603 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2604 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2617 static struct WSAData wsa_state;
2619 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2620 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2629 return cmd_shell(argc, argv);
2631 for(int i = 0; commands[i].command; i++) {
2632 if(!strcasecmp(argv[optind], commands[i].command))
2633 return commands[i].function(argc - optind, argv + optind);
2636 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);