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((size_t)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((size_t)(newline - buffer) >= len) {
584 len = newline - buffer;
586 memcpy(line, buffer, len);
588 memmove(buffer, newline + 1, blen - len - 1);
594 static bool recvdata(int fd, char *data, size_t len) {
596 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
598 if(result == -1 && sockerrno == EINTR) {
600 } else if(result <= 0) {
607 memcpy(data, buffer, len);
608 memmove(buffer, buffer + len, blen - len);
614 bool sendline(int fd, char *format, ...) {
615 static char buffer[4096];
620 va_start(ap, format);
621 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
622 buffer[sizeof(buffer) - 1] = 0;
625 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
633 int result = send(fd, p, blen, MSG_NOSIGNAL);
635 if(result == -1 && sockerrno == EINTR) {
637 } else if(result <= 0) {
648 static void pcap(int fd, FILE *out, uint32_t snaplen) {
649 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
657 uint32_t tz_accuracy;
664 snaplen ? snaplen : sizeof(data),
677 fwrite(&header, sizeof(header), 1, out);
682 while(recvline(fd, line, sizeof(line))) {
684 int n = sscanf(line, "%d %d %d", &code, &req, &len);
685 gettimeofday(&tv, NULL);
687 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || (size_t)len > sizeof(data)) {
691 if(!recvdata(fd, data, len)) {
695 packet.tv_sec = tv.tv_sec;
696 packet.tv_usec = tv.tv_usec;
698 packet.origlen = len;
699 fwrite(&packet, sizeof(packet), 1, out);
700 fwrite(data, len, 1, out);
705 static void logcontrol(int fd, FILE *out, int level) {
706 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
710 while(recvline(fd, line, sizeof(line))) {
712 int n = sscanf(line, "%d %d %d", &code, &req, &len);
714 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
718 if(!recvdata(fd, data, len)) {
722 fwrite(data, len, 1, out);
729 static bool remove_service(void) {
730 SC_HANDLE manager = NULL;
731 SC_HANDLE service = NULL;
732 SERVICE_STATUS status = {0};
733 bool success = false;
735 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
738 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
742 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
745 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
749 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
750 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
752 fprintf(stderr, "%s service stopped\n", identname);
755 if(!DeleteService(service)) {
756 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
765 CloseServiceHandle(service);
769 CloseServiceHandle(manager);
773 fprintf(stderr, "%s service removed\n", identname);
780 bool connect_tincd(bool verbose) {
785 struct timeval tv = {0, 0};
787 if(select(fd + 1, &r, NULL, NULL, &tv)) {
788 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
796 FILE *f = fopen(pidfilename, "r");
800 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
809 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
811 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
822 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
823 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
824 /* clean up the stale socket and pid file */
826 unlink(unixsocketname);
830 struct sockaddr_un sa;
832 sa.sun_family = AF_UNIX;
834 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
836 sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
838 fd = socket(AF_UNIX, SOCK_STREAM, 0);
842 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
848 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
850 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
859 struct addrinfo hints = {
860 .ai_family = AF_UNSPEC,
861 .ai_socktype = SOCK_STREAM,
862 .ai_protocol = IPPROTO_TCP,
866 struct addrinfo *res = NULL;
868 if(getaddrinfo(host, port, &hints, &res) || !res) {
870 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
876 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
880 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
887 unsigned long arg = 0;
889 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
891 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
897 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
899 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
911 static const int one = 1;
912 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
915 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
920 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
922 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
930 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
932 fprintf(stderr, "Could not fully establish control socket connection\n");
944 static int cmd_start(int argc, char *argv[]) {
945 if(connect_tincd(false)) {
947 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
949 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
956 char *slash = strrchr(program_name, '/');
960 if((c = strrchr(program_name, '\\')) > slash) {
967 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
973 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
978 Windows has no real concept of an "argv array". A command line is just one string.
979 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
980 it uses quotes to handle spaces in arguments.
981 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
982 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
983 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
985 xasprintf(&arg0, "\"%s\"", arg0);
987 nargv[nargc++] = arg0;
989 for(int i = 1; i < optind; i++) {
990 nargv[nargc++] = orig_argv[i];
993 for(int i = 1; i < argc; i++) {
994 nargv[nargc++] = argv[i];
998 int status = spawnvp(_P_WAIT, c, nargv);
1001 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
1007 int pfd[2] = {-1, -1};
1009 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
1010 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
1018 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1026 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1027 setenv("TINC_UMBILICAL", buf, true);
1028 exit(execvp(c, nargv));
1035 int status = -1, result;
1037 signal(SIGINT, SIG_IGN);
1040 // Pass all log messages from the umbilical to stderr.
1041 // A nul-byte right before closure means tincd started successfully.
1042 bool failure = true;
1046 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1047 failure = buf[len - 1];
1062 // Make sure the child process is really gone.
1063 result = waitpid(pid, &status, 0);
1066 signal(SIGINT, SIG_DFL);
1069 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1070 fprintf(stderr, "Error starting %s\n", c);
1078 static int cmd_stop(int argc, char *argv[]) {
1082 fprintf(stderr, "Too many arguments!\n");
1088 if(!connect_tincd(true)) {
1090 if(kill(pid, SIGTERM)) {
1091 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1095 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1096 waitpid(pid, NULL, 0);
1103 sendline(fd, "%d %d", CONTROL, REQ_STOP);
1105 while(recvline(fd, line, sizeof(line))) {
1106 // Wait for tincd to close the connection...
1111 if(!remove_service()) {
1123 static int cmd_restart(int argc, char *argv[]) {
1125 return cmd_start(argc, argv);
1128 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((size_t)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[]) {
1427 fprintf(stderr, "Too many arguments!\n");
1431 if(!connect_tincd(true)) {
1435 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1437 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1438 fprintf(stderr, "Could not purge old information.\n");
1445 static int cmd_debug(int argc, char *argv[]) {
1447 fprintf(stderr, "Invalid number of arguments.\n");
1451 if(!connect_tincd(true)) {
1455 int debuglevel = atoi(argv[1]);
1458 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1460 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1461 fprintf(stderr, "Could not set debug level.\n");
1465 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1469 static int cmd_retry(int argc, char *argv[]) {
1473 fprintf(stderr, "Too many arguments!\n");
1477 if(!connect_tincd(true)) {
1481 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1483 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1484 fprintf(stderr, "Could not retry outgoing connections.\n");
1491 static int cmd_connect(int argc, char *argv[]) {
1493 fprintf(stderr, "Invalid number of arguments.\n");
1497 if(!check_id(argv[1])) {
1498 fprintf(stderr, "Invalid name for node.\n");
1502 if(!connect_tincd(true)) {
1506 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1508 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1509 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1516 static int cmd_disconnect(int argc, char *argv[]) {
1518 fprintf(stderr, "Invalid number of arguments.\n");
1522 if(!check_id(argv[1])) {
1523 fprintf(stderr, "Invalid name for node.\n");
1527 if(!connect_tincd(true)) {
1531 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1533 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1534 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1541 static int cmd_top(int argc, char *argv[]) {
1545 fprintf(stderr, "Too many arguments!\n");
1551 if(!connect_tincd(true)) {
1558 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1563 static int cmd_pcap(int argc, char *argv[]) {
1565 fprintf(stderr, "Too many arguments!\n");
1569 if(!connect_tincd(true)) {
1573 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1578 static void sigint_handler(int sig) {
1581 fprintf(stderr, "\n");
1582 shutdown(fd, SHUT_RDWR);
1586 static int cmd_log(int argc, char *argv[]) {
1588 fprintf(stderr, "Too many arguments!\n");
1592 if(!connect_tincd(true)) {
1597 signal(SIGINT, sigint_handler);
1600 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1603 signal(SIGINT, SIG_DFL);
1611 static int cmd_pid(int argc, char *argv[]) {
1615 fprintf(stderr, "Too many arguments!\n");
1619 if(!connect_tincd(true) || !pid) {
1623 printf("%d\n", pid);
1627 int rstrip(char *value) {
1628 int len = strlen(value);
1630 while(len && strchr("\t\r\n ", value[len - 1])) {
1637 char *get_my_name(bool verbose) {
1638 FILE *f = fopen(tinc_conf, "r");
1642 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1651 while(fgets(buf, sizeof(buf), f)) {
1652 int len = strcspn(buf, "\t =");
1654 value += strspn(value, "\t ");
1658 value += strspn(value, "\t ");
1661 if(!rstrip(value)) {
1667 if(strcasecmp(buf, "Name")) {
1673 return replace_name(value);
1680 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1686 ecdsa_t *get_pubkey(FILE *f) {
1690 while(fgets(buf, sizeof(buf), f)) {
1691 int len = strcspn(buf, "\t =");
1693 value += strspn(value, "\t ");
1697 value += strspn(value, "\t ");
1700 if(!rstrip(value)) {
1706 if(strcasecmp(buf, "Ed25519PublicKey")) {
1711 return ecdsa_set_base64_public_key(value);
1718 const var_t variables[] = {
1719 /* Server configuration */
1720 {"AddressFamily", VAR_SERVER},
1721 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1722 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1723 {"BindToInterface", VAR_SERVER},
1724 {"Broadcast", VAR_SERVER | VAR_SAFE},
1725 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1726 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1727 {"DecrementTTL", VAR_SERVER},
1728 {"Device", VAR_SERVER},
1729 {"DeviceStandby", VAR_SERVER},
1730 {"DeviceType", VAR_SERVER},
1731 {"DirectOnly", VAR_SERVER},
1732 {"Ed25519PrivateKeyFile", VAR_SERVER},
1733 {"ExperimentalProtocol", VAR_SERVER},
1734 {"Forwarding", VAR_SERVER},
1735 {"FWMark", VAR_SERVER},
1736 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1737 {"Hostnames", VAR_SERVER},
1738 {"IffOneQueue", VAR_SERVER},
1739 {"Interface", VAR_SERVER},
1740 {"InvitationExpire", VAR_SERVER},
1741 {"KeyExpire", VAR_SERVER},
1742 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1743 {"LocalDiscovery", VAR_SERVER},
1744 {"LogLevel", VAR_SERVER},
1745 {"MACExpire", VAR_SERVER},
1746 {"MaxConnectionBurst", VAR_SERVER},
1747 {"MaxOutputBufferSize", VAR_SERVER},
1748 {"MaxTimeout", VAR_SERVER},
1749 {"Mode", VAR_SERVER | VAR_SAFE},
1750 {"Name", VAR_SERVER},
1751 {"PingInterval", VAR_SERVER},
1752 {"PingTimeout", VAR_SERVER},
1753 {"PriorityInheritance", VAR_SERVER},
1754 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1755 {"PrivateKeyFile", VAR_SERVER},
1756 {"ProcessPriority", VAR_SERVER},
1757 {"Proxy", VAR_SERVER},
1758 {"ReplayWindow", VAR_SERVER},
1759 {"ScriptsExtension", VAR_SERVER},
1760 {"ScriptsInterpreter", VAR_SERVER},
1761 {"StrictSubnets", VAR_SERVER},
1762 {"TunnelServer", VAR_SERVER},
1763 {"UDPDiscovery", VAR_SERVER},
1764 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1765 {"UDPDiscoveryInterval", VAR_SERVER},
1766 {"UDPDiscoveryTimeout", VAR_SERVER},
1767 {"MTUInfoInterval", VAR_SERVER},
1768 {"UDPInfoInterval", VAR_SERVER},
1769 {"UDPRcvBuf", VAR_SERVER},
1770 {"UDPSndBuf", VAR_SERVER},
1771 {"UPnP", VAR_SERVER},
1772 {"UPnPDiscoverWait", VAR_SERVER},
1773 {"UPnPRefreshPeriod", VAR_SERVER},
1774 {"VDEGroup", VAR_SERVER},
1775 {"VDEPort", VAR_SERVER},
1776 /* Host configuration */
1777 {"Address", VAR_HOST | VAR_MULTIPLE},
1778 {"Cipher", VAR_SERVER | VAR_HOST},
1779 {"ClampMSS", VAR_SERVER | VAR_HOST},
1780 {"Compression", VAR_SERVER | VAR_HOST},
1781 {"Digest", VAR_SERVER | VAR_HOST},
1782 {"Ed25519PublicKey", VAR_HOST},
1783 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1784 {"IndirectData", VAR_SERVER | VAR_HOST},
1785 {"MACLength", VAR_SERVER | VAR_HOST},
1786 {"PMTU", VAR_SERVER | VAR_HOST},
1787 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1789 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1790 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1791 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1792 {"TCPOnly", VAR_SERVER | VAR_HOST},
1793 {"Weight", VAR_HOST | VAR_SAFE},
1797 static int cmd_config(int argc, char *argv[]) {
1799 fprintf(stderr, "Invalid number of arguments.\n");
1803 if(strcasecmp(argv[0], "config")) {
1809 if(!strcasecmp(argv[1], "get")) {
1811 } else if(!strcasecmp(argv[1], "add")) {
1812 argv++, argc--, action = 1;
1813 } else if(!strcasecmp(argv[1], "del")) {
1814 argv++, argc--, action = -1;
1815 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1816 argv++, argc--, action = 0;
1820 fprintf(stderr, "Invalid number of arguments.\n");
1824 // Concatenate the rest of the command line
1825 strncpy(line, argv[1], sizeof(line) - 1);
1827 for(int i = 2; i < argc; i++) {
1828 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1829 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1832 // Liberal parsing into node name, variable name and value.
1838 len = strcspn(line, "\t =");
1840 value += strspn(value, "\t ");
1844 value += strspn(value, "\t ");
1848 variable = strchr(line, '.');
1858 fprintf(stderr, "No variable given.\n");
1862 if(action >= 0 && !*value) {
1863 fprintf(stderr, "No value for variable given.\n");
1867 if(action < -1 && *value) {
1871 /* Some simple checks. */
1873 bool warnonremove = false;
1875 for(int i = 0; variables[i].name; i++) {
1876 if(strcasecmp(variables[i].name, variable)) {
1881 variable = (char *)variables[i].name;
1883 /* Discourage use of obsolete variables. */
1885 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1887 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1889 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1894 /* Don't put server variables in host config files */
1896 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1898 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1900 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1905 /* Should this go into our own host config file? */
1907 if(!node && !(variables[i].type & VAR_SERVER)) {
1908 node = get_my_name(true);
1915 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1916 Turn on warnings when it seems variables might be removed unintentionally. */
1918 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1919 warnonremove = true;
1921 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1922 warnonremove = true;
1928 if(node && !check_id(node)) {
1929 fprintf(stderr, "Invalid name for node.\n");
1934 if(force || action < 0) {
1935 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1937 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1942 // Open the right configuration file.
1943 char filename[PATH_MAX];
1946 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1948 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1951 FILE *f = fopen(filename, "r");
1954 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1958 char tmpfile[PATH_MAX];
1962 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1963 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1967 tf = fopen(tmpfile, "w");
1970 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1976 // Copy the file, making modifications on the fly, unless we are just getting a value.
1980 bool removed = false;
1983 while(fgets(buf1, sizeof(buf1), f)) {
1984 buf1[sizeof(buf1) - 1] = 0;
1985 strncpy(buf2, buf1, sizeof(buf2));
1987 // Parse line in a simple way
1991 len = strcspn(buf2, "\t =");
1992 bvalue = buf2 + len;
1993 bvalue += strspn(bvalue, "\t ");
1995 if(*bvalue == '=') {
1997 bvalue += strspn(bvalue, "\t ");
2004 if(!strcasecmp(buf2, variable)) {
2008 printf("%s\n", bvalue);
2010 } else if(action == -1) {
2011 if(!*value || !strcasecmp(bvalue, value)) {
2017 } else if(action == 0) {
2018 // Warn if "set" was used for variables that can occur multiple times
2019 if(warnonremove && strcasecmp(bvalue, value)) {
2020 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2023 // Already set? Delete the rest...
2028 // Otherwise, replace.
2029 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2030 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2037 } else if(action > 0) {
2038 // Check if we've already seen this variable with the same value
2039 if(!strcasecmp(bvalue, value)) {
2046 // Copy original line...
2047 if(fputs(buf1, tf) < 0) {
2048 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2052 // Add newline if it is missing...
2053 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2054 if(fputc('\n', tf) < 0) {
2055 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2062 // Make sure we read everything...
2063 if(ferror(f) || !feof(f)) {
2064 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2069 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2073 // Add new variable if necessary.
2074 if((action > 0 && !found) || (action == 0 && !set)) {
2075 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2076 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2085 fprintf(stderr, "No matching configuration variables found.\n");
2090 // Make sure we wrote everything...
2092 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2096 // Could we find what we had to remove?
2097 if(action < 0 && !removed) {
2099 fprintf(stderr, "No configuration variables deleted.\n");
2103 // Replace the configuration file with the new one
2106 if(remove(filename)) {
2107 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2113 if(rename(tmpfile, filename)) {
2114 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2118 // Silently try notifying a running tincd of changes.
2119 if(connect_tincd(false)) {
2120 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2126 static bool try_bind(int port) {
2127 struct addrinfo *ai = NULL, *aip;
2128 struct addrinfo hint = {
2129 .ai_flags = AI_PASSIVE,
2130 .ai_family = AF_UNSPEC,
2131 .ai_socktype = SOCK_STREAM,
2132 .ai_protocol = IPPROTO_TCP,
2135 bool success = true;
2137 snprintf(portstr, sizeof(portstr), "%d", port);
2139 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2143 for(aip = ai; aip; aip = aip->ai_next) {
2144 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2151 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2164 int check_port(const char *name) {
2169 fprintf(stderr, "Warning: could not bind to port 655. ");
2171 for(int i = 0; i < 100; i++) {
2172 int port = 0x1000 + (rand() & 0x7fff);
2174 if(try_bind(port)) {
2175 char filename[PATH_MAX];
2176 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2177 FILE *f = fopen(filename, "a");
2180 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2181 fprintf(stderr, "Please change tinc's Port manually.\n");
2185 fprintf(f, "Port = %d\n", port);
2187 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2192 fprintf(stderr, "Please change tinc's Port manually.\n");
2196 static int cmd_init(int argc, char *argv[]) {
2197 if(!access(tinc_conf, F_OK)) {
2198 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2203 fprintf(stderr, "Too many arguments!\n");
2205 } else if(argc < 2) {
2208 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2210 if(!fgets(buf, sizeof(buf), stdin)) {
2211 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2215 int len = rstrip(buf);
2218 fprintf(stderr, "No name given!\n");
2224 fprintf(stderr, "No Name given!\n");
2228 name = strdup(argv[1]);
2231 fprintf(stderr, "No Name given!\n");
2236 if(!check_id(name)) {
2237 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2241 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2242 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2246 if(mkdir(confbase, 0777) && errno != EEXIST) {
2247 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2251 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2252 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2256 FILE *f = fopen(tinc_conf, "w");
2259 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2263 fprintf(f, "Name = %s\n", name);
2266 #ifndef DISABLE_LEGACY
2268 if(!rsa_keygen(2048, false)) {
2274 if(!ed25519_keygen(false)) {
2281 char filename[PATH_MAX];
2282 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2284 if(access(filename, F_OK)) {
2285 FILE *f = fopenmask(filename, "w", 0777);
2288 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2292 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");
2302 static int cmd_generate_keys(int argc, char *argv[]) {
2303 #ifdef DISABLE_LEGACY
2310 fprintf(stderr, "Too many arguments!\n");
2315 name = get_my_name(false);
2318 #ifndef DISABLE_LEGACY
2320 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2326 if(!ed25519_keygen(true)) {
2333 #ifndef DISABLE_LEGACY
2334 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2336 fprintf(stderr, "Too many arguments!\n");
2341 name = get_my_name(false);
2344 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2348 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2352 fprintf(stderr, "Too many arguments!\n");
2357 name = get_my_name(false);
2360 return !ed25519_keygen(true);
2363 static int cmd_help(int argc, char *argv[]) {
2371 static int cmd_version(int argc, char *argv[]) {
2375 fprintf(stderr, "Too many arguments!\n");
2383 static int cmd_info(int argc, char *argv[]) {
2385 fprintf(stderr, "Invalid number of arguments.\n");
2389 if(!connect_tincd(true)) {
2393 return info(fd, argv[1]);
2396 static const char *conffiles[] = {
2407 static int cmd_edit(int argc, char *argv[]) {
2409 fprintf(stderr, "Invalid number of arguments.\n");
2413 char filename[PATH_MAX] = "";
2415 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2416 for(int i = 0; conffiles[i]; i++) {
2417 if(!strcmp(argv[1], conffiles[i])) {
2418 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2427 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2428 char *dash = strchr(argv[1], '-');
2433 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2434 fprintf(stderr, "Invalid configuration filename.\n");
2442 const char *editor = getenv("VISUAL");
2445 editor = getenv("EDITOR");
2452 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2454 xasprintf(&command, "edit \"%s\"", filename);
2456 int result = system(command);
2463 // Silently try notifying a running tincd of changes.
2464 if(connect_tincd(false)) {
2465 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2471 static int export(const char *name, FILE *out) {
2472 char filename[PATH_MAX];
2473 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2474 FILE *in = fopen(filename, "r");
2477 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2481 fprintf(out, "Name = %s\n", name);
2484 while(fgets(buf, sizeof(buf), in)) {
2485 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2491 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2500 static int cmd_export(int argc, char *argv[]) {
2504 fprintf(stderr, "Too many arguments!\n");
2508 char *name = get_my_name(true);
2514 int result = export(name, stdout);
2524 static int cmd_export_all(int argc, char *argv[]) {
2528 fprintf(stderr, "Too many arguments!\n");
2532 DIR *dir = opendir(hosts_dir);
2535 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2543 while((ent = readdir(dir))) {
2544 if(!check_id(ent->d_name)) {
2551 printf("#---------------------------------------------------------------#\n");
2554 result |= export(ent->d_name, stdout);
2566 static int cmd_import(int argc, char *argv[]) {
2570 fprintf(stderr, "Too many arguments!\n");
2579 char filename[PATH_MAX] = "";
2581 bool firstline = true;
2583 while(fgets(buf, sizeof(buf), in)) {
2584 if(sscanf(buf, "Name = %4095s", name) == 1) {
2587 if(!check_id(name)) {
2588 fprintf(stderr, "Invalid Name in input!\n");
2596 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2597 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2601 if(!force && !access(filename, F_OK)) {
2602 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2607 out = fopen(filename, "w");
2610 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2616 } else if(firstline) {
2617 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2622 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2627 if(fputs(buf, out) < 0) {
2628 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2639 fprintf(stderr, "Imported %d host configuration files.\n", count);
2642 fprintf(stderr, "No host configuration files imported.\n");
2647 static int cmd_exchange(int argc, char *argv[]) {
2648 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2651 static int cmd_exchange_all(int argc, char *argv[]) {
2652 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2655 static int switch_network(char *name) {
2656 if(strcmp(name, ".")) {
2657 if(!check_netname(name, false)) {
2658 fprintf(stderr, "Invalid character in netname!\n");
2662 if(!check_netname(name, true)) {
2663 fprintf(stderr, "Warning: unsafe character in netname!\n");
2673 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2680 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2681 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2682 xasprintf(&prompt, "%s> ", identname);
2687 static int cmd_network(int argc, char *argv[]) {
2689 fprintf(stderr, "Too many arguments!\n");
2694 return switch_network(argv[1]);
2697 DIR *dir = opendir(confdir);
2700 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2706 while((ent = readdir(dir))) {
2707 if(*ent->d_name == '.') {
2711 if(!strcmp(ent->d_name, "tinc.conf")) {
2716 char fname[PATH_MAX];
2717 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2719 if(!access(fname, R_OK)) {
2720 printf("%s\n", ent->d_name);
2729 static int cmd_fsck(int argc, char *argv[]) {
2733 fprintf(stderr, "Too many arguments!\n");
2737 return fsck(orig_argv[0]);
2740 static void *readfile(FILE *in, size_t *len) {
2742 size_t bufsize = 4096;
2743 char *buf = xmalloc(bufsize);
2746 size_t read = fread(buf + count, 1, bufsize - count, in);
2754 if(count >= bufsize) {
2756 buf = xrealloc(buf, bufsize);
2767 static int cmd_sign(int argc, char *argv[]) {
2769 fprintf(stderr, "Too many arguments!\n");
2774 name = get_my_name(true);
2781 char fname[PATH_MAX];
2782 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2783 FILE *fp = fopen(fname, "r");
2786 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2790 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2793 fprintf(stderr, "Could not read private key from %s\n", fname);
2803 in = fopen(argv[1], "rb");
2806 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2815 char *data = readfile(in, &len);
2822 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2827 // Ensure we sign our name and current time as well
2828 long t = time(NULL);
2830 xasprintf(&trailer, " %s %ld", name, t);
2831 int trailer_len = strlen(trailer);
2833 data = xrealloc(data, len + trailer_len);
2834 memcpy(data + len, trailer, trailer_len);
2839 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2840 fprintf(stderr, "Error generating signature\n");
2846 b64encode(sig, sig, 64);
2849 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2850 fwrite(data, len, 1, stdout);
2856 static int cmd_verify(int argc, char *argv[]) {
2858 fprintf(stderr, "Not enough arguments!\n");
2863 fprintf(stderr, "Too many arguments!\n");
2867 char *node = argv[1];
2869 if(!strcmp(node, ".")) {
2871 name = get_my_name(true);
2879 } else if(!strcmp(node, "*")) {
2882 if(!check_id(node)) {
2883 fprintf(stderr, "Invalid node name\n");
2891 in = fopen(argv[2], "rb");
2894 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2902 char *data = readfile(in, &len);
2909 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2913 char *newline = memchr(data, '\n', len);
2915 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2916 fprintf(stderr, "Invalid input\n");
2922 size_t skip = newline - data;
2924 char signer[MAX_STRING_SIZE] = "";
2925 char sig[MAX_STRING_SIZE] = "";
2928 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2929 fprintf(stderr, "Invalid input\n");
2934 if(node && strcmp(node, signer)) {
2935 fprintf(stderr, "Signature is not made by %s\n", node);
2945 xasprintf(&trailer, " %s %ld", signer, t);
2946 int trailer_len = strlen(trailer);
2948 data = xrealloc(data, len + trailer_len);
2949 memcpy(data + len, trailer, trailer_len);
2952 newline = data + skip;
2954 char fname[PATH_MAX];
2955 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2956 FILE *fp = fopen(fname, "r");
2959 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2964 ecdsa_t *key = get_pubkey(fp);
2968 key = ecdsa_read_pem_public_key(fp);
2972 fprintf(stderr, "Could not read public key from %s\n", fname);
2980 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2981 fprintf(stderr, "Invalid signature\n");
2989 fwrite(newline, len - (newline - data), 1, stdout);
2995 static const struct {
2996 const char *command;
2997 int (*function)(int argc, char *argv[]);
3000 {"start", cmd_start, false},
3001 {"stop", cmd_stop, false},
3002 {"restart", cmd_restart, false},
3003 {"reload", cmd_reload, false},
3004 {"dump", cmd_dump, false},
3005 {"list", cmd_dump, false},
3006 {"purge", cmd_purge, false},
3007 {"debug", cmd_debug, false},
3008 {"retry", cmd_retry, false},
3009 {"connect", cmd_connect, false},
3010 {"disconnect", cmd_disconnect, false},
3011 {"top", cmd_top, false},
3012 {"pcap", cmd_pcap, false},
3013 {"log", cmd_log, false},
3014 {"pid", cmd_pid, false},
3015 {"config", cmd_config, true},
3016 {"add", cmd_config, false},
3017 {"del", cmd_config, false},
3018 {"get", cmd_config, false},
3019 {"set", cmd_config, false},
3020 {"init", cmd_init, false},
3021 {"generate-keys", cmd_generate_keys, false},
3022 #ifndef DISABLE_LEGACY
3023 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3025 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3026 {"help", cmd_help, false},
3027 {"version", cmd_version, false},
3028 {"info", cmd_info, false},
3029 {"edit", cmd_edit, false},
3030 {"export", cmd_export, false},
3031 {"export-all", cmd_export_all, false},
3032 {"import", cmd_import, false},
3033 {"exchange", cmd_exchange, false},
3034 {"exchange-all", cmd_exchange_all, false},
3035 {"invite", cmd_invite, false},
3036 {"join", cmd_join, false},
3037 {"network", cmd_network, false},
3038 {"fsck", cmd_fsck, false},
3039 {"sign", cmd_sign, false},
3040 {"verify", cmd_verify, false},
3041 {NULL, NULL, false},
3044 #ifdef HAVE_READLINE
3045 static char *complete_command(const char *text, int state) {
3054 while(commands[i].command) {
3055 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3056 return xstrdup(commands[i].command);
3065 static char *complete_dump(const char *text, int state) {
3066 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3076 if(!strncasecmp(matches[i], text, strlen(text))) {
3077 return xstrdup(matches[i]);
3086 static char *complete_config(const char *text, int state) {
3095 while(variables[i].name) {
3096 char *dot = strchr(text, '.');
3099 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3101 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3105 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3106 return xstrdup(variables[i].name);
3116 static char *complete_info(const char *text, int state) {
3122 if(!connect_tincd(false)) {
3126 // Check the list of nodes
3127 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3128 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3131 while(recvline(fd, line, sizeof(line))) {
3133 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3146 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3150 if(!strncmp(item, text, strlen(text))) {
3151 return xstrdup(strip_weight(item));
3158 static char *complete_nothing(const char *text, int state) {
3164 static char **completion(const char *text, int start, int end) {
3166 char **matches = NULL;
3169 matches = rl_completion_matches(text, complete_command);
3170 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3171 matches = rl_completion_matches(text, complete_dump);
3172 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3173 matches = rl_completion_matches(text, complete_config);
3174 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3175 matches = rl_completion_matches(text, complete_config);
3176 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3177 matches = rl_completion_matches(text, complete_config);
3178 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3179 matches = rl_completion_matches(text, complete_config);
3180 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3181 matches = rl_completion_matches(text, complete_info);
3188 static int cmd_shell(int argc, char *argv[]) {
3189 xasprintf(&prompt, "%s> ", identname);
3193 int maxargs = argc + 16;
3194 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3196 for(int i = 0; i < argc; i++) {
3200 #ifdef HAVE_READLINE
3201 rl_readline_name = "tinc";
3202 rl_completion_entry_function = complete_nothing;
3203 rl_attempted_completion_function = completion;
3204 rl_filename_completion_desired = 0;
3209 #ifdef HAVE_READLINE
3214 rl_basic_word_break_characters = "\t\n ";
3215 line = readline(prompt);
3216 copy = line ? xstrdup(line) : NULL;
3218 line = fgets(buf, sizeof(buf), stdin);
3224 fputs(prompt, stdout);
3227 line = fgets(buf, sizeof(buf), stdin);
3234 /* Ignore comments */
3243 char *p = line + strspn(line, " \t\n");
3244 char *next = strtok(p, " \t\n");
3247 if(nargc >= maxargs) {
3249 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3254 next = strtok(NULL, " \t\n");
3261 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3262 #ifdef HAVE_READLINE
3271 for(int i = 0; commands[i].command; i++) {
3272 if(!strcasecmp(nargv[argc], commands[i].command)) {
3273 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3279 #ifdef HAVE_READLINE
3288 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3293 #ifdef HAVE_READLINE
3306 int main(int argc, char *argv[]) {
3307 program_name = argv[0];
3310 tty = isatty(0) && isatty(1);
3312 if(!parse_options(argc, argv)) {
3317 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3318 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3331 static struct WSAData wsa_state;
3333 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3334 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3343 if(optind >= argc) {
3344 return cmd_shell(argc, argv);
3347 for(int i = 0; commands[i].command; i++) {
3348 if(!strcasecmp(argv[optind], commands[i].command)) {
3349 return commands[i].function(argc - optind, argv + optind);
3353 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);