2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2018 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 = "";
81 static struct option const long_options[] = {
82 {"batch", no_argument, NULL, 'b'},
83 {"config", required_argument, NULL, 'c'},
84 {"net", required_argument, NULL, 'n'},
85 {"help", no_argument, NULL, 1},
86 {"version", no_argument, NULL, 2},
87 {"pidfile", required_argument, NULL, 3},
88 {"force", no_argument, NULL, 4},
92 static void version(void) {
93 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
94 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
95 printf("Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
96 "See the AUTHORS file for a complete list.\n\n"
97 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
98 "and you are welcome to redistribute it under certain conditions;\n"
99 "see the file COPYING for details.\n");
102 static void usage(bool status) {
104 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
106 printf("Usage: %s [options] command\n\n", program_name);
107 printf("Valid options are:\n"
108 " -b, --batch Don't ask for anything (non-interactive mode).\n"
109 " -c, --config=DIR Read configuration options from DIR.\n"
110 " -n, --net=NETNAME Connect to net NETNAME.\n"
111 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
112 " --force Force some commands to work despite warnings.\n"
113 " --help Display this help and exit.\n"
114 " --version Output version information and exit.\n"
116 "Valid commands are:\n"
117 " init [name] Create initial configuration files.\n"
118 " get VARIABLE Print current value of VARIABLE\n"
119 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
120 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
121 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
122 " start [tincd options] Start tincd.\n"
123 " stop Stop tincd.\n"
124 " restart [tincd options] Restart tincd.\n"
125 " reload Partially reload configuration of running tincd.\n"
126 " pid Show PID of currently running tincd.\n"
127 #ifdef DISABLE_LEGACY
128 " generate-keys Generate a new Ed25519 public/private keypair.\n"
130 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
131 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
133 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
134 " dump Dump a list of one of the following things:\n"
135 " [reachable] nodes - all known nodes in the VPN\n"
136 " edges - all known connections in the VPN\n"
137 " subnets - all known subnets in the VPN\n"
138 " connections - all meta connections with ourself\n"
139 " [di]graph - graph of the VPN in dotty format\n"
140 " invitations - outstanding invitations\n"
141 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
142 " purge Purge unreachable nodes\n"
143 " debug N Set debug level\n"
144 " retry Retry all outgoing connections\n"
145 " disconnect NODE Close meta connection with NODE\n"
147 " top Show real-time statistics\n"
149 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
150 " log [level] Dump log output [up to the specified level]\n"
151 " export Export host configuration of local node to standard output\n"
152 " export-all Export all host configuration files to standard output\n"
153 " import Import host configuration file(s) from standard input\n"
154 " exchange Same as export followed by import\n"
155 " exchange-all Same as export-all followed by import\n"
156 " invite NODE [...] Generate an invitation for NODE\n"
157 " join INVITATION Join a VPN using an INVITATION\n"
158 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
159 " fsck Check the configuration files for problems.\n"
160 " sign [FILE] Generate a signed version of a file.\n"
161 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
163 printf("Report bugs to tinc@tinc-vpn.org.\n");
167 static bool parse_options(int argc, char **argv) {
169 int option_index = 0;
171 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
173 case 0: /* long option */
180 case 'c': /* config file */
181 confbase = xstrdup(optarg);
182 confbasegiven = true;
185 case 'n': /* net name given */
186 netname = xstrdup(optarg);
189 case 1: /* show help */
193 case 2: /* show version */
197 case 3: /* open control socket here */
198 pidfilename = xstrdup(optarg);
205 case '?': /* wrong options */
214 if(!netname && (netname = getenv("NETNAME"))) {
215 netname = xstrdup(netname);
218 /* netname "." is special: a "top-level name" */
220 if(netname && (!*netname || !strcmp(netname, "."))) {
225 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
226 fprintf(stderr, "Invalid character in netname!\n");
233 /* Open a file with the desired permissions, minus the umask.
234 Also, if we want to create an executable file, we call fchmod()
235 to set the executable bits. */
237 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
238 mode_t mask = umask(0);
241 FILE *f = fopen(filename, mode);
244 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
250 if((perms & 0444) && f) {
251 fchmod(fileno(f), perms);
259 static void disable_old_keys(const char *filename, const char *what) {
260 char tmpfile[PATH_MAX] = "";
262 bool disabled = false;
267 r = fopen(filename, "r");
273 snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
275 struct stat st = {.st_mode = 0600};
276 fstat(fileno(r), &st);
277 w = fopenmask(tmpfile, "w", st.st_mode);
279 while(fgets(buf, sizeof(buf), r)) {
280 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
281 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
287 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
294 if(block || ed25519pubkey) {
298 if(fputs(buf, w) < 0) {
304 if(block && !strncmp(buf, "-----END ", 9)) {
314 if(ferror(r) || fclose(r) < 0) {
320 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
330 // We cannot atomically replace files on Windows.
331 char bakfile[PATH_MAX] = "";
332 snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
334 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
335 rename(bakfile, filename);
338 if(rename(tmpfile, filename)) {
340 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
345 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
352 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
354 char directory[PATH_MAX] = ".";
360 /* Check stdin and stdout */
362 /* Ask for a file and/or directory name. */
363 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
365 if(fgets(buf, sizeof(buf), stdin) == NULL) {
366 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
370 size_t len = strlen(buf);
383 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
386 if(filename[0] != '/') {
388 /* The directory is a relative path or a filename. */
389 getcwd(directory, sizeof(directory));
391 if(snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
392 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
404 disable_old_keys(filename, what);
406 /* Open it first to keep the inode busy */
408 r = fopenmask(filename, mode, perms);
411 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
419 Generate a public/private Ed25519 keypair, and ask for a file to store
422 static bool ed25519_keygen(bool ask) {
425 char fname[PATH_MAX];
427 fprintf(stderr, "Generating Ed25519 keypair:\n");
429 if(!(key = ecdsa_generate())) {
430 fprintf(stderr, "Error during key generation!\n");
433 fprintf(stderr, "Done.\n");
436 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
437 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
443 if(!ecdsa_write_pem_private_key(key, f)) {
444 fprintf(stderr, "Error writing private key!\n");
451 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
453 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
456 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
462 char *pubkey = ecdsa_get_base64_public_key(key);
463 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
481 #ifndef DISABLE_LEGACY
483 Generate a public/private RSA keypair, and ask for a file to store
486 static bool rsa_keygen(int bits, bool ask) {
489 char fname[PATH_MAX];
491 // Make sure the key size is a multiple of 8 bits.
494 // Make sure that a valid key size is used.
495 if(bits < 1024 || bits > 8192) {
496 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
498 } else if(bits < 2048) {
499 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
502 fprintf(stderr, "Generating %d bits keys:\n", bits);
504 if(!(key = rsa_generate(bits, 0x10001))) {
505 fprintf(stderr, "Error during key generation!\n");
508 fprintf(stderr, "Done.\n");
511 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
512 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
518 if(!rsa_write_pem_private_key(key, f)) {
519 fprintf(stderr, "Error writing private key!\n");
526 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
528 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
531 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
537 if(!rsa_write_pem_public_key(key, f)) {
538 fprintf(stderr, "Error writing public key!\n");
561 bool recvline(int fd, char *line, size_t len) {
562 char *newline = NULL;
568 while(!(newline = memchr(buffer, '\n', blen))) {
569 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
571 if(result == -1 && sockerrno == EINTR) {
573 } else if(result <= 0) {
580 if(newline - buffer >= len) {
584 len = newline - buffer;
586 memcpy(line, buffer, len);
588 memmove(buffer, newline + 1, blen - len - 1);
594 bool recvdata(int fd, char *data, size_t len) {
600 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
602 if(result == -1 && sockerrno == EINTR) {
604 } else if(result <= 0) {
611 memcpy(data, buffer, len);
612 memmove(buffer, buffer + len, blen - len);
618 bool sendline(int fd, char *format, ...) {
619 static char buffer[4096];
624 va_start(ap, format);
625 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
626 buffer[sizeof(buffer) - 1] = 0;
629 if(blen < 1 || blen >= sizeof(buffer)) {
637 int result = send(fd, p, blen, MSG_NOSIGNAL);
639 if(result == -1 && sockerrno == EINTR) {
641 } else if(result <= 0) {
652 static void pcap(int fd, FILE *out, int snaplen) {
653 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
661 uint32_t tz_accuracy;
668 snaplen ? : sizeof(data),
681 fwrite(&header, sizeof(header), 1, out);
686 while(recvline(fd, line, sizeof(line))) {
688 int n = sscanf(line, "%d %d %d", &code, &req, &len);
689 gettimeofday(&tv, NULL);
691 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof(data)) {
695 if(!recvdata(fd, data, len)) {
699 packet.tv_sec = tv.tv_sec;
700 packet.tv_usec = tv.tv_usec;
702 packet.origlen = len;
703 fwrite(&packet, sizeof(packet), 1, out);
704 fwrite(data, len, 1, out);
709 static void logcontrol(int fd, FILE *out, int level) {
710 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
714 while(recvline(fd, line, sizeof(line))) {
716 int n = sscanf(line, "%d %d %d", &code, &req, &len);
718 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof(data)) {
722 if(!recvdata(fd, data, len)) {
726 fwrite(data, len, 1, out);
733 static bool remove_service(void) {
734 SC_HANDLE manager = NULL;
735 SC_HANDLE service = NULL;
736 SERVICE_STATUS status = {0};
737 bool success = false;
739 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
742 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
746 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
749 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
753 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
754 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
756 fprintf(stderr, "%s service stopped\n", identname);
759 if(!DeleteService(service)) {
760 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
769 CloseServiceHandle(service);
773 CloseServiceHandle(manager);
777 fprintf(stderr, "%s service removed\n", identname);
784 bool connect_tincd(bool verbose) {
789 struct timeval tv = {0, 0};
791 if(select(fd + 1, &r, NULL, NULL, &tv)) {
792 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
800 FILE *f = fopen(pidfilename, "r");
804 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
813 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
815 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
826 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
827 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
828 /* clean up the stale socket and pid file */
830 unlink(unixsocketname);
834 struct sockaddr_un sa;
836 sa.sun_family = AF_UNIX;
838 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
840 sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
842 fd = socket(AF_UNIX, SOCK_STREAM, 0);
846 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
852 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
854 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
863 struct addrinfo hints = {
864 .ai_family = AF_UNSPEC,
865 .ai_socktype = SOCK_STREAM,
866 .ai_protocol = IPPROTO_TCP,
870 struct addrinfo *res = NULL;
872 if(getaddrinfo(host, port, &hints, &res) || !res) {
874 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
880 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
884 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
891 unsigned long arg = 0;
893 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
895 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
901 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
903 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
915 static const int one = 1;
916 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
919 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
924 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
926 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
934 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
936 fprintf(stderr, "Could not fully establish control socket connection\n");
948 static int cmd_start(int argc, char *argv[]) {
949 if(connect_tincd(false)) {
951 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
953 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
960 char *slash = strrchr(program_name, '/');
964 if((c = strrchr(program_name, '\\')) > slash) {
971 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
977 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
982 Windows has no real concept of an "argv array". A command line is just one string.
983 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
984 it uses quotes to handle spaces in arguments.
985 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
986 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
987 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
989 xasprintf(&arg0, "\"%s\"", arg0);
991 nargv[nargc++] = arg0;
993 for(int i = 1; i < optind; i++) {
994 nargv[nargc++] = orig_argv[i];
997 for(int i = 1; i < argc; i++) {
998 nargv[nargc++] = argv[i];
1002 int status = spawnvp(_P_WAIT, c, nargv);
1005 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
1011 int pfd[2] = {-1, -1};
1013 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
1014 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
1022 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1030 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1031 setenv("TINC_UMBILICAL", buf, true);
1032 exit(execvp(c, nargv));
1039 int status = -1, result;
1041 signal(SIGINT, SIG_IGN);
1044 // Pass all log messages from the umbilical to stderr.
1045 // A nul-byte right before closure means tincd started successfully.
1046 bool failure = true;
1050 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1051 failure = buf[len - 1];
1066 // Make sure the child process is really gone.
1067 result = waitpid(pid, &status, 0);
1070 signal(SIGINT, SIG_DFL);
1073 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1074 fprintf(stderr, "Error starting %s\n", c);
1082 static int cmd_stop(int argc, char *argv[]) {
1084 fprintf(stderr, "Too many arguments!\n");
1090 if(!connect_tincd(true)) {
1092 if(kill(pid, SIGTERM)) {
1093 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1097 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1098 waitpid(pid, NULL, 0);
1105 sendline(fd, "%d %d", CONTROL, REQ_STOP);
1107 while(recvline(fd, line, sizeof(line))) {
1108 // Wait for tincd to close the connection...
1113 if(!remove_service()) {
1125 static int cmd_restart(int argc, char *argv[]) {
1127 return cmd_start(argc, argv);
1130 static int cmd_reload(int argc, char *argv[]) {
1132 fprintf(stderr, "Too many arguments!\n");
1136 if(!connect_tincd(true)) {
1140 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1142 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1143 fprintf(stderr, "Could not reload configuration.\n");
1151 static int dump_invitations(void) {
1152 char dname[PATH_MAX];
1153 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1154 DIR *dir = opendir(dname);
1157 if(errno == ENOENT) {
1158 fprintf(stderr, "No outstanding invitations.\n");
1162 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1170 while((ent = readdir(dir))) {
1171 char buf[MAX_STRING_SIZE];
1173 if(b64decode(ent->d_name, buf, 24) != 18) {
1177 char fname[PATH_MAX];
1179 if(snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1180 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1184 FILE *f = fopen(fname, "r");
1187 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1193 if(!fgets(buf, sizeof(buf), f)) {
1194 fprintf(stderr, "Invalid invitation file %s\n", fname);
1201 char *eol = buf + strlen(buf);
1203 while(strchr("\t \r\n", *--eol)) {
1207 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1208 fprintf(stderr, "Invalid invitation file %s\n", fname);
1213 printf("%s %s\n", ent->d_name, buf + 7);
1219 fprintf(stderr, "No outstanding invitations.\n");
1225 static int cmd_dump(int argc, char *argv[]) {
1226 bool only_reachable = false;
1228 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1229 if(strcasecmp(argv[2], "nodes")) {
1230 fprintf(stderr, "`reachable' only supported for nodes.\n");
1235 only_reachable = true;
1241 fprintf(stderr, "Invalid number of arguments.\n");
1246 if(!strcasecmp(argv[1], "invitations")) {
1247 return dump_invitations();
1250 if(!connect_tincd(true)) {
1256 if(!strcasecmp(argv[1], "nodes")) {
1257 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1258 } else if(!strcasecmp(argv[1], "edges")) {
1259 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1260 } else if(!strcasecmp(argv[1], "subnets")) {
1261 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1262 } else if(!strcasecmp(argv[1], "connections")) {
1263 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1264 } else if(!strcasecmp(argv[1], "graph")) {
1265 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1266 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1268 } else if(!strcasecmp(argv[1], "digraph")) {
1269 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1270 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1273 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1279 printf("graph {\n");
1280 } else if(do_graph == 2) {
1281 printf("digraph {\n");
1284 while(recvline(fd, line, sizeof(line))) {
1285 char node1[4096], node2[4096];
1286 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1289 if(do_graph && req == REQ_DUMP_NODES) {
1311 char local_host[4096];
1312 char local_port[4096];
1315 int cipher, digest, maclength, compression, distance, socket, weight;
1316 short int pmtu, minmtu, maxmtu;
1317 unsigned int options, status_int;
1318 node_status_t status;
1319 long int last_state_change;
1321 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1324 case REQ_DUMP_NODES: {
1325 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %d %d %d %d %x %x %4095s %4095s %d %hd %hd %hd %ld %d %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change, &udp_ping_rtt, &in_packets, &in_bytes, &out_packets, &out_bytes);
1328 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1332 memcpy(&status, &status_int, sizeof(status));
1335 const char *color = "black";
1337 if(!strcmp(host, "MYSELF")) {
1339 } else if(!status.reachable) {
1341 } else if(strcmp(via, node)) {
1343 } else if(!status.validkey) {
1345 } else if(minmtu > 0) {
1349 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1351 if(only_reachable && !status.reachable) {
1355 printf("%s id %s at %s port %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s distance %d pmtu %d (min %d max %d) rx %"PRIu64" %"PRIu64" tx %"PRIu64" %"PRIu64,
1356 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu, in_packets, in_bytes, out_packets, out_bytes);
1358 if(udp_ping_rtt != -1) {
1359 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1367 case REQ_DUMP_EDGES: {
1368 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %4095s port %4095s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
1371 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1376 float w = 1 + 65536.0 / weight;
1378 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1379 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1380 } else if(do_graph == 2) {
1381 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1384 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);
1389 case REQ_DUMP_SUBNETS: {
1390 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1393 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1397 printf("%s owner %s\n", strip_weight(subnet), node);
1401 case REQ_DUMP_CONNECTIONS: {
1402 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1405 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1409 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1414 fprintf(stderr, "Unable to parse dump from tincd.\n");
1419 fprintf(stderr, "Error receiving dump.\n");
1423 static int cmd_purge(int argc, char *argv[]) {
1425 fprintf(stderr, "Too many arguments!\n");
1429 if(!connect_tincd(true)) {
1433 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1435 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1436 fprintf(stderr, "Could not purge old information.\n");
1443 static int cmd_debug(int argc, char *argv[]) {
1445 fprintf(stderr, "Invalid number of arguments.\n");
1449 if(!connect_tincd(true)) {
1453 int debuglevel = atoi(argv[1]);
1456 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1458 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1459 fprintf(stderr, "Could not set debug level.\n");
1463 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1467 static int cmd_retry(int argc, char *argv[]) {
1469 fprintf(stderr, "Too many arguments!\n");
1473 if(!connect_tincd(true)) {
1477 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1479 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1480 fprintf(stderr, "Could not retry outgoing connections.\n");
1487 static int cmd_connect(int argc, char *argv[]) {
1489 fprintf(stderr, "Invalid number of arguments.\n");
1493 if(!check_id(argv[1])) {
1494 fprintf(stderr, "Invalid name for node.\n");
1498 if(!connect_tincd(true)) {
1502 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1504 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1505 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1512 static int cmd_disconnect(int argc, char *argv[]) {
1514 fprintf(stderr, "Invalid number of arguments.\n");
1518 if(!check_id(argv[1])) {
1519 fprintf(stderr, "Invalid name for node.\n");
1523 if(!connect_tincd(true)) {
1527 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1529 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1530 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1537 static int cmd_top(int argc, char *argv[]) {
1539 fprintf(stderr, "Too many arguments!\n");
1545 if(!connect_tincd(true)) {
1552 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1557 static int cmd_pcap(int argc, char *argv[]) {
1559 fprintf(stderr, "Too many arguments!\n");
1563 if(!connect_tincd(true)) {
1567 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1572 static void sigint_handler(int sig) {
1573 fprintf(stderr, "\n");
1574 shutdown(fd, SHUT_RDWR);
1578 static int cmd_log(int argc, char *argv[]) {
1580 fprintf(stderr, "Too many arguments!\n");
1584 if(!connect_tincd(true)) {
1589 signal(SIGINT, sigint_handler);
1592 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1595 signal(SIGINT, SIG_DFL);
1603 static int cmd_pid(int argc, char *argv[]) {
1605 fprintf(stderr, "Too many arguments!\n");
1609 if(!connect_tincd(true) || !pid) {
1613 printf("%d\n", pid);
1617 int rstrip(char *value) {
1618 int len = strlen(value);
1620 while(len && strchr("\t\r\n ", value[len - 1])) {
1627 char *get_my_name(bool verbose) {
1628 FILE *f = fopen(tinc_conf, "r");
1632 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1641 while(fgets(buf, sizeof(buf), f)) {
1642 int len = strcspn(buf, "\t =");
1644 value += strspn(value, "\t ");
1648 value += strspn(value, "\t ");
1651 if(!rstrip(value)) {
1657 if(strcasecmp(buf, "Name")) {
1663 return replace_name(value);
1670 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1676 ecdsa_t *get_pubkey(FILE *f) {
1680 while(fgets(buf, sizeof(buf), f)) {
1681 int len = strcspn(buf, "\t =");
1683 value += strspn(value, "\t ");
1687 value += strspn(value, "\t ");
1690 if(!rstrip(value)) {
1696 if(strcasecmp(buf, "Ed25519PublicKey")) {
1701 return ecdsa_set_base64_public_key(value);
1708 const var_t variables[] = {
1709 /* Server configuration */
1710 {"AddressFamily", VAR_SERVER},
1711 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1712 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1713 {"BindToInterface", VAR_SERVER},
1714 {"Broadcast", VAR_SERVER | VAR_SAFE},
1715 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1716 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1717 {"DecrementTTL", VAR_SERVER},
1718 {"Device", VAR_SERVER},
1719 {"DeviceStandby", VAR_SERVER},
1720 {"DeviceType", VAR_SERVER},
1721 {"DirectOnly", VAR_SERVER},
1722 {"Ed25519PrivateKeyFile", VAR_SERVER},
1723 {"ExperimentalProtocol", VAR_SERVER},
1724 {"Forwarding", VAR_SERVER},
1725 {"FWMark", VAR_SERVER},
1726 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1727 {"Hostnames", VAR_SERVER},
1728 {"IffOneQueue", VAR_SERVER},
1729 {"Interface", VAR_SERVER},
1730 {"InvitationExpire", VAR_SERVER},
1731 {"KeyExpire", VAR_SERVER},
1732 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1733 {"LocalDiscovery", VAR_SERVER},
1734 {"LogLevel", VAR_SERVER},
1735 {"MACExpire", VAR_SERVER},
1736 {"MaxConnectionBurst", VAR_SERVER},
1737 {"MaxOutputBufferSize", VAR_SERVER},
1738 {"MaxTimeout", VAR_SERVER},
1739 {"Mode", VAR_SERVER | VAR_SAFE},
1740 {"Name", VAR_SERVER},
1741 {"PingInterval", VAR_SERVER},
1742 {"PingTimeout", VAR_SERVER},
1743 {"PriorityInheritance", VAR_SERVER},
1744 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1745 {"PrivateKeyFile", VAR_SERVER},
1746 {"ProcessPriority", VAR_SERVER},
1747 {"Proxy", VAR_SERVER},
1748 {"ReplayWindow", VAR_SERVER},
1749 {"ScriptsExtension", VAR_SERVER},
1750 {"ScriptsInterpreter", VAR_SERVER},
1751 {"StrictSubnets", VAR_SERVER},
1752 {"TunnelServer", VAR_SERVER},
1753 {"UDPDiscovery", VAR_SERVER},
1754 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1755 {"UDPDiscoveryInterval", VAR_SERVER},
1756 {"UDPDiscoveryTimeout", VAR_SERVER},
1757 {"MTUInfoInterval", VAR_SERVER},
1758 {"UDPInfoInterval", VAR_SERVER},
1759 {"UDPRcvBuf", VAR_SERVER},
1760 {"UDPSndBuf", VAR_SERVER},
1761 {"UPnP", VAR_SERVER},
1762 {"UPnPDiscoverWait", VAR_SERVER},
1763 {"UPnPRefreshPeriod", VAR_SERVER},
1764 {"VDEGroup", VAR_SERVER},
1765 {"VDEPort", VAR_SERVER},
1766 /* Host configuration */
1767 {"Address", VAR_HOST | VAR_MULTIPLE},
1768 {"Cipher", VAR_SERVER | VAR_HOST},
1769 {"ClampMSS", VAR_SERVER | VAR_HOST},
1770 {"Compression", VAR_SERVER | VAR_HOST},
1771 {"Digest", VAR_SERVER | VAR_HOST},
1772 {"Ed25519PublicKey", VAR_HOST},
1773 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1774 {"IndirectData", VAR_SERVER | VAR_HOST},
1775 {"MACLength", VAR_SERVER | VAR_HOST},
1776 {"PMTU", VAR_SERVER | VAR_HOST},
1777 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1779 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1780 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1781 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1782 {"TCPOnly", VAR_SERVER | VAR_HOST},
1783 {"Weight", VAR_HOST | VAR_SAFE},
1787 static int cmd_config(int argc, char *argv[]) {
1789 fprintf(stderr, "Invalid number of arguments.\n");
1793 if(strcasecmp(argv[0], "config")) {
1799 if(!strcasecmp(argv[1], "get")) {
1801 } else if(!strcasecmp(argv[1], "add")) {
1802 argv++, argc--, action = 1;
1803 } else if(!strcasecmp(argv[1], "del")) {
1804 argv++, argc--, action = -1;
1805 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1806 argv++, argc--, action = 0;
1810 fprintf(stderr, "Invalid number of arguments.\n");
1814 // Concatenate the rest of the command line
1815 strncpy(line, argv[1], sizeof(line) - 1);
1817 for(int i = 2; i < argc; i++) {
1818 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1819 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1822 // Liberal parsing into node name, variable name and value.
1828 len = strcspn(line, "\t =");
1830 value += strspn(value, "\t ");
1834 value += strspn(value, "\t ");
1838 variable = strchr(line, '.');
1848 fprintf(stderr, "No variable given.\n");
1852 if(action >= 0 && !*value) {
1853 fprintf(stderr, "No value for variable given.\n");
1857 if(action < -1 && *value) {
1861 /* Some simple checks. */
1863 bool warnonremove = false;
1865 for(int i = 0; variables[i].name; i++) {
1866 if(strcasecmp(variables[i].name, variable)) {
1871 variable = (char *)variables[i].name;
1873 /* Discourage use of obsolete variables. */
1875 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1877 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1879 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1884 /* Don't put server variables in host config files */
1886 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1888 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1890 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1895 /* Should this go into our own host config file? */
1897 if(!node && !(variables[i].type & VAR_SERVER)) {
1898 node = get_my_name(true);
1905 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1906 Turn on warnings when it seems variables might be removed unintentionally. */
1908 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1909 warnonremove = true;
1911 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1912 warnonremove = true;
1918 if(node && !check_id(node)) {
1919 fprintf(stderr, "Invalid name for node.\n");
1924 if(force || action < 0) {
1925 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1927 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1932 // Open the right configuration file.
1933 char filename[PATH_MAX];
1936 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1938 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1941 FILE *f = fopen(filename, "r");
1944 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1948 char tmpfile[PATH_MAX];
1952 if(snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1953 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1957 tf = fopen(tmpfile, "w");
1960 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1966 // Copy the file, making modifications on the fly, unless we are just getting a value.
1970 bool removed = false;
1973 while(fgets(buf1, sizeof(buf1), f)) {
1974 buf1[sizeof(buf1) - 1] = 0;
1975 strncpy(buf2, buf1, sizeof(buf2));
1977 // Parse line in a simple way
1981 len = strcspn(buf2, "\t =");
1982 bvalue = buf2 + len;
1983 bvalue += strspn(bvalue, "\t ");
1985 if(*bvalue == '=') {
1987 bvalue += strspn(bvalue, "\t ");
1994 if(!strcasecmp(buf2, variable)) {
1998 printf("%s\n", bvalue);
2000 } else if(action == -1) {
2001 if(!*value || !strcasecmp(bvalue, value)) {
2007 } else if(action == 0) {
2008 // Warn if "set" was used for variables that can occur multiple times
2009 if(warnonremove && strcasecmp(bvalue, value)) {
2010 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2013 // Already set? Delete the rest...
2018 // Otherwise, replace.
2019 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2020 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2027 } else if(action > 0) {
2028 // Check if we've already seen this variable with the same value
2029 if(!strcasecmp(bvalue, value)) {
2036 // Copy original line...
2037 if(fputs(buf1, tf) < 0) {
2038 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2042 // Add newline if it is missing...
2043 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2044 if(fputc('\n', tf) < 0) {
2045 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2052 // Make sure we read everything...
2053 if(ferror(f) || !feof(f)) {
2054 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2059 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2063 // Add new variable if necessary.
2064 if((action > 0 && !found) || (action == 0 && !set)) {
2065 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2066 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2075 fprintf(stderr, "No matching configuration variables found.\n");
2080 // Make sure we wrote everything...
2082 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2086 // Could we find what we had to remove?
2087 if(action < 0 && !removed) {
2089 fprintf(stderr, "No configuration variables deleted.\n");
2093 // Replace the configuration file with the new one
2096 if(remove(filename)) {
2097 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2103 if(rename(tmpfile, filename)) {
2104 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2108 // Silently try notifying a running tincd of changes.
2109 if(connect_tincd(false)) {
2110 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2116 static bool try_bind(int port) {
2117 struct addrinfo *ai = NULL, *aip;
2118 struct addrinfo hint = {
2119 .ai_flags = AI_PASSIVE,
2120 .ai_family = AF_UNSPEC,
2121 .ai_socktype = SOCK_STREAM,
2122 .ai_protocol = IPPROTO_TCP,
2125 bool success = true;
2127 snprintf(portstr, sizeof(portstr), "%d", port);
2129 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2133 for(aip = ai; aip; aip = aip->ai_next) {
2134 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2141 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2154 int check_port(char *name) {
2159 fprintf(stderr, "Warning: could not bind to port 655. ");
2161 for(int i = 0; i < 100; i++) {
2162 int port = 0x1000 + (rand() & 0x7fff);
2164 if(try_bind(port)) {
2165 char filename[PATH_MAX];
2166 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2167 FILE *f = fopen(filename, "a");
2170 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2171 fprintf(stderr, "Please change tinc's Port manually.\n");
2175 fprintf(f, "Port = %d\n", port);
2177 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2182 fprintf(stderr, "Please change tinc's Port manually.\n");
2186 static int cmd_init(int argc, char *argv[]) {
2187 if(!access(tinc_conf, F_OK)) {
2188 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2193 fprintf(stderr, "Too many arguments!\n");
2195 } else if(argc < 2) {
2198 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2200 if(!fgets(buf, sizeof(buf), stdin)) {
2201 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2205 int len = rstrip(buf);
2208 fprintf(stderr, "No name given!\n");
2214 fprintf(stderr, "No Name given!\n");
2218 name = strdup(argv[1]);
2221 fprintf(stderr, "No Name given!\n");
2226 if(!check_id(name)) {
2227 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2231 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2232 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2236 if(mkdir(confbase, 0777) && errno != EEXIST) {
2237 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2241 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2242 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2246 FILE *f = fopen(tinc_conf, "w");
2249 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2253 fprintf(f, "Name = %s\n", name);
2256 #ifndef DISABLE_LEGACY
2258 if(!rsa_keygen(2048, false)) {
2264 if(!ed25519_keygen(false)) {
2271 char filename[PATH_MAX];
2272 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2274 if(access(filename, F_OK)) {
2275 FILE *f = fopenmask(filename, "w", 0777);
2278 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2282 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");
2292 static int cmd_generate_keys(int argc, char *argv[]) {
2293 #ifdef DISABLE_LEGACY
2300 fprintf(stderr, "Too many arguments!\n");
2305 name = get_my_name(false);
2308 #ifndef DISABLE_LEGACY
2310 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2316 if(!ed25519_keygen(true)) {
2323 #ifndef DISABLE_LEGACY
2324 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2326 fprintf(stderr, "Too many arguments!\n");
2331 name = get_my_name(false);
2334 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2338 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2340 fprintf(stderr, "Too many arguments!\n");
2345 name = get_my_name(false);
2348 return !ed25519_keygen(true);
2351 static int cmd_help(int argc, char *argv[]) {
2356 static int cmd_version(int argc, char *argv[]) {
2358 fprintf(stderr, "Too many arguments!\n");
2366 static int cmd_info(int argc, char *argv[]) {
2368 fprintf(stderr, "Invalid number of arguments.\n");
2372 if(!connect_tincd(true)) {
2376 return info(fd, argv[1]);
2379 static const char *conffiles[] = {
2390 static int cmd_edit(int argc, char *argv[]) {
2392 fprintf(stderr, "Invalid number of arguments.\n");
2396 char filename[PATH_MAX] = "";
2398 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2399 for(int i = 0; conffiles[i]; i++) {
2400 if(!strcmp(argv[1], conffiles[i])) {
2401 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2410 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2411 char *dash = strchr(argv[1], '-');
2416 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2417 fprintf(stderr, "Invalid configuration filename.\n");
2425 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ? : getenv("EDITOR") ? : "vi", filename);
2427 xasprintf(&command, "edit \"%s\"", filename);
2429 int result = system(command);
2436 // Silently try notifying a running tincd of changes.
2437 if(connect_tincd(false)) {
2438 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2444 static int export(const char *name, FILE *out) {
2445 char filename[PATH_MAX];
2446 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2447 FILE *in = fopen(filename, "r");
2450 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2454 fprintf(out, "Name = %s\n", name);
2457 while(fgets(buf, sizeof(buf), in)) {
2458 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2464 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2473 static int cmd_export(int argc, char *argv[]) {
2475 fprintf(stderr, "Too many arguments!\n");
2479 char *name = get_my_name(true);
2485 int result = export(name, stdout);
2495 static int cmd_export_all(int argc, char *argv[]) {
2497 fprintf(stderr, "Too many arguments!\n");
2501 DIR *dir = opendir(hosts_dir);
2504 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2512 while((ent = readdir(dir))) {
2513 if(!check_id(ent->d_name)) {
2520 printf("#---------------------------------------------------------------#\n");
2523 result |= export(ent->d_name, stdout);
2535 static int cmd_import(int argc, char *argv[]) {
2537 fprintf(stderr, "Too many arguments!\n");
2546 char filename[PATH_MAX] = "";
2548 bool firstline = true;
2550 while(fgets(buf, sizeof(buf), in)) {
2551 if(sscanf(buf, "Name = %4095s", name) == 1) {
2554 if(!check_id(name)) {
2555 fprintf(stderr, "Invalid Name in input!\n");
2563 if(snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2564 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2568 if(!force && !access(filename, F_OK)) {
2569 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2574 out = fopen(filename, "w");
2577 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2583 } else if(firstline) {
2584 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2589 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2594 if(fputs(buf, out) < 0) {
2595 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2606 fprintf(stderr, "Imported %d host configuration files.\n", count);
2609 fprintf(stderr, "No host configuration files imported.\n");
2614 static int cmd_exchange(int argc, char *argv[]) {
2615 return cmd_export(argc, argv) ? : cmd_import(argc, argv);
2618 static int cmd_exchange_all(int argc, char *argv[]) {
2619 return cmd_export_all(argc, argv) ? : cmd_import(argc, argv);
2622 static int switch_network(char *name) {
2623 if(strcmp(name, ".")) {
2624 if(!check_netname(name, false)) {
2625 fprintf(stderr, "Invalid character in netname!\n");
2629 if(!check_netname(name, true)) {
2630 fprintf(stderr, "Warning: unsafe character in netname!\n");
2640 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2647 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2648 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2649 xasprintf(&prompt, "%s> ", identname);
2654 static int cmd_network(int argc, char *argv[]) {
2656 fprintf(stderr, "Too many arguments!\n");
2661 return switch_network(argv[1]);
2664 DIR *dir = opendir(confdir);
2667 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2673 while((ent = readdir(dir))) {
2674 if(*ent->d_name == '.') {
2678 if(!strcmp(ent->d_name, "tinc.conf")) {
2683 char fname[PATH_MAX];
2684 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2686 if(!access(fname, R_OK)) {
2687 printf("%s\n", ent->d_name);
2696 static int cmd_fsck(int argc, char *argv[]) {
2698 fprintf(stderr, "Too many arguments!\n");
2702 return fsck(orig_argv[0]);
2705 static void *readfile(FILE *in, size_t *len) {
2707 size_t alloced = 4096;
2708 char *buf = xmalloc(alloced);
2711 size_t read = fread(buf + count, 1, alloced - count, in);
2719 if(count >= alloced) {
2721 buf = xrealloc(buf, alloced);
2732 static int cmd_sign(int argc, char *argv[]) {
2734 fprintf(stderr, "Too many arguments!\n");
2739 name = get_my_name(true);
2746 char fname[PATH_MAX];
2747 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2748 FILE *fp = fopen(fname, "r");
2751 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2755 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2758 fprintf(stderr, "Could not read private key from %s\n", fname);
2768 in = fopen(argv[1], "rb");
2771 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2780 char *data = readfile(in, &len);
2787 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2792 // Ensure we sign our name and current time as well
2793 long t = time(NULL);
2795 xasprintf(&trailer, " %s %ld", name, t);
2796 int trailer_len = strlen(trailer);
2798 data = xrealloc(data, len + trailer_len);
2799 memcpy(data + len, trailer, trailer_len);
2804 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2805 fprintf(stderr, "Error generating signature\n");
2811 b64encode(sig, sig, 64);
2814 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2815 fwrite(data, len, 1, stdout);
2821 static int cmd_verify(int argc, char *argv[]) {
2823 fprintf(stderr, "Not enough arguments!\n");
2828 fprintf(stderr, "Too many arguments!\n");
2832 char *node = argv[1];
2834 if(!strcmp(node, ".")) {
2836 name = get_my_name(true);
2844 } else if(!strcmp(node, "*")) {
2847 if(!check_id(node)) {
2848 fprintf(stderr, "Invalid node name\n");
2856 in = fopen(argv[2], "rb");
2859 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2867 char *data = readfile(in, &len);
2874 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2878 char *newline = memchr(data, '\n', len);
2880 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2881 fprintf(stderr, "Invalid input\n");
2887 size_t skip = newline - data;
2889 char signer[MAX_STRING_SIZE] = "";
2890 char sig[MAX_STRING_SIZE] = "";
2893 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2894 fprintf(stderr, "Invalid input\n");
2899 if(node && strcmp(node, signer)) {
2900 fprintf(stderr, "Signature is not made by %s\n", node);
2910 xasprintf(&trailer, " %s %ld", signer, t);
2911 int trailer_len = strlen(trailer);
2913 data = xrealloc(data, len + trailer_len);
2914 memcpy(data + len, trailer, trailer_len);
2917 newline = data + skip;
2919 char fname[PATH_MAX];
2920 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2921 FILE *fp = fopen(fname, "r");
2924 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2929 ecdsa_t *key = get_pubkey(fp);
2933 key = ecdsa_read_pem_public_key(fp);
2937 fprintf(stderr, "Could not read public key from %s\n", fname);
2945 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2946 fprintf(stderr, "Invalid signature\n");
2954 fwrite(newline, len - (newline - data), 1, stdout);
2960 static const struct {
2961 const char *command;
2962 int (*function)(int argc, char *argv[]);
2965 {"start", cmd_start},
2967 {"restart", cmd_restart},
2968 {"reload", cmd_reload},
2971 {"purge", cmd_purge},
2972 {"debug", cmd_debug},
2973 {"retry", cmd_retry},
2974 {"connect", cmd_connect},
2975 {"disconnect", cmd_disconnect},
2980 {"config", cmd_config, true},
2981 {"add", cmd_config},
2982 {"del", cmd_config},
2983 {"get", cmd_config},
2984 {"set", cmd_config},
2986 {"generate-keys", cmd_generate_keys},
2987 #ifndef DISABLE_LEGACY
2988 {"generate-rsa-keys", cmd_generate_rsa_keys},
2990 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2992 {"version", cmd_version},
2995 {"export", cmd_export},
2996 {"export-all", cmd_export_all},
2997 {"import", cmd_import},
2998 {"exchange", cmd_exchange},
2999 {"exchange-all", cmd_exchange_all},
3000 {"invite", cmd_invite},
3002 {"network", cmd_network},
3005 {"verify", cmd_verify},
3009 #ifdef HAVE_READLINE
3010 static char *complete_command(const char *text, int state) {
3019 while(commands[i].command) {
3020 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3021 return xstrdup(commands[i].command);
3030 static char *complete_dump(const char *text, int state) {
3031 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3041 if(!strncasecmp(matches[i], text, strlen(text))) {
3042 return xstrdup(matches[i]);
3051 static char *complete_config(const char *text, int state) {
3060 while(variables[i].name) {
3061 char *dot = strchr(text, '.');
3064 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3066 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3070 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3071 return xstrdup(variables[i].name);
3081 static char *complete_info(const char *text, int state) {
3087 if(!connect_tincd(false)) {
3091 // Check the list of nodes
3092 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3093 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3096 while(recvline(fd, line, sizeof(line))) {
3098 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3111 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3115 if(!strncmp(item, text, strlen(text))) {
3116 return xstrdup(strip_weight(item));
3123 static char *complete_nothing(const char *text, int state) {
3127 static char **completion(const char *text, int start, int end) {
3128 char **matches = NULL;
3131 matches = rl_completion_matches(text, complete_command);
3132 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3133 matches = rl_completion_matches(text, complete_dump);
3134 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3135 matches = rl_completion_matches(text, complete_config);
3136 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3137 matches = rl_completion_matches(text, complete_config);
3138 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3139 matches = rl_completion_matches(text, complete_config);
3140 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3141 matches = rl_completion_matches(text, complete_config);
3142 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3143 matches = rl_completion_matches(text, complete_info);
3150 static int cmd_shell(int argc, char *argv[]) {
3151 xasprintf(&prompt, "%s> ", identname);
3155 int maxargs = argc + 16;
3156 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3158 for(int i = 0; i < argc; i++) {
3162 #ifdef HAVE_READLINE
3163 rl_readline_name = "tinc";
3164 rl_completion_entry_function = complete_nothing;
3165 rl_attempted_completion_function = completion;
3166 rl_filename_completion_desired = 0;
3171 #ifdef HAVE_READLINE
3176 rl_basic_word_break_characters = "\t\n ";
3177 line = readline(prompt);
3180 copy = xstrdup(line);
3183 line = fgets(buf, sizeof(buf), stdin);
3189 fputs(prompt, stdout);
3192 line = fgets(buf, sizeof(buf), stdin);
3199 /* Ignore comments */
3208 char *p = line + strspn(line, " \t\n");
3209 char *next = strtok(p, " \t\n");
3212 if(nargc >= maxargs) {
3214 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3219 next = strtok(NULL, " \t\n");
3226 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3233 for(int i = 0; commands[i].command; i++) {
3234 if(!strcasecmp(nargv[argc], commands[i].command)) {
3235 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3241 #ifdef HAVE_READLINE
3250 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3265 int main(int argc, char *argv[]) {
3266 program_name = argv[0];
3269 tty = isatty(0) && isatty(1);
3271 if(!parse_options(argc, argv)) {
3276 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3277 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3290 static struct WSAData wsa_state;
3292 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3293 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3302 if(optind >= argc) {
3303 return cmd_shell(argc, argv);
3306 for(int i = 0; commands[i].command; i++) {
3307 if(!strcasecmp(argv[optind], commands[i].command)) {
3308 return commands[i].function(argc - optind, argv + optind);
3312 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);