2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2022 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.
23 #include "readline/readline.h"
24 #include "readline/history.h"
29 #include "control_common.h"
34 #include "invitation.h"
48 #define MSG_NOSIGNAL 0
51 static char **orig_argv;
53 /* If nonzero, display usage information and exit. */
54 static bool show_help = false;
56 /* If nonzero, print the version on standard output and exit. */
57 static bool show_version = false;
59 static char *name = NULL;
60 static char controlcookie[1025];
61 char *tinc_conf = NULL;
62 char *hosts_dir = NULL;
65 // Horrible global variables...
74 bool confbasegiven = false;
75 char *scriptinterpreter = NULL;
76 static char defaultextension[] = "";
77 char *scriptextension = defaultextension;
83 typedef enum option_t {
89 OPT_CONFIG_FILE = 'c',
99 static struct option const long_options[] = {
100 {"batch", no_argument, NULL, OPT_BATCH},
101 {"config", required_argument, NULL, OPT_CONFIG_FILE},
102 {"net", required_argument, NULL, OPT_NETNAME},
103 {"help", no_argument, NULL, OPT_HELP},
104 {"version", no_argument, NULL, OPT_VERSION},
105 {"pidfile", required_argument, NULL, OPT_PIDFILE},
106 {"force", no_argument, NULL, OPT_FORCE},
110 static void version(void) {
111 static const char *message =
112 "%s version %s (built %s %s, protocol %d.%d)\n"
120 #ifndef DISABLE_LEGACY
124 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
125 "See the AUTHORS file for a complete list.\n"
127 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
128 "and you are welcome to redistribute it under certain conditions;\n"
129 "see the file COPYING for details.\n";
131 printf(message, PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
134 static void usage(bool status) {
136 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
138 static const char *message =
139 "Usage: %s [options] command\n"
141 "Valid options are:\n"
142 " -b, --batch Don't ask for anything (non-interactive mode).\n"
143 " -c, --config=DIR Read configuration options from DIR.\n"
144 " -n, --net=NETNAME Connect to net NETNAME.\n"
145 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
146 " --force Force some commands to work despite warnings.\n"
147 " --help Display this help and exit.\n"
148 " --version Output version information and exit.\n"
150 "Valid commands are:\n"
151 " init [name] Create initial configuration files.\n"
152 " get VARIABLE Print current value of VARIABLE\n"
153 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
154 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
155 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
156 " start [tincd options] Start tincd.\n"
157 " stop Stop tincd.\n"
158 " restart [tincd options] Restart tincd.\n"
159 " reload Partially reload configuration of running tincd.\n"
160 " pid Show PID of currently running tincd.\n"
161 #ifdef DISABLE_LEGACY
162 " generate-keys Generate a new Ed25519 public/private key pair.\n"
164 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
165 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
167 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
168 " dump Dump a list of one of the following things:\n"
169 " [reachable] nodes - all known nodes in the VPN\n"
170 " edges - all known connections in the VPN\n"
171 " subnets - all known subnets in the VPN\n"
172 " connections - all meta connections with ourself\n"
173 " [di]graph - graph of the VPN in dotty format\n"
174 " invitations - outstanding invitations\n"
175 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
176 " purge Purge unreachable nodes\n"
177 " debug N Set debug level\n"
178 " retry Retry all outgoing connections\n"
179 " disconnect NODE Close meta connection with NODE\n"
181 " top Show real-time statistics\n"
183 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
184 " log [level] Dump log output [up to the specified level]\n"
185 " export Export host configuration of local node to standard output\n"
186 " export-all Export all host configuration files to standard output\n"
187 " import Import host configuration file(s) from standard input\n"
188 " exchange Same as export followed by import\n"
189 " exchange-all Same as export-all followed by import\n"
190 " invite NODE [...] Generate an invitation for NODE\n"
191 " join INVITATION Join a VPN using an INVITATION\n"
192 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
193 " fsck Check the configuration files for problems.\n"
194 " sign [FILE] Generate a signed version of a file.\n"
195 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
197 "Report bugs to tinc@tinc-vpn.org.\n";
199 printf(message, program_name);
203 static bool parse_options(int argc, char **argv) {
205 int option_index = 0;
207 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
208 switch((option_t) r) {
209 case OPT_LONG_OPTION:
221 case OPT_CONFIG_FILE:
223 confbase = xstrdup(optarg);
224 confbasegiven = true;
229 netname = xstrdup(optarg);
242 pidfilename = xstrdup(optarg);
254 if(!netname && (netname = getenv("NETNAME"))) {
255 netname = xstrdup(netname);
258 /* netname "." is special: a "top-level name" */
260 if(netname && (!*netname || !strcmp(netname, "."))) {
265 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
266 fprintf(stderr, "Invalid character in netname!\n");
274 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
276 char directory[PATH_MAX] = ".";
282 /* Check stdin and stdout */
284 /* Ask for a file and/or directory name. */
285 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
287 if(fgets(buf, sizeof(buf), stdin) == NULL) {
288 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
292 size_t len = strlen(buf);
305 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
308 if(filename[0] != '/') {
311 /* The directory is a relative path or a filename. */
312 if(!getcwd(directory, sizeof(directory))) {
313 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
317 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
318 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
330 disable_old_keys(filename, what);
332 /* Open it first to keep the inode busy */
334 r = fopenmask(filename, mode, perms);
337 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
345 Generate a public/private Ed25519 key pair, and ask for a file to store
348 static bool ed25519_keygen(bool ask) {
351 char fname[PATH_MAX];
353 fprintf(stderr, "Generating Ed25519 key pair:\n");
355 if(!(key = ecdsa_generate())) {
356 fprintf(stderr, "Error during key generation!\n");
359 fprintf(stderr, "Done.\n");
362 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
363 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
369 if(!ecdsa_write_pem_private_key(key, f)) {
370 fprintf(stderr, "Error writing private key!\n");
377 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
379 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
382 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
388 char *pubkey = ecdsa_get_base64_public_key(key);
389 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
407 #ifndef DISABLE_LEGACY
409 Generate a public/private RSA key pair, and ask for a file to store
412 static bool rsa_keygen(int bits, bool ask) {
415 char fname[PATH_MAX];
417 // Make sure the key size is a multiple of 8 bits.
420 // Make sure that a valid key size is used.
421 if(bits < 1024 || bits > 8192) {
422 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
424 } else if(bits < 2048) {
425 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
428 fprintf(stderr, "Generating %d bits keys:\n", bits);
430 if(!(key = rsa_generate(bits, 0x10001))) {
431 fprintf(stderr, "Error during key generation!\n");
434 fprintf(stderr, "Done.\n");
437 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
438 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
444 if(!rsa_write_pem_private_key(key, f)) {
445 fprintf(stderr, "Error writing private key!\n");
452 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
454 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
457 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
463 if(!rsa_write_pem_public_key(key, f)) {
464 fprintf(stderr, "Error writing public key!\n");
487 bool recvline(int fd, char *line, size_t len) {
488 char *newline = NULL;
494 while(!(newline = memchr(buffer, '\n', blen))) {
495 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
497 if(nrecv == -1 && sockerrno == EINTR) {
499 } else if(nrecv <= 0) {
506 if((size_t)(newline - buffer) >= len) {
510 len = newline - buffer;
512 memcpy(line, buffer, len);
514 memmove(buffer, newline + 1, blen - len - 1);
520 static bool recvdata(int fd, char *data, size_t len) {
522 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
524 if(nrecv == -1 && sockerrno == EINTR) {
526 } else if(nrecv <= 0) {
533 memcpy(data, buffer, len);
534 memmove(buffer, buffer + len, blen - len);
540 bool sendline(int fd, const char *format, ...) {
541 static char buffer[4096];
546 va_start(ap, format);
547 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
548 buffer[sizeof(buffer) - 1] = 0;
551 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
559 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
561 if(nsend == -1 && sockerrno == EINTR) {
563 } else if(nsend <= 0) {
574 static void pcap(int fd, FILE *out, uint32_t snaplen) {
575 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
583 uint32_t tz_accuracy;
590 snaplen ? snaplen : sizeof(data),
603 fwrite(&header, sizeof(header), 1, out);
608 while(recvline(fd, line, sizeof(line))) {
611 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
612 gettimeofday(&tv, NULL);
614 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
618 if(!recvdata(fd, data, len)) {
622 packet.tv_sec = tv.tv_sec;
623 packet.tv_usec = tv.tv_usec;
625 packet.origlen = len;
626 fwrite(&packet, sizeof(packet), 1, out);
627 fwrite(data, len, 1, out);
632 static void log_control(int fd, FILE *out, int level, bool use_color) {
633 sendline(fd, "%d %d %d %d", CONTROL, REQ_LOG, level, use_color);
638 while(recvline(fd, line, sizeof(line))) {
640 int n = sscanf(line, "%d %d %d", &code, &req, &len);
642 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
646 if(!recvdata(fd, data, len)) {
650 fwrite(data, len, 1, out);
656 static bool stop_tincd(void) {
657 if(!connect_tincd(true)) {
661 sendline(fd, "%d %d", CONTROL, REQ_STOP);
663 while(recvline(fd, line, sizeof(line))) {
664 // wait for tincd to close the connection...
675 static bool remove_service(void) {
676 SC_HANDLE manager = NULL;
677 SC_HANDLE service = NULL;
678 SERVICE_STATUS status = {0};
679 bool success = false;
681 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
684 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
688 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
691 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
692 success = stop_tincd();
694 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
700 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
701 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
703 fprintf(stderr, "%s service stopped\n", identname);
706 if(!DeleteService(service)) {
707 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
716 CloseServiceHandle(service);
720 CloseServiceHandle(manager);
724 fprintf(stderr, "%s service removed\n", identname);
731 bool connect_tincd(bool verbose) {
736 struct timeval tv = {0, 0};
738 if(select(fd + 1, &r, NULL, NULL, &tv)) {
739 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
747 pidfile_t *pidfile = read_pidfile();
751 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
758 strcpy(controlcookie, pidfile->cookie);
763 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
764 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
765 /* clean up the stale socket and pid file */
767 unlink(unixsocketname);
771 struct sockaddr_un sa = {
772 .sun_family = AF_UNIX,
775 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
776 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
780 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
782 fd = socket(AF_UNIX, SOCK_STREAM, 0);
786 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
792 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
794 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
803 struct addrinfo hints = {
804 .ai_family = AF_UNSPEC,
805 .ai_socktype = SOCK_STREAM,
806 .ai_protocol = IPPROTO_TCP,
810 struct addrinfo *res = NULL;
812 if(getaddrinfo(pidfile->host, pidfile->port, &hints, &res) || !res) {
814 fprintf(stderr, "Cannot resolve %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
821 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
825 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
832 unsigned long arg = 0;
834 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
836 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
840 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
842 fprintf(stderr, "Cannot connect to %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
856 static const int one = 1;
857 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
860 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
865 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
867 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
875 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
877 fprintf(stderr, "Could not fully establish control socket connection\n");
889 static int cmd_start(int argc, char *argv[]) {
890 if(connect_tincd(false)) {
892 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
894 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
901 char *slash = strrchr(program_name, '/');
905 if((c = strrchr(program_name, '\\')) > slash) {
912 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
914 c = xstrdup("tincd");
918 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
923 Windows has no real concept of an "argv array". A command line is just one string.
924 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
925 it uses quotes to handle spaces in arguments.
926 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
927 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
928 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
930 xasprintf(&arg0, "\"%s\"", arg0);
932 nargv[nargc++] = arg0;
934 for(int i = 1; i < optind; i++) {
935 nargv[nargc++] = orig_argv[i];
938 for(int i = 1; i < argc; i++) {
939 nargv[nargc++] = argv[i];
943 int status = spawnvp(_P_WAIT, c, nargv);
949 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
955 int pfd[2] = {-1, -1};
957 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
958 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
967 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
976 snprintf(buf, sizeof(buf), "%d %d", pfd[1], use_ansi_escapes(stderr));
977 setenv("TINC_UMBILICAL", buf, true);
978 exit(execvp(c, nargv));
986 signal(SIGINT, SIG_IGN);
989 // Pass all log messages from the umbilical to stderr.
990 // A nul-byte right before closure means tincd started successfully.
995 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
996 failure = buf[len - 1];
1002 if(write(2, buf, len) != len) {
1003 // Nothing we can do about it.
1013 // Make sure the child process is really gone.
1015 pid_t result = waitpid(pid, &status, 0);
1018 signal(SIGINT, SIG_DFL);
1021 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1024 fprintf(stderr, "Error starting %s\n", c);
1029 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1033 static int cmd_stop(int argc, char *argv[]) {
1037 fprintf(stderr, "Too many arguments!\n");
1042 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1047 if(kill(pid, SIGTERM)) {
1048 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1052 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1053 waitpid(pid, NULL, 0);
1064 static int cmd_restart(int argc, char *argv[]) {
1066 return cmd_start(argc, argv);
1069 static int cmd_reload(int argc, char *argv[]) {
1073 fprintf(stderr, "Too many arguments!\n");
1077 if(!connect_tincd(true)) {
1081 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1083 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1084 fprintf(stderr, "Could not reload configuration.\n");
1092 static int dump_invitations(void) {
1093 char dname[PATH_MAX];
1094 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1095 DIR *dir = opendir(dname);
1098 if(errno == ENOENT) {
1099 fprintf(stderr, "No outstanding invitations.\n");
1103 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1111 while((ent = readdir(dir))) {
1112 char buf[MAX_STRING_SIZE];
1114 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1118 char fname[PATH_MAX];
1120 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1121 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1125 FILE *f = fopen(fname, "r");
1128 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1134 if(!fgets(buf, sizeof(buf), f)) {
1135 fprintf(stderr, "Invalid invitation file %s\n", fname);
1142 char *eol = buf + strlen(buf);
1144 while(strchr("\t \r\n", *--eol)) {
1148 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1149 fprintf(stderr, "Invalid invitation file %s\n", fname);
1154 printf("%s %s\n", ent->d_name, buf + 7);
1160 fprintf(stderr, "No outstanding invitations.\n");
1166 static int cmd_dump(int argc, char *argv[]) {
1167 bool only_reachable = false;
1169 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1170 if(strcasecmp(argv[2], "nodes")) {
1171 fprintf(stderr, "`reachable' only supported for nodes.\n");
1176 only_reachable = true;
1182 fprintf(stderr, "Invalid number of arguments.\n");
1187 if(!strcasecmp(argv[1], "invitations")) {
1188 return dump_invitations();
1191 if(!connect_tincd(true)) {
1197 if(!strcasecmp(argv[1], "nodes")) {
1198 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1199 } else if(!strcasecmp(argv[1], "edges")) {
1200 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1201 } else if(!strcasecmp(argv[1], "subnets")) {
1202 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1203 } else if(!strcasecmp(argv[1], "connections")) {
1204 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1205 } else if(!strcasecmp(argv[1], "graph")) {
1206 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1207 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1209 } else if(!strcasecmp(argv[1], "digraph")) {
1210 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1211 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1214 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1220 printf("graph {\n");
1221 } else if(do_graph == 2) {
1222 printf("digraph {\n");
1225 while(recvline(fd, line, sizeof(line))) {
1226 char node1[4096], node2[4096];
1227 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1230 if(do_graph && req == REQ_DUMP_NODES) {
1252 char local_host[4096];
1253 char local_port[4096];
1256 int cipher, digest, maclength, compression, distance, socket, weight;
1257 short int pmtu, minmtu, maxmtu;
1258 unsigned int options;
1259 node_status_t status;
1260 long int last_state_change;
1262 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1265 case REQ_DUMP_NODES: {
1266 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %d %d %d %d %x %"PRIx32" %4095s %4095s %d %hd %hd %hd %ld %d %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status.value, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change, &udp_ping_rtt, &in_packets, &in_bytes, &out_packets, &out_bytes);
1269 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1274 const char *color = "black";
1276 if(!strcmp(host, "MYSELF")) {
1278 } else if(!status.reachable) {
1280 } else if(strcmp(via, node)) {
1282 } else if(!status.validkey) {
1284 } else if(minmtu > 0) {
1288 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1290 if(only_reachable && !status.reachable) {
1294 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,
1295 node, id, host, port, cipher, digest, maclength, compression, options, status.value, nexthop, via, distance, pmtu, minmtu, maxmtu, in_packets, in_bytes, out_packets, out_bytes);
1297 if(udp_ping_rtt != -1) {
1298 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1306 case REQ_DUMP_EDGES: {
1307 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);
1310 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1315 float w = 1.0f + 65536.0f / (float)weight;
1317 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1318 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1319 } else if(do_graph == 2) {
1320 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1323 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);
1328 case REQ_DUMP_SUBNETS: {
1329 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1332 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1336 printf("%s owner %s\n", strip_weight(subnet), node);
1340 case REQ_DUMP_CONNECTIONS: {
1341 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1344 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1348 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1353 fprintf(stderr, "Unable to parse dump from tincd.\n");
1358 fprintf(stderr, "Error receiving dump.\n");
1362 static int cmd_purge(int argc, char *argv[]) {
1366 fprintf(stderr, "Too many arguments!\n");
1370 if(!connect_tincd(true)) {
1374 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1376 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1377 fprintf(stderr, "Could not purge old information.\n");
1384 static int cmd_debug(int argc, char *argv[]) {
1386 fprintf(stderr, "Invalid number of arguments.\n");
1390 if(!connect_tincd(true)) {
1394 int debuglevel = atoi(argv[1]);
1397 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1399 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1400 fprintf(stderr, "Could not set debug level.\n");
1404 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1408 static int cmd_retry(int argc, char *argv[]) {
1412 fprintf(stderr, "Too many arguments!\n");
1416 if(!connect_tincd(true)) {
1420 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1422 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1423 fprintf(stderr, "Could not retry outgoing connections.\n");
1430 static int cmd_connect(int argc, char *argv[]) {
1432 fprintf(stderr, "Invalid number of arguments.\n");
1436 if(!check_id(argv[1])) {
1437 fprintf(stderr, "Invalid name for node.\n");
1441 if(!connect_tincd(true)) {
1445 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1447 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1448 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1455 static int cmd_disconnect(int argc, char *argv[]) {
1457 fprintf(stderr, "Invalid number of arguments.\n");
1461 if(!check_id(argv[1])) {
1462 fprintf(stderr, "Invalid name for node.\n");
1466 if(!connect_tincd(true)) {
1470 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1472 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1473 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1480 static int cmd_top(int argc, char *argv[]) {
1484 fprintf(stderr, "Too many arguments!\n");
1490 if(!connect_tincd(true)) {
1497 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1502 static int cmd_pcap(int argc, char *argv[]) {
1504 fprintf(stderr, "Too many arguments!\n");
1508 if(!connect_tincd(true)) {
1512 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1517 static void sigint_handler(int sig) {
1520 fprintf(stderr, "\n");
1521 shutdown(fd, SHUT_RDWR);
1525 static int cmd_log(int argc, char *argv[]) {
1527 fprintf(stderr, "Too many arguments!\n");
1531 if(!connect_tincd(true)) {
1536 signal(SIGINT, sigint_handler);
1539 bool use_color = use_ansi_escapes(stdout);
1540 log_control(fd, stdout, argc > 1 ? atoi(argv[1]) : DEBUG_UNSET, use_color);
1543 signal(SIGINT, SIG_DFL);
1551 static int cmd_pid(int argc, char *argv[]) {
1555 fprintf(stderr, "Too many arguments!\n");
1559 if(!connect_tincd(true) || !pid) {
1563 printf("%d\n", pid);
1567 size_t rstrip(char *value) {
1568 size_t len = strlen(value);
1570 while(len && strchr("\t\r\n ", value[len - 1])) {
1577 char *get_my_name(bool verbose) {
1578 FILE *f = fopen(tinc_conf, "r");
1582 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1591 while(fgets(buf, sizeof(buf), f)) {
1592 size_t len = strcspn(buf, "\t =");
1594 value += strspn(value, "\t ");
1598 value += strspn(value, "\t ");
1601 if(!rstrip(value)) {
1607 if(strcasecmp(buf, "Name")) {
1613 return replace_name(value);
1620 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1626 static ecdsa_t *get_pubkey(FILE *f) ATTR_MALLOC ATTR_DEALLOCATOR(ecdsa_free);
1627 static ecdsa_t *get_pubkey(FILE *f) {
1631 while(fgets(buf, sizeof(buf), f)) {
1632 size_t len = strcspn(buf, "\t =");
1634 value += strspn(value, "\t ");
1638 value += strspn(value, "\t ");
1641 if(!rstrip(value)) {
1647 if(strcasecmp(buf, "Ed25519PublicKey")) {
1652 return ecdsa_set_base64_public_key(value);
1659 const var_t variables[] = {
1660 /* Server configuration */
1661 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1662 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1663 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1664 {"BindToInterface", VAR_SERVER},
1665 {"Broadcast", VAR_SERVER | VAR_SAFE},
1666 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1667 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1668 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1669 {"Device", VAR_SERVER},
1670 {"DeviceStandby", VAR_SERVER},
1671 {"DeviceType", VAR_SERVER},
1672 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1673 {"Ed25519PrivateKeyFile", VAR_SERVER},
1674 {"ExperimentalProtocol", VAR_SERVER},
1675 {"Forwarding", VAR_SERVER},
1676 {"FWMark", VAR_SERVER},
1677 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1678 {"Hostnames", VAR_SERVER},
1679 {"IffOneQueue", VAR_SERVER},
1680 {"Interface", VAR_SERVER},
1681 {"InvitationExpire", VAR_SERVER},
1682 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1683 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1684 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1685 {"LogLevel", VAR_SERVER},
1686 {"MACExpire", VAR_SERVER | VAR_SAFE},
1687 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1688 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1689 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1690 {"Mode", VAR_SERVER | VAR_SAFE},
1691 {"Name", VAR_SERVER},
1692 {"PingInterval", VAR_SERVER | VAR_SAFE},
1693 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1694 {"PriorityInheritance", VAR_SERVER},
1695 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1696 {"PrivateKeyFile", VAR_SERVER},
1697 {"ProcessPriority", VAR_SERVER},
1698 {"Proxy", VAR_SERVER},
1699 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1700 {"ScriptsExtension", VAR_SERVER},
1701 {"ScriptsInterpreter", VAR_SERVER},
1702 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1703 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1704 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1705 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1706 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1707 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1708 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1709 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1710 {"UDPRcvBuf", VAR_SERVER},
1711 {"UDPSndBuf", VAR_SERVER},
1712 {"UPnP", VAR_SERVER},
1713 {"UPnPDiscoverWait", VAR_SERVER},
1714 {"UPnPRefreshPeriod", VAR_SERVER},
1715 {"VDEGroup", VAR_SERVER},
1716 {"VDEPort", VAR_SERVER},
1717 /* Host configuration */
1718 {"Address", VAR_HOST | VAR_MULTIPLE},
1719 {"Cipher", VAR_SERVER | VAR_HOST},
1720 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1721 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1722 {"Digest", VAR_SERVER | VAR_HOST},
1723 {"Ed25519PublicKey", VAR_HOST},
1724 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1725 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1726 {"MACLength", VAR_SERVER | VAR_HOST},
1727 {"PMTU", VAR_SERVER | VAR_HOST},
1728 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1730 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1731 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1732 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1733 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1734 {"Weight", VAR_HOST | VAR_SAFE},
1738 // Request actual port from tincd
1739 static bool read_actual_port(void) {
1740 pidfile_t *pidfile = read_pidfile();
1743 printf("%s\n", pidfile->port);
1747 fprintf(stderr, "Could not get port from the pidfile.\n");
1752 static int cmd_config(int argc, char *argv[]) {
1754 fprintf(stderr, "Invalid number of arguments.\n");
1758 if(strcasecmp(argv[0], "config")) {
1762 typedef enum { GET, DEL, SET, ADD } action_t;
1763 action_t action = GET;
1765 if(!strcasecmp(argv[1], "get")) {
1767 } else if(!strcasecmp(argv[1], "add")) {
1768 argv++, argc--, action = ADD;
1769 } else if(!strcasecmp(argv[1], "del")) {
1770 argv++, argc--, action = DEL;
1771 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1772 argv++, argc--, action = SET;
1776 fprintf(stderr, "Invalid number of arguments.\n");
1780 // Concatenate the rest of the command line
1781 strncpy(line, argv[1], sizeof(line) - 1);
1783 for(int i = 2; i < argc; i++) {
1784 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1785 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1788 // Liberal parsing into node name, variable name and value.
1794 len = strcspn(line, "\t =");
1796 value += strspn(value, "\t ");
1800 value += strspn(value, "\t ");
1804 variable = strchr(line, '.');
1814 fprintf(stderr, "No variable given.\n");
1818 if((action == SET || action == ADD) && !*value) {
1819 fprintf(stderr, "No value for variable given.\n");
1823 if(action == GET && *value) {
1827 // If port is requested, try reading it from the pidfile and fall back to configs if that fails
1828 if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
1832 /* Some simple checks. */
1834 bool warnonremove = false;
1836 for(int i = 0; variables[i].name; i++) {
1837 if(strcasecmp(variables[i].name, variable)) {
1842 variable = (char *)variables[i].name;
1844 if(!strcasecmp(variable, "Subnet") && *value) {
1847 if(!str2net(&s, value)) {
1848 fprintf(stderr, "Malformed subnet definition %s\n", value);
1852 if(!subnetcheck(s)) {
1853 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1858 /* Discourage use of obsolete variables. */
1860 if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
1862 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1864 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1869 /* Don't put server variables in host config files */
1871 if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
1873 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1875 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1880 /* Should this go into our own host config file? */
1882 if(!node && !(variables[i].type & VAR_SERVER)) {
1883 node = get_my_name(true);
1890 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1891 Turn on warnings when it seems variables might be removed unintentionally. */
1893 if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
1894 warnonremove = true;
1896 } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
1897 warnonremove = true;
1903 if(node && !check_id(node)) {
1904 fprintf(stderr, "Invalid name for node.\n");
1914 if(force || action == GET || action == DEL) {
1915 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1917 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1919 if(node && node != line) {
1927 // Open the right configuration file.
1928 char filename[PATH_MAX];
1931 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node) >= sizeof(filename)) {
1932 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1942 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1945 FILE *f = fopen(filename, "r");
1948 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1952 char tmpfile[PATH_MAX];
1956 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1957 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1961 tf = fopen(tmpfile, "w");
1964 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1970 // Copy the file, making modifications on the fly, unless we are just getting a value.
1974 bool removed = false;
1977 while(fgets(buf1, sizeof(buf1), f)) {
1978 buf1[sizeof(buf1) - 1] = 0;
1979 strncpy(buf2, buf1, sizeof(buf2));
1981 // Parse line in a simple way
1984 size_t len = strcspn(buf2, "\t =");
1985 bvalue = buf2 + len;
1986 bvalue += strspn(bvalue, "\t ");
1988 if(*bvalue == '=') {
1990 bvalue += strspn(bvalue, "\t ");
1997 if(!strcasecmp(buf2, variable)) {
2000 printf("%s\n", bvalue);
2001 } else if(action == DEL) {
2002 if(!*value || !strcasecmp(bvalue, value)) {
2006 } else if(action == SET) {
2007 // Warn if "set" was used for variables that can occur multiple times
2008 if(warnonremove && strcasecmp(bvalue, value)) {
2009 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2012 // Already set? Delete the rest...
2017 // Otherwise, replace.
2018 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2019 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2025 } else if(action == ADD) {
2026 // Check if we've already seen this variable with the same value
2027 if(!strcasecmp(bvalue, value)) {
2034 // Copy original line...
2035 if(fputs(buf1, tf) < 0) {
2036 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2040 // Add newline if it is missing...
2041 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2042 if(fputc('\n', tf) < 0) {
2043 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2050 // Make sure we read everything...
2051 if(ferror(f) || !feof(f)) {
2052 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2057 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2061 // Add new variable if necessary.
2062 if((action == ADD && !found) || (action == SET && !set)) {
2063 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2064 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2073 fprintf(stderr, "No matching configuration variables found.\n");
2078 // Make sure we wrote everything...
2080 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2084 // Could we find what we had to remove?
2085 if((action == GET || action == DEL) && !removed) {
2087 fprintf(stderr, "No configuration variables deleted.\n");
2091 // Replace the configuration file with the new one
2094 if(remove(filename)) {
2095 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2101 if(rename(tmpfile, filename)) {
2102 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2106 // Silently try notifying a running tincd of changes.
2107 if(connect_tincd(false)) {
2108 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2114 static bool try_bind(int port) {
2115 struct addrinfo *ai = NULL, *aip;
2116 struct addrinfo hint = {
2117 .ai_flags = AI_PASSIVE,
2118 .ai_family = AF_UNSPEC,
2119 .ai_socktype = SOCK_STREAM,
2120 .ai_protocol = IPPROTO_TCP,
2123 bool success = true;
2125 snprintf(portstr, sizeof(portstr), "%d", port);
2127 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2131 for(aip = ai; aip; aip = aip->ai_next) {
2132 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2139 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2152 int check_port(const char *name) {
2157 fprintf(stderr, "Warning: could not bind to port 655. ");
2159 for(int i = 0; i < 100; i++) {
2160 uint16_t port = 0x1000 + prng(0x8000);
2162 if(try_bind(port)) {
2163 char filename[PATH_MAX];
2164 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2165 FILE *f = fopen(filename, "a");
2168 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2169 fprintf(stderr, "Please change tinc's Port manually.\n");
2173 fprintf(f, "Port = %d\n", port);
2175 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2180 fprintf(stderr, "Please change tinc's Port manually.\n");
2184 static int cmd_init(int argc, char *argv[]) {
2185 if(!access(tinc_conf, F_OK)) {
2186 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2191 fprintf(stderr, "Too many arguments!\n");
2193 } else if(argc < 2) {
2196 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2198 if(!fgets(buf, sizeof(buf), stdin)) {
2199 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2203 size_t len = rstrip(buf);
2206 fprintf(stderr, "No name given!\n");
2212 fprintf(stderr, "No Name given!\n");
2216 name = strdup(argv[1]);
2219 fprintf(stderr, "No Name given!\n");
2224 if(!check_id(name)) {
2225 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2229 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2230 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2234 if(mkdir(confbase, 0777) && errno != EEXIST) {
2235 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2239 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2240 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2244 FILE *f = fopen(tinc_conf, "w");
2247 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2251 fprintf(f, "Name = %s\n", name);
2254 #ifndef DISABLE_LEGACY
2256 if(!rsa_keygen(2048, false)) {
2262 if(!ed25519_keygen(false)) {
2268 #ifndef HAVE_WINDOWS
2269 char filename[PATH_MAX];
2270 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2272 if(access(filename, F_OK)) {
2273 FILE *f = fopenmask(filename, "w", 0777);
2276 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2280 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");
2290 static int cmd_generate_keys(int argc, char *argv[]) {
2291 #ifdef DISABLE_LEGACY
2299 fprintf(stderr, "Too many arguments!\n");
2304 name = get_my_name(false);
2307 #ifndef DISABLE_LEGACY
2309 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2315 if(!ed25519_keygen(true)) {
2322 #ifndef DISABLE_LEGACY
2323 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2325 fprintf(stderr, "Too many arguments!\n");
2330 name = get_my_name(false);
2333 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2337 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2341 fprintf(stderr, "Too many arguments!\n");
2346 name = get_my_name(false);
2349 return !ed25519_keygen(true);
2352 static int cmd_help(int argc, char *argv[]) {
2360 static int cmd_version(int argc, char *argv[]) {
2364 fprintf(stderr, "Too many arguments!\n");
2372 static int cmd_info(int argc, char *argv[]) {
2374 fprintf(stderr, "Invalid number of arguments.\n");
2378 if(!connect_tincd(true)) {
2382 return info(fd, argv[1]);
2385 static const char *conffiles[] = {
2396 static int cmd_edit(int argc, char *argv[]) {
2398 fprintf(stderr, "Invalid number of arguments.\n");
2402 char filename[PATH_MAX] = "";
2404 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2405 for(int i = 0; conffiles[i]; i++) {
2406 if(!strcmp(argv[1], conffiles[i])) {
2407 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2416 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2417 char *dash = strchr(argv[1], '-');
2422 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2423 fprintf(stderr, "Invalid configuration filename.\n");
2430 #ifndef HAVE_WINDOWS
2431 const char *editor = getenv("VISUAL");
2434 editor = getenv("EDITOR");
2441 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2443 xasprintf(&command, "edit \"%s\"", filename);
2445 int result = system(command);
2452 // Silently try notifying a running tincd of changes.
2453 if(connect_tincd(false)) {
2454 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2460 static int export(const char *name, FILE *out) {
2461 char filename[PATH_MAX];
2462 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2463 FILE *in = fopen(filename, "r");
2466 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2470 fprintf(out, "Name = %s\n", name);
2473 while(fgets(buf, sizeof(buf), in)) {
2474 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2480 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2489 static int cmd_export(int argc, char *argv[]) {
2493 fprintf(stderr, "Too many arguments!\n");
2497 char *name = get_my_name(true);
2503 int result = export(name, stdout);
2513 static int cmd_export_all(int argc, char *argv[]) {
2517 fprintf(stderr, "Too many arguments!\n");
2521 DIR *dir = opendir(hosts_dir);
2524 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2532 while((ent = readdir(dir))) {
2533 if(!check_id(ent->d_name)) {
2540 printf("#---------------------------------------------------------------#\n");
2543 result |= export(ent->d_name, stdout);
2555 static int cmd_import(int argc, char *argv[]) {
2559 fprintf(stderr, "Too many arguments!\n");
2568 char filename[PATH_MAX] = "";
2570 bool firstline = true;
2572 while(fgets(buf, sizeof(buf), in)) {
2573 if(sscanf(buf, "Name = %4095s", name) == 1) {
2576 if(!check_id(name)) {
2577 fprintf(stderr, "Invalid Name in input!\n");
2585 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2586 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2590 if(!force && !access(filename, F_OK)) {
2591 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2596 out = fopen(filename, "w");
2599 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2605 } else if(firstline) {
2606 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2611 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2616 if(fputs(buf, out) < 0) {
2617 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2628 fprintf(stderr, "Imported %d host configuration files.\n", count);
2631 fprintf(stderr, "No host configuration files imported.\n");
2636 static int cmd_exchange(int argc, char *argv[]) {
2637 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2640 static int cmd_exchange_all(int argc, char *argv[]) {
2641 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2644 static int switch_network(char *name) {
2645 if(strcmp(name, ".")) {
2646 if(!check_netname(name, false)) {
2647 fprintf(stderr, "Invalid character in netname!\n");
2651 if(!check_netname(name, true)) {
2652 fprintf(stderr, "Warning: unsafe character in netname!\n");
2662 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2669 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2670 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2671 xasprintf(&prompt, "%s> ", identname);
2676 static int cmd_network(int argc, char *argv[]) {
2678 fprintf(stderr, "Too many arguments!\n");
2683 return switch_network(argv[1]);
2686 DIR *dir = opendir(confdir);
2689 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2695 while((ent = readdir(dir))) {
2696 if(*ent->d_name == '.') {
2700 if(!strcmp(ent->d_name, "tinc.conf")) {
2705 char fname[PATH_MAX];
2706 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2708 if(!access(fname, R_OK)) {
2709 printf("%s\n", ent->d_name);
2718 static int cmd_fsck(int argc, char *argv[]) {
2722 fprintf(stderr, "Too many arguments!\n");
2726 return fsck(orig_argv[0]);
2729 static void *readfile(FILE *in, size_t *len) {
2731 size_t bufsize = 4096;
2732 char *buf = xmalloc(bufsize);
2735 size_t read = fread(buf + count, 1, bufsize - count, in);
2743 if(count >= bufsize) {
2745 buf = xrealloc(buf, bufsize);
2756 static int cmd_sign(int argc, char *argv[]) {
2758 fprintf(stderr, "Too many arguments!\n");
2763 name = get_my_name(true);
2770 char fname[PATH_MAX];
2771 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2772 FILE *fp = fopen(fname, "r");
2775 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2779 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2782 fprintf(stderr, "Could not read private key from %s\n", fname);
2792 in = fopen(argv[1], "rb");
2795 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2804 char *data = readfile(in, &len);
2811 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2816 // Ensure we sign our name and current time as well
2817 long t = time(NULL);
2819 xasprintf(&trailer, " %s %ld", name, t);
2820 size_t trailer_len = strlen(trailer);
2822 data = xrealloc(data, len + trailer_len);
2823 memcpy(data + len, trailer, trailer_len);
2828 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2829 fprintf(stderr, "Error generating signature\n");
2835 b64encode_tinc(sig, sig, 64);
2838 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2839 fwrite(data, len, 1, stdout);
2845 static int cmd_verify(int argc, char *argv[]) {
2847 fprintf(stderr, "Not enough arguments!\n");
2852 fprintf(stderr, "Too many arguments!\n");
2856 char *node = argv[1];
2858 if(!strcmp(node, ".")) {
2860 name = get_my_name(true);
2868 } else if(!strcmp(node, "*")) {
2871 if(!check_id(node)) {
2872 fprintf(stderr, "Invalid node name\n");
2880 in = fopen(argv[2], "rb");
2883 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2891 char *data = readfile(in, &len);
2898 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2902 char *newline = memchr(data, '\n', len);
2904 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2905 fprintf(stderr, "Invalid input\n");
2911 size_t skip = newline - data;
2913 char signer[MAX_STRING_SIZE] = "";
2914 char sig[MAX_STRING_SIZE] = "";
2917 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2918 fprintf(stderr, "Invalid input\n");
2923 if(node && strcmp(node, signer)) {
2924 fprintf(stderr, "Signature is not made by %s\n", node);
2934 xasprintf(&trailer, " %s %ld", signer, t);
2935 size_t trailer_len = strlen(trailer);
2937 data = xrealloc(data, len + trailer_len);
2938 memcpy(data + len, trailer, trailer_len);
2941 newline = data + skip;
2943 char fname[PATH_MAX];
2944 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2945 FILE *fp = fopen(fname, "r");
2948 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2953 ecdsa_t *key = get_pubkey(fp);
2957 key = ecdsa_read_pem_public_key(fp);
2961 fprintf(stderr, "Could not read public key from %s\n", fname);
2969 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2970 fprintf(stderr, "Invalid signature\n");
2978 fwrite(newline, len - (newline - data), 1, stdout);
2984 static const struct {
2985 const char *command;
2986 int (*function)(int argc, char *argv[]);
2989 {"start", cmd_start, false},
2990 {"stop", cmd_stop, false},
2991 {"restart", cmd_restart, false},
2992 {"reload", cmd_reload, false},
2993 {"dump", cmd_dump, false},
2994 {"list", cmd_dump, false},
2995 {"purge", cmd_purge, false},
2996 {"debug", cmd_debug, false},
2997 {"retry", cmd_retry, false},
2998 {"connect", cmd_connect, false},
2999 {"disconnect", cmd_disconnect, false},
3000 {"top", cmd_top, false},
3001 {"pcap", cmd_pcap, false},
3002 {"log", cmd_log, false},
3003 {"pid", cmd_pid, false},
3004 {"config", cmd_config, true},
3005 {"add", cmd_config, false},
3006 {"del", cmd_config, false},
3007 {"get", cmd_config, false},
3008 {"set", cmd_config, false},
3009 {"init", cmd_init, false},
3010 {"generate-keys", cmd_generate_keys, false},
3011 #ifndef DISABLE_LEGACY
3012 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3014 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3015 {"help", cmd_help, false},
3016 {"version", cmd_version, false},
3017 {"info", cmd_info, false},
3018 {"edit", cmd_edit, false},
3019 {"export", cmd_export, false},
3020 {"export-all", cmd_export_all, false},
3021 {"import", cmd_import, false},
3022 {"exchange", cmd_exchange, false},
3023 {"exchange-all", cmd_exchange_all, false},
3024 {"invite", cmd_invite, false},
3025 {"join", cmd_join, false},
3026 {"network", cmd_network, false},
3027 {"fsck", cmd_fsck, false},
3028 {"sign", cmd_sign, false},
3029 {"verify", cmd_verify, false},
3030 {NULL, NULL, false},
3033 #ifdef HAVE_READLINE
3034 static char *complete_command(const char *text, int state) {
3043 while(commands[i].command) {
3044 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3045 return xstrdup(commands[i].command);
3054 static char *complete_dump(const char *text, int state) {
3055 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3065 if(!strncasecmp(matches[i], text, strlen(text))) {
3066 return xstrdup(matches[i]);
3075 static char *complete_config(const char *text, int state) {
3084 while(variables[i].name) {
3085 char *dot = strchr(text, '.');
3088 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3090 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3094 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3095 return xstrdup(variables[i].name);
3105 static char *complete_info(const char *text, int state) {
3111 if(!connect_tincd(false)) {
3115 // Check the list of nodes
3116 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3117 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3120 while(recvline(fd, line, sizeof(line))) {
3122 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3135 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3139 if(!strncmp(item, text, strlen(text))) {
3140 return xstrdup(strip_weight(item));
3147 static char *complete_nothing(const char *text, int state) {
3153 static char **completion(const char *text, int start, int end) {
3155 char **matches = NULL;
3158 matches = rl_completion_matches(text, complete_command);
3159 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3160 matches = rl_completion_matches(text, complete_dump);
3161 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3162 matches = rl_completion_matches(text, complete_config);
3163 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3164 matches = rl_completion_matches(text, complete_config);
3165 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3166 matches = rl_completion_matches(text, complete_config);
3167 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3168 matches = rl_completion_matches(text, complete_config);
3169 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3170 matches = rl_completion_matches(text, complete_info);
3177 static int cmd_shell(int argc, char *argv[]) {
3178 xasprintf(&prompt, "%s> ", identname);
3182 int maxargs = argc + 16;
3183 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3185 for(int i = 0; i < argc; i++) {
3189 #ifdef HAVE_READLINE
3190 rl_readline_name = "tinc";
3191 rl_basic_word_break_characters = "\t\n ";
3192 rl_completion_entry_function = complete_nothing;
3193 rl_attempted_completion_function = completion;
3194 rl_filename_completion_desired = 0;
3199 #ifdef HAVE_READLINE
3204 line = readline(prompt);
3205 copy = line ? xstrdup(line) : NULL;
3207 line = fgets(buf, sizeof(buf), stdin);
3213 fputs(prompt, stdout);
3216 line = fgets(buf, sizeof(buf), stdin);
3223 /* Ignore comments */
3232 char *p = line + strspn(line, " \t\n");
3233 char *next = strtok(p, " \t\n");
3236 if(nargc >= maxargs) {
3238 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3243 next = strtok(NULL, " \t\n");
3250 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3251 #ifdef HAVE_READLINE
3260 for(int i = 0; commands[i].command; i++) {
3261 if(!strcasecmp(nargv[argc], commands[i].command)) {
3262 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3268 #ifdef HAVE_READLINE
3277 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3282 #ifdef HAVE_READLINE
3294 static void cleanup(void) {
3300 static int run_command(int argc, char *argv[]) {
3301 if(optind >= argc) {
3302 return cmd_shell(argc, argv);
3305 for(int i = 0; commands[i].command; i++) {
3306 if(!strcasecmp(argv[optind], commands[i].command)) {
3307 return commands[i].function(argc - optind, argv + optind);
3311 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3316 int main(int argc, char *argv[]) {
3317 program_name = argv[0];
3319 tty = isatty(0) && isatty(1);
3321 if(!parse_options(argc, argv)) {
3326 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3327 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3341 static struct WSAData wsa_state;
3343 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3344 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3350 gettimeofday(&now, NULL);
3355 int result = run_command(argc, argv);