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");
2444 editor = getenv("EDITOR");
2448 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2450 xasprintf(&command, "edit \"%s\"", filename);
2452 int result = system(command);
2459 // Silently try notifying a running tincd of changes.
2460 if(connect_tincd(false)) {
2461 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2467 static int export(const char *name, FILE *out) {
2468 char filename[PATH_MAX];
2469 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2470 FILE *in = fopen(filename, "r");
2473 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2477 fprintf(out, "Name = %s\n", name);
2480 while(fgets(buf, sizeof(buf), in)) {
2481 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2487 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2496 static int cmd_export(int argc, char *argv[]) {
2500 fprintf(stderr, "Too many arguments!\n");
2504 char *name = get_my_name(true);
2510 int result = export(name, stdout);
2520 static int cmd_export_all(int argc, char *argv[]) {
2524 fprintf(stderr, "Too many arguments!\n");
2528 DIR *dir = opendir(hosts_dir);
2531 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2539 while((ent = readdir(dir))) {
2540 if(!check_id(ent->d_name)) {
2547 printf("#---------------------------------------------------------------#\n");
2550 result |= export(ent->d_name, stdout);
2562 static int cmd_import(int argc, char *argv[]) {
2566 fprintf(stderr, "Too many arguments!\n");
2575 char filename[PATH_MAX] = "";
2577 bool firstline = true;
2579 while(fgets(buf, sizeof(buf), in)) {
2580 if(sscanf(buf, "Name = %4095s", name) == 1) {
2583 if(!check_id(name)) {
2584 fprintf(stderr, "Invalid Name in input!\n");
2592 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2593 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2597 if(!force && !access(filename, F_OK)) {
2598 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2603 out = fopen(filename, "w");
2606 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2612 } else if(firstline) {
2613 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2618 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2623 if(fputs(buf, out) < 0) {
2624 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2635 fprintf(stderr, "Imported %d host configuration files.\n", count);
2638 fprintf(stderr, "No host configuration files imported.\n");
2643 static int cmd_exchange(int argc, char *argv[]) {
2644 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2647 static int cmd_exchange_all(int argc, char *argv[]) {
2648 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2651 static int switch_network(char *name) {
2652 if(strcmp(name, ".")) {
2653 if(!check_netname(name, false)) {
2654 fprintf(stderr, "Invalid character in netname!\n");
2658 if(!check_netname(name, true)) {
2659 fprintf(stderr, "Warning: unsafe character in netname!\n");
2669 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2676 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2677 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2678 xasprintf(&prompt, "%s> ", identname);
2683 static int cmd_network(int argc, char *argv[]) {
2685 fprintf(stderr, "Too many arguments!\n");
2690 return switch_network(argv[1]);
2693 DIR *dir = opendir(confdir);
2696 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2702 while((ent = readdir(dir))) {
2703 if(*ent->d_name == '.') {
2707 if(!strcmp(ent->d_name, "tinc.conf")) {
2712 char fname[PATH_MAX];
2713 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2715 if(!access(fname, R_OK)) {
2716 printf("%s\n", ent->d_name);
2725 static int cmd_fsck(int argc, char *argv[]) {
2729 fprintf(stderr, "Too many arguments!\n");
2733 return fsck(orig_argv[0]);
2736 static void *readfile(FILE *in, size_t *len) {
2738 size_t bufsize = 4096;
2739 char *buf = xmalloc(bufsize);
2742 size_t read = fread(buf + count, 1, bufsize - count, in);
2750 if(count >= bufsize) {
2752 buf = xrealloc(buf, bufsize);
2763 static int cmd_sign(int argc, char *argv[]) {
2765 fprintf(stderr, "Too many arguments!\n");
2770 name = get_my_name(true);
2777 char fname[PATH_MAX];
2778 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2779 FILE *fp = fopen(fname, "r");
2782 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2786 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2789 fprintf(stderr, "Could not read private key from %s\n", fname);
2799 in = fopen(argv[1], "rb");
2802 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2811 char *data = readfile(in, &len);
2818 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2823 // Ensure we sign our name and current time as well
2824 long t = time(NULL);
2826 xasprintf(&trailer, " %s %ld", name, t);
2827 int trailer_len = strlen(trailer);
2829 data = xrealloc(data, len + trailer_len);
2830 memcpy(data + len, trailer, trailer_len);
2835 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2836 fprintf(stderr, "Error generating signature\n");
2842 b64encode(sig, sig, 64);
2845 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2846 fwrite(data, len, 1, stdout);
2852 static int cmd_verify(int argc, char *argv[]) {
2854 fprintf(stderr, "Not enough arguments!\n");
2859 fprintf(stderr, "Too many arguments!\n");
2863 char *node = argv[1];
2865 if(!strcmp(node, ".")) {
2867 name = get_my_name(true);
2875 } else if(!strcmp(node, "*")) {
2878 if(!check_id(node)) {
2879 fprintf(stderr, "Invalid node name\n");
2887 in = fopen(argv[2], "rb");
2890 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2898 char *data = readfile(in, &len);
2905 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2909 char *newline = memchr(data, '\n', len);
2911 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2912 fprintf(stderr, "Invalid input\n");
2918 size_t skip = newline - data;
2920 char signer[MAX_STRING_SIZE] = "";
2921 char sig[MAX_STRING_SIZE] = "";
2924 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2925 fprintf(stderr, "Invalid input\n");
2930 if(node && strcmp(node, signer)) {
2931 fprintf(stderr, "Signature is not made by %s\n", node);
2941 xasprintf(&trailer, " %s %ld", signer, t);
2942 int trailer_len = strlen(trailer);
2944 data = xrealloc(data, len + trailer_len);
2945 memcpy(data + len, trailer, trailer_len);
2948 newline = data + skip;
2950 char fname[PATH_MAX];
2951 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2952 FILE *fp = fopen(fname, "r");
2955 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2960 ecdsa_t *key = get_pubkey(fp);
2964 key = ecdsa_read_pem_public_key(fp);
2968 fprintf(stderr, "Could not read public key from %s\n", fname);
2976 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2977 fprintf(stderr, "Invalid signature\n");
2985 fwrite(newline, len - (newline - data), 1, stdout);
2991 static const struct {
2992 const char *command;
2993 int (*function)(int argc, char *argv[]);
2996 {"start", cmd_start, false},
2997 {"stop", cmd_stop, false},
2998 {"restart", cmd_restart, false},
2999 {"reload", cmd_reload, false},
3000 {"dump", cmd_dump, false},
3001 {"list", cmd_dump, false},
3002 {"purge", cmd_purge, false},
3003 {"debug", cmd_debug, false},
3004 {"retry", cmd_retry, false},
3005 {"connect", cmd_connect, false},
3006 {"disconnect", cmd_disconnect, false},
3007 {"top", cmd_top, false},
3008 {"pcap", cmd_pcap, false},
3009 {"log", cmd_log, false},
3010 {"pid", cmd_pid, false},
3011 {"config", cmd_config, true},
3012 {"add", cmd_config, false},
3013 {"del", cmd_config, false},
3014 {"get", cmd_config, false},
3015 {"set", cmd_config, false},
3016 {"init", cmd_init, false},
3017 {"generate-keys", cmd_generate_keys, false},
3018 #ifndef DISABLE_LEGACY
3019 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3021 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3022 {"help", cmd_help, false},
3023 {"version", cmd_version, false},
3024 {"info", cmd_info, false},
3025 {"edit", cmd_edit, false},
3026 {"export", cmd_export, false},
3027 {"export-all", cmd_export_all, false},
3028 {"import", cmd_import, false},
3029 {"exchange", cmd_exchange, false},
3030 {"exchange-all", cmd_exchange_all, false},
3031 {"invite", cmd_invite, false},
3032 {"join", cmd_join, false},
3033 {"network", cmd_network, false},
3034 {"fsck", cmd_fsck, false},
3035 {"sign", cmd_sign, false},
3036 {"verify", cmd_verify, false},
3037 {NULL, NULL, false},
3040 #ifdef HAVE_READLINE
3041 static char *complete_command(const char *text, int state) {
3050 while(commands[i].command) {
3051 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3052 return xstrdup(commands[i].command);
3061 static char *complete_dump(const char *text, int state) {
3062 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3072 if(!strncasecmp(matches[i], text, strlen(text))) {
3073 return xstrdup(matches[i]);
3082 static char *complete_config(const char *text, int state) {
3091 while(variables[i].name) {
3092 char *dot = strchr(text, '.');
3095 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3097 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3101 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3102 return xstrdup(variables[i].name);
3112 static char *complete_info(const char *text, int state) {
3118 if(!connect_tincd(false)) {
3122 // Check the list of nodes
3123 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3124 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3127 while(recvline(fd, line, sizeof(line))) {
3129 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3142 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3146 if(!strncmp(item, text, strlen(text))) {
3147 return xstrdup(strip_weight(item));
3154 static char *complete_nothing(const char *text, int state) {
3160 static char **completion(const char *text, int start, int end) {
3162 char **matches = NULL;
3165 matches = rl_completion_matches(text, complete_command);
3166 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3167 matches = rl_completion_matches(text, complete_dump);
3168 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3169 matches = rl_completion_matches(text, complete_config);
3170 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3171 matches = rl_completion_matches(text, complete_config);
3172 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3173 matches = rl_completion_matches(text, complete_config);
3174 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3175 matches = rl_completion_matches(text, complete_config);
3176 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3177 matches = rl_completion_matches(text, complete_info);
3184 static int cmd_shell(int argc, char *argv[]) {
3185 xasprintf(&prompt, "%s> ", identname);
3189 int maxargs = argc + 16;
3190 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3192 for(int i = 0; i < argc; i++) {
3196 #ifdef HAVE_READLINE
3197 rl_readline_name = "tinc";
3198 rl_completion_entry_function = complete_nothing;
3199 rl_attempted_completion_function = completion;
3200 rl_filename_completion_desired = 0;
3205 #ifdef HAVE_READLINE
3210 rl_basic_word_break_characters = "\t\n ";
3211 line = readline(prompt);
3212 copy = line ? xstrdup(line) : NULL;
3214 line = fgets(buf, sizeof(buf), stdin);
3220 fputs(prompt, stdout);
3223 line = fgets(buf, sizeof(buf), stdin);
3230 /* Ignore comments */
3239 char *p = line + strspn(line, " \t\n");
3240 char *next = strtok(p, " \t\n");
3243 if(nargc >= maxargs) {
3245 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3250 next = strtok(NULL, " \t\n");
3257 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3258 #ifdef HAVE_READLINE
3267 for(int i = 0; commands[i].command; i++) {
3268 if(!strcasecmp(nargv[argc], commands[i].command)) {
3269 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3275 #ifdef HAVE_READLINE
3284 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3289 #ifdef HAVE_READLINE
3302 int main(int argc, char *argv[]) {
3303 program_name = argv[0];
3306 tty = isatty(0) && isatty(1);
3308 if(!parse_options(argc, argv)) {
3313 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3314 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3327 static struct WSAData wsa_state;
3329 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3330 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3339 if(optind >= argc) {
3340 return cmd_shell(argc, argv);
3343 for(int i = 0; commands[i].command; i++) {
3344 if(!strcasecmp(argv[optind], commands[i].command)) {
3345 return commands[i].function(argc - optind, argv + optind);
3349 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);