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"
47 #define MSG_NOSIGNAL 0
50 static char **orig_argv;
52 /* If nonzero, display usage information and exit. */
53 static bool show_help = false;
55 /* If nonzero, print the version on standard output and exit. */
56 static bool show_version = false;
58 static char *name = NULL;
59 static char controlcookie[1025];
60 char *tinc_conf = NULL;
61 char *hosts_dir = NULL;
64 // Horrible global variables...
73 bool confbasegiven = false;
74 char *scriptinterpreter = NULL;
75 static char defaultextension[] = "";
76 char *scriptextension = defaultextension;
82 static struct option const long_options[] = {
83 {"batch", no_argument, NULL, 'b'},
84 {"config", required_argument, NULL, 'c'},
85 {"net", required_argument, NULL, 'n'},
86 {"help", no_argument, NULL, 1},
87 {"version", no_argument, NULL, 2},
88 {"pidfile", required_argument, NULL, 3},
89 {"force", no_argument, NULL, 4},
93 static void version(void) {
94 static const char *message =
95 "%s version %s (built %s %s, protocol %d.%d)\n"
103 #ifndef DISABLE_LEGACY
107 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
108 "See the AUTHORS file for a complete list.\n"
110 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
111 "and you are welcome to redistribute it under certain conditions;\n"
112 "see the file COPYING for details.\n";
114 printf(message, PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
117 static void usage(bool status) {
119 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
121 static const char *message =
122 "Usage: %s [options] command\n"
124 "Valid options are:\n"
125 " -b, --batch Don't ask for anything (non-interactive mode).\n"
126 " -c, --config=DIR Read configuration options from DIR.\n"
127 " -n, --net=NETNAME Connect to net NETNAME.\n"
128 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
129 " --force Force some commands to work despite warnings.\n"
130 " --help Display this help and exit.\n"
131 " --version Output version information and exit.\n"
133 "Valid commands are:\n"
134 " init [name] Create initial configuration files.\n"
135 " get VARIABLE Print current value of VARIABLE\n"
136 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
137 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
138 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
139 " start [tincd options] Start tincd.\n"
140 " stop Stop tincd.\n"
141 " restart [tincd options] Restart tincd.\n"
142 " reload Partially reload configuration of running tincd.\n"
143 " pid Show PID of currently running tincd.\n"
144 #ifdef DISABLE_LEGACY
145 " generate-keys Generate a new Ed25519 public/private key pair.\n"
147 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
148 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
150 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
151 " dump Dump a list of one of the following things:\n"
152 " [reachable] nodes - all known nodes in the VPN\n"
153 " edges - all known connections in the VPN\n"
154 " subnets - all known subnets in the VPN\n"
155 " connections - all meta connections with ourself\n"
156 " [di]graph - graph of the VPN in dotty format\n"
157 " invitations - outstanding invitations\n"
158 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
159 " purge Purge unreachable nodes\n"
160 " debug N Set debug level\n"
161 " retry Retry all outgoing connections\n"
162 " disconnect NODE Close meta connection with NODE\n"
164 " top Show real-time statistics\n"
166 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
167 " log [level] Dump log output [up to the specified level]\n"
168 " export Export host configuration of local node to standard output\n"
169 " export-all Export all host configuration files to standard output\n"
170 " import Import host configuration file(s) from standard input\n"
171 " exchange Same as export followed by import\n"
172 " exchange-all Same as export-all followed by import\n"
173 " invite NODE [...] Generate an invitation for NODE\n"
174 " join INVITATION Join a VPN using an INVITATION\n"
175 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
176 " fsck Check the configuration files for problems.\n"
177 " sign [FILE] Generate a signed version of a file.\n"
178 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
180 "Report bugs to tinc@tinc-vpn.org.\n";
182 printf(message, program_name);
186 static bool parse_options(int argc, char **argv) {
188 int option_index = 0;
190 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
192 case 0: /* long option */
199 case 'c': /* config file */
201 confbase = xstrdup(optarg);
202 confbasegiven = true;
205 case 'n': /* net name given */
207 netname = xstrdup(optarg);
210 case 1: /* show help */
214 case 2: /* show version */
218 case 3: /* open control socket here */
220 pidfilename = xstrdup(optarg);
227 case '?': /* wrong options */
237 if(!netname && (netname = getenv("NETNAME"))) {
238 netname = xstrdup(netname);
241 /* netname "." is special: a "top-level name" */
243 if(netname && (!*netname || !strcmp(netname, "."))) {
248 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
249 fprintf(stderr, "Invalid character in netname!\n");
257 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
259 char directory[PATH_MAX] = ".";
265 /* Check stdin and stdout */
267 /* Ask for a file and/or directory name. */
268 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
270 if(fgets(buf, sizeof(buf), stdin) == NULL) {
271 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
275 size_t len = strlen(buf);
288 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
291 if(filename[0] != '/') {
294 /* The directory is a relative path or a filename. */
295 if(!getcwd(directory, sizeof(directory))) {
296 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
300 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
301 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
313 disable_old_keys(filename, what);
315 /* Open it first to keep the inode busy */
317 r = fopenmask(filename, mode, perms);
320 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
328 Generate a public/private Ed25519 key pair, and ask for a file to store
331 static bool ed25519_keygen(bool ask) {
334 char fname[PATH_MAX];
336 fprintf(stderr, "Generating Ed25519 key pair:\n");
338 if(!(key = ecdsa_generate())) {
339 fprintf(stderr, "Error during key generation!\n");
342 fprintf(stderr, "Done.\n");
345 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
346 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
352 if(!ecdsa_write_pem_private_key(key, f)) {
353 fprintf(stderr, "Error writing private key!\n");
360 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
362 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
365 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
371 char *pubkey = ecdsa_get_base64_public_key(key);
372 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
390 #ifndef DISABLE_LEGACY
392 Generate a public/private RSA key pair, and ask for a file to store
395 static bool rsa_keygen(int bits, bool ask) {
398 char fname[PATH_MAX];
400 // Make sure the key size is a multiple of 8 bits.
403 // Make sure that a valid key size is used.
404 if(bits < 1024 || bits > 8192) {
405 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
407 } else if(bits < 2048) {
408 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
411 fprintf(stderr, "Generating %d bits keys:\n", bits);
413 if(!(key = rsa_generate(bits, 0x10001))) {
414 fprintf(stderr, "Error during key generation!\n");
417 fprintf(stderr, "Done.\n");
420 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
421 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
427 if(!rsa_write_pem_private_key(key, f)) {
428 fprintf(stderr, "Error writing private key!\n");
435 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
437 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
440 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
446 if(!rsa_write_pem_public_key(key, f)) {
447 fprintf(stderr, "Error writing public key!\n");
470 bool recvline(int fd, char *line, size_t len) {
471 char *newline = NULL;
477 while(!(newline = memchr(buffer, '\n', blen))) {
478 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
480 if(nrecv == -1 && sockerrno == EINTR) {
482 } else if(nrecv <= 0) {
489 if((size_t)(newline - buffer) >= len) {
493 len = newline - buffer;
495 memcpy(line, buffer, len);
497 memmove(buffer, newline + 1, blen - len - 1);
503 static bool recvdata(int fd, char *data, size_t len) {
505 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
507 if(nrecv == -1 && sockerrno == EINTR) {
509 } else if(nrecv <= 0) {
516 memcpy(data, buffer, len);
517 memmove(buffer, buffer + len, blen - len);
523 bool sendline(int fd, const char *format, ...) {
524 static char buffer[4096];
529 va_start(ap, format);
530 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
531 buffer[sizeof(buffer) - 1] = 0;
534 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
542 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
544 if(nsend == -1 && sockerrno == EINTR) {
546 } else if(nsend <= 0) {
557 static void pcap(int fd, FILE *out, uint32_t snaplen) {
558 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
566 uint32_t tz_accuracy;
573 snaplen ? snaplen : sizeof(data),
586 fwrite(&header, sizeof(header), 1, out);
591 while(recvline(fd, line, sizeof(line))) {
594 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
595 gettimeofday(&tv, NULL);
597 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
601 if(!recvdata(fd, data, len)) {
605 packet.tv_sec = tv.tv_sec;
606 packet.tv_usec = tv.tv_usec;
608 packet.origlen = len;
609 fwrite(&packet, sizeof(packet), 1, out);
610 fwrite(data, len, 1, out);
615 static void log_control(int fd, FILE *out, int level) {
616 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
620 while(recvline(fd, line, sizeof(line))) {
622 int n = sscanf(line, "%d %d %d", &code, &req, &len);
624 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
628 if(!recvdata(fd, data, len)) {
632 fwrite(data, len, 1, out);
638 static bool stop_tincd(void) {
639 if(!connect_tincd(true)) {
643 sendline(fd, "%d %d", CONTROL, REQ_STOP);
645 while(recvline(fd, line, sizeof(line))) {
646 // wait for tincd to close the connection...
657 static bool remove_service(void) {
658 SC_HANDLE manager = NULL;
659 SC_HANDLE service = NULL;
660 SERVICE_STATUS status = {0};
661 bool success = false;
663 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
666 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
670 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
673 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
674 success = stop_tincd();
676 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
682 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
683 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
685 fprintf(stderr, "%s service stopped\n", identname);
688 if(!DeleteService(service)) {
689 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
698 CloseServiceHandle(service);
702 CloseServiceHandle(manager);
706 fprintf(stderr, "%s service removed\n", identname);
713 bool connect_tincd(bool verbose) {
718 struct timeval tv = {0, 0};
720 if(select(fd + 1, &r, NULL, NULL, &tv)) {
721 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
729 pidfile_t *pidfile = read_pidfile();
733 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
740 strcpy(controlcookie, pidfile->cookie);
745 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
746 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
747 /* clean up the stale socket and pid file */
749 unlink(unixsocketname);
753 struct sockaddr_un sa = {
754 .sun_family = AF_UNIX,
757 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
758 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
762 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
764 fd = socket(AF_UNIX, SOCK_STREAM, 0);
768 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
774 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
776 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
785 struct addrinfo hints = {
786 .ai_family = AF_UNSPEC,
787 .ai_socktype = SOCK_STREAM,
788 .ai_protocol = IPPROTO_TCP,
792 struct addrinfo *res = NULL;
794 if(getaddrinfo(pidfile->host, pidfile->port, &hints, &res) || !res) {
796 fprintf(stderr, "Cannot resolve %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
803 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
807 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
814 unsigned long arg = 0;
816 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
818 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
822 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
824 fprintf(stderr, "Cannot connect to %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
838 static const int one = 1;
839 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
842 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
847 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
849 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
857 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
859 fprintf(stderr, "Could not fully establish control socket connection\n");
871 static int cmd_start(int argc, char *argv[]) {
872 if(connect_tincd(false)) {
874 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
876 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
883 char *slash = strrchr(program_name, '/');
887 if((c = strrchr(program_name, '\\')) > slash) {
894 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
896 c = xstrdup("tincd");
900 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
905 Windows has no real concept of an "argv array". A command line is just one string.
906 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
907 it uses quotes to handle spaces in arguments.
908 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
909 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
910 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
912 xasprintf(&arg0, "\"%s\"", arg0);
914 nargv[nargc++] = arg0;
916 for(int i = 1; i < optind; i++) {
917 nargv[nargc++] = orig_argv[i];
920 for(int i = 1; i < argc; i++) {
921 nargv[nargc++] = argv[i];
925 int status = spawnvp(_P_WAIT, c, nargv);
931 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
937 int pfd[2] = {-1, -1};
939 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
940 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
949 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
958 snprintf(buf, sizeof(buf), "%d", pfd[1]);
959 setenv("TINC_UMBILICAL", buf, true);
960 exit(execvp(c, nargv));
968 signal(SIGINT, SIG_IGN);
971 // Pass all log messages from the umbilical to stderr.
972 // A nul-byte right before closure means tincd started successfully.
977 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
978 failure = buf[len - 1];
984 if(write(2, buf, len) != len) {
985 // Nothing we can do about it.
995 // Make sure the child process is really gone.
997 pid_t result = waitpid(pid, &status, 0);
1000 signal(SIGINT, SIG_DFL);
1003 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1006 fprintf(stderr, "Error starting %s\n", c);
1011 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1015 static int cmd_stop(int argc, char *argv[]) {
1019 fprintf(stderr, "Too many arguments!\n");
1024 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1029 if(kill(pid, SIGTERM)) {
1030 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1034 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1035 waitpid(pid, NULL, 0);
1046 static int cmd_restart(int argc, char *argv[]) {
1048 return cmd_start(argc, argv);
1051 static int cmd_reload(int argc, char *argv[]) {
1055 fprintf(stderr, "Too many arguments!\n");
1059 if(!connect_tincd(true)) {
1063 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1065 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1066 fprintf(stderr, "Could not reload configuration.\n");
1074 static int dump_invitations(void) {
1075 char dname[PATH_MAX];
1076 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1077 DIR *dir = opendir(dname);
1080 if(errno == ENOENT) {
1081 fprintf(stderr, "No outstanding invitations.\n");
1085 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1093 while((ent = readdir(dir))) {
1094 char buf[MAX_STRING_SIZE];
1096 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1100 char fname[PATH_MAX];
1102 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1103 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1107 FILE *f = fopen(fname, "r");
1110 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1116 if(!fgets(buf, sizeof(buf), f)) {
1117 fprintf(stderr, "Invalid invitation file %s\n", fname);
1124 char *eol = buf + strlen(buf);
1126 while(strchr("\t \r\n", *--eol)) {
1130 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1131 fprintf(stderr, "Invalid invitation file %s\n", fname);
1136 printf("%s %s\n", ent->d_name, buf + 7);
1142 fprintf(stderr, "No outstanding invitations.\n");
1148 static int cmd_dump(int argc, char *argv[]) {
1149 bool only_reachable = false;
1151 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1152 if(strcasecmp(argv[2], "nodes")) {
1153 fprintf(stderr, "`reachable' only supported for nodes.\n");
1158 only_reachable = true;
1164 fprintf(stderr, "Invalid number of arguments.\n");
1169 if(!strcasecmp(argv[1], "invitations")) {
1170 return dump_invitations();
1173 if(!connect_tincd(true)) {
1179 if(!strcasecmp(argv[1], "nodes")) {
1180 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1181 } else if(!strcasecmp(argv[1], "edges")) {
1182 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1183 } else if(!strcasecmp(argv[1], "subnets")) {
1184 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1185 } else if(!strcasecmp(argv[1], "connections")) {
1186 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1187 } else if(!strcasecmp(argv[1], "graph")) {
1188 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1189 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1191 } else if(!strcasecmp(argv[1], "digraph")) {
1192 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1193 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1196 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1202 printf("graph {\n");
1203 } else if(do_graph == 2) {
1204 printf("digraph {\n");
1207 while(recvline(fd, line, sizeof(line))) {
1208 char node1[4096], node2[4096];
1209 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1212 if(do_graph && req == REQ_DUMP_NODES) {
1234 char local_host[4096];
1235 char local_port[4096];
1238 int cipher, digest, maclength, compression, distance, socket, weight;
1239 short int pmtu, minmtu, maxmtu;
1240 unsigned int options;
1241 node_status_t status;
1242 long int last_state_change;
1244 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1247 case REQ_DUMP_NODES: {
1248 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);
1251 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1256 const char *color = "black";
1258 if(!strcmp(host, "MYSELF")) {
1260 } else if(!status.reachable) {
1262 } else if(strcmp(via, node)) {
1264 } else if(!status.validkey) {
1266 } else if(minmtu > 0) {
1270 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1272 if(only_reachable && !status.reachable) {
1276 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,
1277 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);
1279 if(udp_ping_rtt != -1) {
1280 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1288 case REQ_DUMP_EDGES: {
1289 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);
1292 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1297 float w = 1.0f + 65536.0f / (float)weight;
1299 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1300 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1301 } else if(do_graph == 2) {
1302 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1305 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);
1310 case REQ_DUMP_SUBNETS: {
1311 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1314 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1318 printf("%s owner %s\n", strip_weight(subnet), node);
1322 case REQ_DUMP_CONNECTIONS: {
1323 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1326 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1330 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1335 fprintf(stderr, "Unable to parse dump from tincd.\n");
1340 fprintf(stderr, "Error receiving dump.\n");
1344 static int cmd_purge(int argc, char *argv[]) {
1348 fprintf(stderr, "Too many arguments!\n");
1352 if(!connect_tincd(true)) {
1356 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1358 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1359 fprintf(stderr, "Could not purge old information.\n");
1366 static int cmd_debug(int argc, char *argv[]) {
1368 fprintf(stderr, "Invalid number of arguments.\n");
1372 if(!connect_tincd(true)) {
1376 int debuglevel = atoi(argv[1]);
1379 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1381 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1382 fprintf(stderr, "Could not set debug level.\n");
1386 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1390 static int cmd_retry(int argc, char *argv[]) {
1394 fprintf(stderr, "Too many arguments!\n");
1398 if(!connect_tincd(true)) {
1402 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1404 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1405 fprintf(stderr, "Could not retry outgoing connections.\n");
1412 static int cmd_connect(int argc, char *argv[]) {
1414 fprintf(stderr, "Invalid number of arguments.\n");
1418 if(!check_id(argv[1])) {
1419 fprintf(stderr, "Invalid name for node.\n");
1423 if(!connect_tincd(true)) {
1427 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1429 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1430 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1437 static int cmd_disconnect(int argc, char *argv[]) {
1439 fprintf(stderr, "Invalid number of arguments.\n");
1443 if(!check_id(argv[1])) {
1444 fprintf(stderr, "Invalid name for node.\n");
1448 if(!connect_tincd(true)) {
1452 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1454 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1455 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1462 static int cmd_top(int argc, char *argv[]) {
1466 fprintf(stderr, "Too many arguments!\n");
1472 if(!connect_tincd(true)) {
1479 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1484 static int cmd_pcap(int argc, char *argv[]) {
1486 fprintf(stderr, "Too many arguments!\n");
1490 if(!connect_tincd(true)) {
1494 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1499 static void sigint_handler(int sig) {
1502 fprintf(stderr, "\n");
1503 shutdown(fd, SHUT_RDWR);
1507 static int cmd_log(int argc, char *argv[]) {
1509 fprintf(stderr, "Too many arguments!\n");
1513 if(!connect_tincd(true)) {
1518 signal(SIGINT, sigint_handler);
1521 log_control(fd, stdout, argc > 1 ? atoi(argv[1]) : DEBUG_UNSET);
1524 signal(SIGINT, SIG_DFL);
1532 static int cmd_pid(int argc, char *argv[]) {
1536 fprintf(stderr, "Too many arguments!\n");
1540 if(!connect_tincd(true) || !pid) {
1544 printf("%d\n", pid);
1548 size_t rstrip(char *value) {
1549 size_t len = strlen(value);
1551 while(len && strchr("\t\r\n ", value[len - 1])) {
1558 char *get_my_name(bool verbose) {
1559 FILE *f = fopen(tinc_conf, "r");
1563 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1572 while(fgets(buf, sizeof(buf), f)) {
1573 size_t len = strcspn(buf, "\t =");
1575 value += strspn(value, "\t ");
1579 value += strspn(value, "\t ");
1582 if(!rstrip(value)) {
1588 if(strcasecmp(buf, "Name")) {
1594 return replace_name(value);
1601 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1607 ecdsa_t *get_pubkey(FILE *f) {
1611 while(fgets(buf, sizeof(buf), f)) {
1612 size_t len = strcspn(buf, "\t =");
1614 value += strspn(value, "\t ");
1618 value += strspn(value, "\t ");
1621 if(!rstrip(value)) {
1627 if(strcasecmp(buf, "Ed25519PublicKey")) {
1632 return ecdsa_set_base64_public_key(value);
1639 const var_t variables[] = {
1640 /* Server configuration */
1641 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1642 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1643 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1644 {"BindToInterface", VAR_SERVER},
1645 {"Broadcast", VAR_SERVER | VAR_SAFE},
1646 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1647 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1648 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1649 {"Device", VAR_SERVER},
1650 {"DeviceStandby", VAR_SERVER},
1651 {"DeviceType", VAR_SERVER},
1652 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1653 {"Ed25519PrivateKeyFile", VAR_SERVER},
1654 {"ExperimentalProtocol", VAR_SERVER},
1655 {"Forwarding", VAR_SERVER},
1656 {"FWMark", VAR_SERVER},
1657 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1658 {"Hostnames", VAR_SERVER},
1659 {"IffOneQueue", VAR_SERVER},
1660 {"Interface", VAR_SERVER},
1661 {"InvitationExpire", VAR_SERVER},
1662 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1663 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1664 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1665 {"LogLevel", VAR_SERVER},
1666 {"MACExpire", VAR_SERVER | VAR_SAFE},
1667 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1668 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1669 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1670 {"Mode", VAR_SERVER | VAR_SAFE},
1671 {"Name", VAR_SERVER},
1672 {"PingInterval", VAR_SERVER | VAR_SAFE},
1673 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1674 {"PriorityInheritance", VAR_SERVER},
1675 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1676 {"PrivateKeyFile", VAR_SERVER},
1677 {"ProcessPriority", VAR_SERVER},
1678 {"Proxy", VAR_SERVER},
1679 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1680 {"ScriptsExtension", VAR_SERVER},
1681 {"ScriptsInterpreter", VAR_SERVER},
1682 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1683 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1684 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1685 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1686 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1687 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1688 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1689 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1690 {"UDPRcvBuf", VAR_SERVER},
1691 {"UDPSndBuf", VAR_SERVER},
1692 {"UPnP", VAR_SERVER},
1693 {"UPnPDiscoverWait", VAR_SERVER},
1694 {"UPnPRefreshPeriod", VAR_SERVER},
1695 {"VDEGroup", VAR_SERVER},
1696 {"VDEPort", VAR_SERVER},
1697 /* Host configuration */
1698 {"Address", VAR_HOST | VAR_MULTIPLE},
1699 {"Cipher", VAR_SERVER | VAR_HOST},
1700 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1701 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1702 {"Digest", VAR_SERVER | VAR_HOST},
1703 {"Ed25519PublicKey", VAR_HOST},
1704 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1705 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1706 {"MACLength", VAR_SERVER | VAR_HOST},
1707 {"PMTU", VAR_SERVER | VAR_HOST},
1708 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1710 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1711 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1712 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1713 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1714 {"Weight", VAR_HOST | VAR_SAFE},
1718 // Request actual port from tincd
1719 static bool read_actual_port(void) {
1720 pidfile_t *pidfile = read_pidfile();
1723 printf("%s\n", pidfile->port);
1727 fprintf(stderr, "Could not get port from the pidfile.\n");
1732 static int cmd_config(int argc, char *argv[]) {
1734 fprintf(stderr, "Invalid number of arguments.\n");
1738 if(strcasecmp(argv[0], "config")) {
1742 typedef enum { GET, DEL, SET, ADD } action_t;
1743 action_t action = GET;
1745 if(!strcasecmp(argv[1], "get")) {
1747 } else if(!strcasecmp(argv[1], "add")) {
1748 argv++, argc--, action = ADD;
1749 } else if(!strcasecmp(argv[1], "del")) {
1750 argv++, argc--, action = DEL;
1751 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1752 argv++, argc--, action = SET;
1756 fprintf(stderr, "Invalid number of arguments.\n");
1760 // Concatenate the rest of the command line
1761 strncpy(line, argv[1], sizeof(line) - 1);
1763 for(int i = 2; i < argc; i++) {
1764 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1765 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1768 // Liberal parsing into node name, variable name and value.
1774 len = strcspn(line, "\t =");
1776 value += strspn(value, "\t ");
1780 value += strspn(value, "\t ");
1784 variable = strchr(line, '.');
1794 fprintf(stderr, "No variable given.\n");
1798 if((action == SET || action == ADD) && !*value) {
1799 fprintf(stderr, "No value for variable given.\n");
1803 if(action == GET && *value) {
1807 // If port is requested, try reading it from the pidfile and fall back to configs if that fails
1808 if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
1812 /* Some simple checks. */
1814 bool warnonremove = false;
1816 for(int i = 0; variables[i].name; i++) {
1817 if(strcasecmp(variables[i].name, variable)) {
1822 variable = (char *)variables[i].name;
1824 if(!strcasecmp(variable, "Subnet") && *value) {
1827 if(!str2net(&s, value)) {
1828 fprintf(stderr, "Malformed subnet definition %s\n", value);
1832 if(!subnetcheck(s)) {
1833 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1838 /* Discourage use of obsolete variables. */
1840 if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
1842 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1844 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1849 /* Don't put server variables in host config files */
1851 if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
1853 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1855 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1860 /* Should this go into our own host config file? */
1862 if(!node && !(variables[i].type & VAR_SERVER)) {
1863 node = get_my_name(true);
1870 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1871 Turn on warnings when it seems variables might be removed unintentionally. */
1873 if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
1874 warnonremove = true;
1876 } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
1877 warnonremove = true;
1883 if(node && !check_id(node)) {
1884 fprintf(stderr, "Invalid name for node.\n");
1894 if(force || action == GET || action == DEL) {
1895 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1897 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1899 if(node && node != line) {
1907 // Open the right configuration file.
1908 char filename[PATH_MAX];
1911 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node) >= sizeof(filename)) {
1912 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1922 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1925 FILE *f = fopen(filename, "r");
1928 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1932 char tmpfile[PATH_MAX];
1936 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1937 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1941 tf = fopen(tmpfile, "w");
1944 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1950 // Copy the file, making modifications on the fly, unless we are just getting a value.
1954 bool removed = false;
1957 while(fgets(buf1, sizeof(buf1), f)) {
1958 buf1[sizeof(buf1) - 1] = 0;
1959 strncpy(buf2, buf1, sizeof(buf2));
1961 // Parse line in a simple way
1964 size_t len = strcspn(buf2, "\t =");
1965 bvalue = buf2 + len;
1966 bvalue += strspn(bvalue, "\t ");
1968 if(*bvalue == '=') {
1970 bvalue += strspn(bvalue, "\t ");
1977 if(!strcasecmp(buf2, variable)) {
1980 printf("%s\n", bvalue);
1981 } else if(action == DEL) {
1982 if(!*value || !strcasecmp(bvalue, value)) {
1986 } else if(action == SET) {
1987 // Warn if "set" was used for variables that can occur multiple times
1988 if(warnonremove && strcasecmp(bvalue, value)) {
1989 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1992 // Already set? Delete the rest...
1997 // Otherwise, replace.
1998 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1999 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2005 } else if(action == ADD) {
2006 // Check if we've already seen this variable with the same value
2007 if(!strcasecmp(bvalue, value)) {
2014 // Copy original line...
2015 if(fputs(buf1, tf) < 0) {
2016 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2020 // Add newline if it is missing...
2021 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2022 if(fputc('\n', tf) < 0) {
2023 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2030 // Make sure we read everything...
2031 if(ferror(f) || !feof(f)) {
2032 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2037 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2041 // Add new variable if necessary.
2042 if((action == ADD && !found) || (action == SET && !set)) {
2043 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2044 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2053 fprintf(stderr, "No matching configuration variables found.\n");
2058 // Make sure we wrote everything...
2060 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2064 // Could we find what we had to remove?
2065 if((action == GET || action == DEL) && !removed) {
2067 fprintf(stderr, "No configuration variables deleted.\n");
2071 // Replace the configuration file with the new one
2074 if(remove(filename)) {
2075 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2081 if(rename(tmpfile, filename)) {
2082 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2086 // Silently try notifying a running tincd of changes.
2087 if(connect_tincd(false)) {
2088 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2094 static bool try_bind(int port) {
2095 struct addrinfo *ai = NULL, *aip;
2096 struct addrinfo hint = {
2097 .ai_flags = AI_PASSIVE,
2098 .ai_family = AF_UNSPEC,
2099 .ai_socktype = SOCK_STREAM,
2100 .ai_protocol = IPPROTO_TCP,
2103 bool success = true;
2105 snprintf(portstr, sizeof(portstr), "%d", port);
2107 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2111 for(aip = ai; aip; aip = aip->ai_next) {
2112 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2119 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2132 int check_port(const char *name) {
2137 fprintf(stderr, "Warning: could not bind to port 655. ");
2139 for(int i = 0; i < 100; i++) {
2140 uint16_t port = 0x1000 + prng(0x8000);
2142 if(try_bind(port)) {
2143 char filename[PATH_MAX];
2144 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2145 FILE *f = fopen(filename, "a");
2148 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2149 fprintf(stderr, "Please change tinc's Port manually.\n");
2153 fprintf(f, "Port = %d\n", port);
2155 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2160 fprintf(stderr, "Please change tinc's Port manually.\n");
2164 static int cmd_init(int argc, char *argv[]) {
2165 if(!access(tinc_conf, F_OK)) {
2166 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2171 fprintf(stderr, "Too many arguments!\n");
2173 } else if(argc < 2) {
2176 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2178 if(!fgets(buf, sizeof(buf), stdin)) {
2179 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2183 size_t len = rstrip(buf);
2186 fprintf(stderr, "No name given!\n");
2192 fprintf(stderr, "No Name given!\n");
2196 name = strdup(argv[1]);
2199 fprintf(stderr, "No Name given!\n");
2204 if(!check_id(name)) {
2205 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2209 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2210 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2214 if(mkdir(confbase, 0777) && errno != EEXIST) {
2215 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2219 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2220 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2224 FILE *f = fopen(tinc_conf, "w");
2227 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2231 fprintf(f, "Name = %s\n", name);
2234 #ifndef DISABLE_LEGACY
2236 if(!rsa_keygen(2048, false)) {
2242 if(!ed25519_keygen(false)) {
2248 #ifndef HAVE_WINDOWS
2249 char filename[PATH_MAX];
2250 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2252 if(access(filename, F_OK)) {
2253 FILE *f = fopenmask(filename, "w", 0777);
2256 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2260 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");
2270 static int cmd_generate_keys(int argc, char *argv[]) {
2271 #ifdef DISABLE_LEGACY
2279 fprintf(stderr, "Too many arguments!\n");
2284 name = get_my_name(false);
2287 #ifndef DISABLE_LEGACY
2289 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2295 if(!ed25519_keygen(true)) {
2302 #ifndef DISABLE_LEGACY
2303 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2305 fprintf(stderr, "Too many arguments!\n");
2310 name = get_my_name(false);
2313 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2317 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2321 fprintf(stderr, "Too many arguments!\n");
2326 name = get_my_name(false);
2329 return !ed25519_keygen(true);
2332 static int cmd_help(int argc, char *argv[]) {
2340 static int cmd_version(int argc, char *argv[]) {
2344 fprintf(stderr, "Too many arguments!\n");
2352 static int cmd_info(int argc, char *argv[]) {
2354 fprintf(stderr, "Invalid number of arguments.\n");
2358 if(!connect_tincd(true)) {
2362 return info(fd, argv[1]);
2365 static const char *conffiles[] = {
2376 static int cmd_edit(int argc, char *argv[]) {
2378 fprintf(stderr, "Invalid number of arguments.\n");
2382 char filename[PATH_MAX] = "";
2384 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2385 for(int i = 0; conffiles[i]; i++) {
2386 if(!strcmp(argv[1], conffiles[i])) {
2387 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2396 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2397 char *dash = strchr(argv[1], '-');
2402 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2403 fprintf(stderr, "Invalid configuration filename.\n");
2410 #ifndef HAVE_WINDOWS
2411 const char *editor = getenv("VISUAL");
2414 editor = getenv("EDITOR");
2421 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2423 xasprintf(&command, "edit \"%s\"", filename);
2425 int result = system(command);
2432 // Silently try notifying a running tincd of changes.
2433 if(connect_tincd(false)) {
2434 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2440 static int export(const char *name, FILE *out) {
2441 char filename[PATH_MAX];
2442 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2443 FILE *in = fopen(filename, "r");
2446 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2450 fprintf(out, "Name = %s\n", name);
2453 while(fgets(buf, sizeof(buf), in)) {
2454 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2460 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2469 static int cmd_export(int argc, char *argv[]) {
2473 fprintf(stderr, "Too many arguments!\n");
2477 char *name = get_my_name(true);
2483 int result = export(name, stdout);
2493 static int cmd_export_all(int argc, char *argv[]) {
2497 fprintf(stderr, "Too many arguments!\n");
2501 DIR *dir = opendir(hosts_dir);
2504 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2512 while((ent = readdir(dir))) {
2513 if(!check_id(ent->d_name)) {
2520 printf("#---------------------------------------------------------------#\n");
2523 result |= export(ent->d_name, stdout);
2535 static int cmd_import(int argc, char *argv[]) {
2539 fprintf(stderr, "Too many arguments!\n");
2548 char filename[PATH_MAX] = "";
2550 bool firstline = true;
2552 while(fgets(buf, sizeof(buf), in)) {
2553 if(sscanf(buf, "Name = %4095s", name) == 1) {
2556 if(!check_id(name)) {
2557 fprintf(stderr, "Invalid Name in input!\n");
2565 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2566 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2570 if(!force && !access(filename, F_OK)) {
2571 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2576 out = fopen(filename, "w");
2579 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2585 } else if(firstline) {
2586 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2591 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2596 if(fputs(buf, out) < 0) {
2597 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2608 fprintf(stderr, "Imported %d host configuration files.\n", count);
2611 fprintf(stderr, "No host configuration files imported.\n");
2616 static int cmd_exchange(int argc, char *argv[]) {
2617 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2620 static int cmd_exchange_all(int argc, char *argv[]) {
2621 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2624 static int switch_network(char *name) {
2625 if(strcmp(name, ".")) {
2626 if(!check_netname(name, false)) {
2627 fprintf(stderr, "Invalid character in netname!\n");
2631 if(!check_netname(name, true)) {
2632 fprintf(stderr, "Warning: unsafe character in netname!\n");
2642 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2649 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2650 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2651 xasprintf(&prompt, "%s> ", identname);
2656 static int cmd_network(int argc, char *argv[]) {
2658 fprintf(stderr, "Too many arguments!\n");
2663 return switch_network(argv[1]);
2666 DIR *dir = opendir(confdir);
2669 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2675 while((ent = readdir(dir))) {
2676 if(*ent->d_name == '.') {
2680 if(!strcmp(ent->d_name, "tinc.conf")) {
2685 char fname[PATH_MAX];
2686 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2688 if(!access(fname, R_OK)) {
2689 printf("%s\n", ent->d_name);
2698 static int cmd_fsck(int argc, char *argv[]) {
2702 fprintf(stderr, "Too many arguments!\n");
2706 return fsck(orig_argv[0]);
2709 static void *readfile(FILE *in, size_t *len) {
2711 size_t bufsize = 4096;
2712 char *buf = xmalloc(bufsize);
2715 size_t read = fread(buf + count, 1, bufsize - count, in);
2723 if(count >= bufsize) {
2725 buf = xrealloc(buf, bufsize);
2736 static int cmd_sign(int argc, char *argv[]) {
2738 fprintf(stderr, "Too many arguments!\n");
2743 name = get_my_name(true);
2750 char fname[PATH_MAX];
2751 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2752 FILE *fp = fopen(fname, "r");
2755 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2759 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2762 fprintf(stderr, "Could not read private key from %s\n", fname);
2772 in = fopen(argv[1], "rb");
2775 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2784 char *data = readfile(in, &len);
2791 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2796 // Ensure we sign our name and current time as well
2797 long t = time(NULL);
2799 xasprintf(&trailer, " %s %ld", name, t);
2800 size_t trailer_len = strlen(trailer);
2802 data = xrealloc(data, len + trailer_len);
2803 memcpy(data + len, trailer, trailer_len);
2808 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2809 fprintf(stderr, "Error generating signature\n");
2815 b64encode_tinc(sig, sig, 64);
2818 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2819 fwrite(data, len, 1, stdout);
2825 static int cmd_verify(int argc, char *argv[]) {
2827 fprintf(stderr, "Not enough arguments!\n");
2832 fprintf(stderr, "Too many arguments!\n");
2836 char *node = argv[1];
2838 if(!strcmp(node, ".")) {
2840 name = get_my_name(true);
2848 } else if(!strcmp(node, "*")) {
2851 if(!check_id(node)) {
2852 fprintf(stderr, "Invalid node name\n");
2860 in = fopen(argv[2], "rb");
2863 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2871 char *data = readfile(in, &len);
2878 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2882 char *newline = memchr(data, '\n', len);
2884 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2885 fprintf(stderr, "Invalid input\n");
2891 size_t skip = newline - data;
2893 char signer[MAX_STRING_SIZE] = "";
2894 char sig[MAX_STRING_SIZE] = "";
2897 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2898 fprintf(stderr, "Invalid input\n");
2903 if(node && strcmp(node, signer)) {
2904 fprintf(stderr, "Signature is not made by %s\n", node);
2914 xasprintf(&trailer, " %s %ld", signer, t);
2915 size_t trailer_len = strlen(trailer);
2917 data = xrealloc(data, len + trailer_len);
2918 memcpy(data + len, trailer, trailer_len);
2921 newline = data + skip;
2923 char fname[PATH_MAX];
2924 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2925 FILE *fp = fopen(fname, "r");
2928 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2933 ecdsa_t *key = get_pubkey(fp);
2937 key = ecdsa_read_pem_public_key(fp);
2941 fprintf(stderr, "Could not read public key from %s\n", fname);
2949 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2950 fprintf(stderr, "Invalid signature\n");
2958 fwrite(newline, len - (newline - data), 1, stdout);
2964 static const struct {
2965 const char *command;
2966 int (*function)(int argc, char *argv[]);
2969 {"start", cmd_start, false},
2970 {"stop", cmd_stop, false},
2971 {"restart", cmd_restart, false},
2972 {"reload", cmd_reload, false},
2973 {"dump", cmd_dump, false},
2974 {"list", cmd_dump, false},
2975 {"purge", cmd_purge, false},
2976 {"debug", cmd_debug, false},
2977 {"retry", cmd_retry, false},
2978 {"connect", cmd_connect, false},
2979 {"disconnect", cmd_disconnect, false},
2980 {"top", cmd_top, false},
2981 {"pcap", cmd_pcap, false},
2982 {"log", cmd_log, false},
2983 {"pid", cmd_pid, false},
2984 {"config", cmd_config, true},
2985 {"add", cmd_config, false},
2986 {"del", cmd_config, false},
2987 {"get", cmd_config, false},
2988 {"set", cmd_config, false},
2989 {"init", cmd_init, false},
2990 {"generate-keys", cmd_generate_keys, false},
2991 #ifndef DISABLE_LEGACY
2992 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
2994 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
2995 {"help", cmd_help, false},
2996 {"version", cmd_version, false},
2997 {"info", cmd_info, false},
2998 {"edit", cmd_edit, false},
2999 {"export", cmd_export, false},
3000 {"export-all", cmd_export_all, false},
3001 {"import", cmd_import, false},
3002 {"exchange", cmd_exchange, false},
3003 {"exchange-all", cmd_exchange_all, false},
3004 {"invite", cmd_invite, false},
3005 {"join", cmd_join, false},
3006 {"network", cmd_network, false},
3007 {"fsck", cmd_fsck, false},
3008 {"sign", cmd_sign, false},
3009 {"verify", cmd_verify, false},
3010 {NULL, NULL, false},
3013 #ifdef HAVE_READLINE
3014 static char *complete_command(const char *text, int state) {
3023 while(commands[i].command) {
3024 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3025 return xstrdup(commands[i].command);
3034 static char *complete_dump(const char *text, int state) {
3035 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3045 if(!strncasecmp(matches[i], text, strlen(text))) {
3046 return xstrdup(matches[i]);
3055 static char *complete_config(const char *text, int state) {
3064 while(variables[i].name) {
3065 char *dot = strchr(text, '.');
3068 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3070 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3074 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3075 return xstrdup(variables[i].name);
3085 static char *complete_info(const char *text, int state) {
3091 if(!connect_tincd(false)) {
3095 // Check the list of nodes
3096 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3097 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3100 while(recvline(fd, line, sizeof(line))) {
3102 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3115 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3119 if(!strncmp(item, text, strlen(text))) {
3120 return xstrdup(strip_weight(item));
3127 static char *complete_nothing(const char *text, int state) {
3133 static char **completion(const char *text, int start, int end) {
3135 char **matches = NULL;
3138 matches = rl_completion_matches(text, complete_command);
3139 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3140 matches = rl_completion_matches(text, complete_dump);
3141 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3142 matches = rl_completion_matches(text, complete_config);
3143 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3144 matches = rl_completion_matches(text, complete_config);
3145 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3146 matches = rl_completion_matches(text, complete_config);
3147 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3148 matches = rl_completion_matches(text, complete_config);
3149 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3150 matches = rl_completion_matches(text, complete_info);
3157 static int cmd_shell(int argc, char *argv[]) {
3158 xasprintf(&prompt, "%s> ", identname);
3162 int maxargs = argc + 16;
3163 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3165 for(int i = 0; i < argc; i++) {
3169 #ifdef HAVE_READLINE
3170 rl_readline_name = "tinc";
3171 rl_basic_word_break_characters = "\t\n ";
3172 rl_completion_entry_function = complete_nothing;
3173 rl_attempted_completion_function = completion;
3174 rl_filename_completion_desired = 0;
3179 #ifdef HAVE_READLINE
3184 line = readline(prompt);
3185 copy = line ? xstrdup(line) : NULL;
3187 line = fgets(buf, sizeof(buf), stdin);
3193 fputs(prompt, stdout);
3196 line = fgets(buf, sizeof(buf), stdin);
3203 /* Ignore comments */
3212 char *p = line + strspn(line, " \t\n");
3213 char *next = strtok(p, " \t\n");
3216 if(nargc >= maxargs) {
3218 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3223 next = strtok(NULL, " \t\n");
3230 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3231 #ifdef HAVE_READLINE
3240 for(int i = 0; commands[i].command; i++) {
3241 if(!strcasecmp(nargv[argc], commands[i].command)) {
3242 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3248 #ifdef HAVE_READLINE
3257 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3262 #ifdef HAVE_READLINE
3274 static void cleanup(void) {
3280 static int run_command(int argc, char *argv[]) {
3281 if(optind >= argc) {
3282 return cmd_shell(argc, argv);
3285 for(int i = 0; commands[i].command; i++) {
3286 if(!strcasecmp(argv[optind], commands[i].command)) {
3287 return commands[i].function(argc - optind, argv + optind);
3291 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3296 int main(int argc, char *argv[]) {
3297 program_name = argv[0];
3299 tty = isatty(0) && isatty(1);
3301 if(!parse_options(argc, argv)) {
3306 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3307 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3321 static struct WSAData wsa_state;
3323 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3324 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3330 gettimeofday(&now, NULL);
3335 int result = run_command(argc, argv);