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"
49 #define MSG_NOSIGNAL 0
52 static char **orig_argv;
54 /* If nonzero, display usage information and exit. */
55 static bool show_help = false;
57 /* If nonzero, print the version on standard output and exit. */
58 static bool show_version = false;
60 static char *name = NULL;
61 static char controlcookie[1025];
62 char *tinc_conf = NULL;
63 char *hosts_dir = NULL;
66 // Horrible global variables...
75 bool confbasegiven = false;
76 char *scriptinterpreter = NULL;
77 static char defaultextension[] = "";
78 char *scriptextension = defaultextension;
84 typedef enum option_t {
90 OPT_CONFIG_FILE = 'c',
100 static struct option const long_options[] = {
101 {"batch", no_argument, NULL, OPT_BATCH},
102 {"config", required_argument, NULL, OPT_CONFIG_FILE},
103 {"net", required_argument, NULL, OPT_NETNAME},
104 {"help", no_argument, NULL, OPT_HELP},
105 {"version", no_argument, NULL, OPT_VERSION},
106 {"pidfile", required_argument, NULL, OPT_PIDFILE},
107 {"force", no_argument, NULL, OPT_FORCE},
111 static void version(void) {
113 "%s version %s (built %s %s, protocol %d.%d)\n"
121 #ifndef DISABLE_LEGACY
128 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
129 "See the AUTHORS file for a complete list.\n"
131 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
132 "and you are welcome to redistribute it under certain conditions;\n"
133 "see the file COPYING for details.\n",
134 PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
137 static void usage(bool status) {
139 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
142 "Usage: %s [options] command\n"
144 "Valid options are:\n"
145 " -b, --batch Don't ask for anything (non-interactive mode).\n"
146 " -c, --config=DIR Read configuration options from DIR.\n"
147 " -n, --net=NETNAME Connect to net NETNAME.\n"
148 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
149 " --force Force some commands to work despite warnings.\n"
150 " --help Display this help and exit.\n"
151 " --version Output version information and exit.\n"
153 "Valid commands are:\n"
154 " init [name] Create initial configuration files.\n"
155 " get VARIABLE Print current value of VARIABLE\n"
156 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
157 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
158 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
159 " start [tincd options] Start tincd.\n"
160 " stop Stop tincd.\n"
161 " restart [tincd options] Restart tincd.\n"
162 " reload Partially reload configuration of running tincd.\n"
163 " pid Show PID of currently running tincd.\n"
164 #ifdef DISABLE_LEGACY
165 " generate-keys Generate a new Ed25519 public/private key pair.\n"
167 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
168 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
170 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
171 " dump Dump a list of one of the following things:\n"
172 " [reachable] nodes - all known nodes in the VPN\n"
173 " edges - all known connections in the VPN\n"
174 " subnets - all known subnets in the VPN\n"
175 " connections - all meta connections with ourself\n"
176 " [di]graph - graph of the VPN in dotty format\n"
177 " invitations - outstanding invitations\n"
178 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
179 " purge Purge unreachable nodes\n"
180 " debug N Set debug level\n"
181 " retry Retry all outgoing connections\n"
182 " disconnect NODE Close meta connection with NODE\n"
184 " top Show real-time statistics\n"
186 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
187 " log [level] Dump log output [up to the specified level]\n"
188 " export Export host configuration of local node to standard output\n"
189 " export-all Export all host configuration files to standard output\n"
190 " import Import host configuration file(s) from standard input\n"
191 " exchange Same as export followed by import\n"
192 " exchange-all Same as export-all followed by import\n"
193 " invite NODE [...] Generate an invitation for NODE\n"
194 " join INVITATION Join a VPN using an INVITATION\n"
195 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
196 " fsck Check the configuration files for problems.\n"
197 " sign [FILE] Generate a signed version of a file.\n"
198 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
200 "Report bugs to tinc@tinc-vpn.org.\n",
205 static bool parse_options(int argc, char **argv) {
207 int option_index = 0;
209 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
210 switch((option_t) r) {
211 case OPT_LONG_OPTION:
223 case OPT_CONFIG_FILE:
225 confbase = xstrdup(optarg);
226 confbasegiven = true;
231 netname = xstrdup(optarg);
244 pidfilename = xstrdup(optarg);
256 if(!netname && (netname = getenv("NETNAME"))) {
257 netname = xstrdup(netname);
260 /* netname "." is special: a "top-level name" */
262 if(netname && (!*netname || !strcmp(netname, "."))) {
267 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
268 fprintf(stderr, "Invalid character in netname!\n");
276 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
278 char directory[PATH_MAX] = ".";
284 /* Check stdin and stdout */
286 /* Ask for a file and/or directory name. */
287 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
289 if(fgets(buf, sizeof(buf), stdin) == NULL) {
290 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
294 size_t len = strlen(buf);
307 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
310 if(filename[0] != '/') {
313 /* The directory is a relative path or a filename. */
314 if(!getcwd(directory, sizeof(directory))) {
315 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
319 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
320 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
332 disable_old_keys(filename, what);
334 /* Open it first to keep the inode busy */
336 r = fopenmask(filename, mode, perms);
339 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
347 Generate a public/private Ed25519 key pair, and ask for a file to store
350 static bool ed25519_keygen(bool ask) {
353 char fname[PATH_MAX];
355 fprintf(stderr, "Generating Ed25519 key pair:\n");
357 if(!(key = ecdsa_generate())) {
358 fprintf(stderr, "Error during key generation!\n");
361 fprintf(stderr, "Done.\n");
364 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
365 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
371 if(!ecdsa_write_pem_private_key(key, f)) {
372 fprintf(stderr, "Error writing private key!\n");
379 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
381 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
384 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
390 char *pubkey = ecdsa_get_base64_public_key(key);
391 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
409 #ifndef DISABLE_LEGACY
411 Generate a public/private RSA key pair, and ask for a file to store
414 static bool rsa_keygen(int bits, bool ask) {
417 char fname[PATH_MAX];
419 // Make sure the key size is a multiple of 8 bits.
422 // Make sure that a valid key size is used.
423 if(bits < 1024 || bits > 8192) {
424 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
426 } else if(bits < 2048) {
427 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
430 fprintf(stderr, "Generating %d bits keys:\n", bits);
432 if(!(key = rsa_generate(bits, 0x10001))) {
433 fprintf(stderr, "Error during key generation!\n");
436 fprintf(stderr, "Done.\n");
439 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
440 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
446 if(!rsa_write_pem_private_key(key, f)) {
447 fprintf(stderr, "Error writing private key!\n");
454 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
456 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
459 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
465 if(!rsa_write_pem_public_key(key, f)) {
466 fprintf(stderr, "Error writing public key!\n");
489 bool recvline(int fd, char *line, size_t len) {
490 char *newline = NULL;
496 while(!(newline = memchr(buffer, '\n', blen))) {
497 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
499 if(nrecv == -1 && sockerrno == EINTR) {
501 } else if(nrecv <= 0) {
508 if((size_t)(newline - buffer) >= len) {
512 len = newline - buffer;
514 memcpy(line, buffer, len);
516 memmove(buffer, newline + 1, blen - len - 1);
522 static bool recvdata(int fd, char *data, size_t len) {
524 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
526 if(nrecv == -1 && sockerrno == EINTR) {
528 } else if(nrecv <= 0) {
535 memcpy(data, buffer, len);
536 memmove(buffer, buffer + len, blen - len);
542 bool sendline(int fd, const char *format, ...) {
543 static char buffer[4096];
548 va_start(ap, format);
549 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
550 buffer[sizeof(buffer) - 1] = 0;
553 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
561 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
563 if(nsend == -1 && sockerrno == EINTR) {
565 } else if(nsend <= 0) {
576 static void pcap(int fd, FILE *out, uint32_t snaplen) {
577 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
585 uint32_t tz_accuracy;
592 snaplen ? snaplen : sizeof(data),
605 fwrite(&header, sizeof(header), 1, out);
610 while(recvline(fd, line, sizeof(line))) {
613 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
614 gettimeofday(&tv, NULL);
616 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
620 if(!recvdata(fd, data, len)) {
624 packet.tv_sec = tv.tv_sec;
625 packet.tv_usec = tv.tv_usec;
627 packet.origlen = len;
628 fwrite(&packet, sizeof(packet), 1, out);
629 fwrite(data, len, 1, out);
634 static void log_control(int fd, FILE *out, int level, bool use_color) {
635 sendline(fd, "%d %d %d %d", CONTROL, REQ_LOG, level, use_color);
640 while(recvline(fd, line, sizeof(line))) {
642 int n = sscanf(line, "%d %d %d", &code, &req, &len);
644 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
648 if(!recvdata(fd, data, len)) {
652 fwrite(data, len, 1, out);
658 static bool stop_tincd(void) {
659 if(!connect_tincd(true)) {
663 sendline(fd, "%d %d", CONTROL, REQ_STOP);
665 while(recvline(fd, line, sizeof(line))) {
666 // wait for tincd to close the connection...
677 static bool remove_service(void) {
678 SC_HANDLE manager = NULL;
679 SC_HANDLE service = NULL;
680 SERVICE_STATUS status = {0};
681 bool success = false;
683 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
686 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
690 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
693 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
694 success = stop_tincd();
696 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
702 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
703 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
705 fprintf(stderr, "%s service stopped\n", identname);
708 if(!DeleteService(service)) {
709 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
718 CloseServiceHandle(service);
722 CloseServiceHandle(manager);
726 fprintf(stderr, "%s service removed\n", identname);
733 bool connect_tincd(bool verbose) {
738 struct timeval tv = {0, 0};
740 if(select(fd + 1, &r, NULL, NULL, &tv)) {
741 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
749 pidfile_t *pidfile = read_pidfile();
753 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
760 strcpy(controlcookie, pidfile->cookie);
765 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
766 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
767 /* clean up the stale socket and pid file */
769 unlink(unixsocketname);
773 struct sockaddr_un sa = {
774 .sun_family = AF_UNIX,
777 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
778 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
782 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
784 fd = socket(AF_UNIX, SOCK_STREAM, 0);
788 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
794 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
796 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
805 struct addrinfo hints = {
806 .ai_family = AF_UNSPEC,
807 .ai_socktype = SOCK_STREAM,
808 .ai_protocol = IPPROTO_TCP,
812 struct addrinfo *res = NULL;
814 if(getaddrinfo(pidfile->host, pidfile->port, &hints, &res) || !res) {
816 fprintf(stderr, "Cannot resolve %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
823 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
827 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
834 unsigned long arg = 0;
836 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
838 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
842 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
844 fprintf(stderr, "Cannot connect to %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
858 static const int one = 1;
859 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
862 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
867 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
869 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
877 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
879 fprintf(stderr, "Could not fully establish control socket connection\n");
891 static int cmd_start(int argc, char *argv[]) {
892 if(connect_tincd(false)) {
894 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
896 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
903 char *slash = strrchr(program_name, '/');
907 if((c = strrchr(program_name, '\\')) > slash) {
914 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
916 c = xstrdup("tincd");
920 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
925 Windows has no real concept of an "argv array". A command line is just one string.
926 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
927 it uses quotes to handle spaces in arguments.
928 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
929 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
930 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
932 xasprintf(&arg0, "\"%s\"", arg0);
934 nargv[nargc++] = arg0;
936 for(int i = 1; i < optind; i++) {
937 nargv[nargc++] = orig_argv[i];
940 for(int i = 1; i < argc; i++) {
941 nargv[nargc++] = argv[i];
945 int status = spawnvp(_P_WAIT, c, nargv);
951 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
957 int pfd[2] = {-1, -1};
959 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
960 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
969 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
978 snprintf(buf, sizeof(buf), "%d %d", pfd[1], use_ansi_escapes(stderr));
979 setenv("TINC_UMBILICAL", buf, true);
980 exit(execvp(c, nargv));
988 signal(SIGINT, SIG_IGN);
991 // Pass all log messages from the umbilical to stderr.
992 // A nul-byte right before closure means tincd started successfully.
997 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
998 failure = buf[len - 1];
1004 if(write(2, buf, len) != len) {
1005 // Nothing we can do about it.
1015 // Make sure the child process is really gone.
1017 pid_t result = waitpid(pid, &status, 0);
1020 signal(SIGINT, SIG_DFL);
1023 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1026 fprintf(stderr, "Error starting %s\n", c);
1031 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1035 static int cmd_stop(int argc, char *argv[]) {
1039 fprintf(stderr, "Too many arguments!\n");
1044 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1049 if(kill(pid, SIGTERM)) {
1050 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1054 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1055 waitpid(pid, NULL, 0);
1066 static int cmd_restart(int argc, char *argv[]) {
1068 return cmd_start(argc, argv);
1071 static int cmd_reload(int argc, char *argv[]) {
1075 fprintf(stderr, "Too many arguments!\n");
1079 if(!connect_tincd(true)) {
1083 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1085 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1086 fprintf(stderr, "Could not reload configuration.\n");
1094 static int dump_invitations(void) {
1095 char dname[PATH_MAX];
1096 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1097 DIR *dir = opendir(dname);
1100 if(errno == ENOENT) {
1101 fprintf(stderr, "No outstanding invitations.\n");
1105 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1113 while((ent = readdir(dir))) {
1114 char buf[MAX_STRING_SIZE];
1116 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1120 char fname[PATH_MAX];
1122 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1123 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1127 FILE *f = fopen(fname, "r");
1130 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1136 if(!fgets(buf, sizeof(buf), f)) {
1137 fprintf(stderr, "Invalid invitation file %s\n", fname);
1144 char *eol = buf + strlen(buf);
1146 while(strchr("\t \r\n", *--eol)) {
1150 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1151 fprintf(stderr, "Invalid invitation file %s\n", fname);
1156 printf("%s %s\n", ent->d_name, buf + 7);
1162 fprintf(stderr, "No outstanding invitations.\n");
1168 static int cmd_dump(int argc, char *argv[]) {
1169 bool only_reachable = false;
1171 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1172 if(strcasecmp(argv[2], "nodes")) {
1173 fprintf(stderr, "`reachable' only supported for nodes.\n");
1178 only_reachable = true;
1184 fprintf(stderr, "Invalid number of arguments.\n");
1189 if(!strcasecmp(argv[1], "invitations")) {
1190 return dump_invitations();
1193 if(!connect_tincd(true)) {
1199 if(!strcasecmp(argv[1], "nodes")) {
1200 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1201 } else if(!strcasecmp(argv[1], "edges")) {
1202 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1203 } else if(!strcasecmp(argv[1], "subnets")) {
1204 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1205 } else if(!strcasecmp(argv[1], "connections")) {
1206 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1207 } else if(!strcasecmp(argv[1], "graph")) {
1208 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1209 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1211 } else if(!strcasecmp(argv[1], "digraph")) {
1212 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1213 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1216 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1222 printf("graph {\n");
1223 } else if(do_graph == 2) {
1224 printf("digraph {\n");
1227 while(recvline(fd, line, sizeof(line))) {
1228 char node1[4096], node2[4096];
1229 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1232 if(do_graph && req == REQ_DUMP_NODES) {
1254 char local_host[4096];
1255 char local_port[4096];
1258 int cipher, digest, maclength, compression, distance, socket, weight;
1259 short int pmtu, minmtu, maxmtu;
1260 unsigned int options;
1261 node_status_t status;
1262 long int last_state_change;
1264 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1267 case REQ_DUMP_NODES: {
1268 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);
1271 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1276 const char *color = "black";
1278 if(!strcmp(host, "MYSELF")) {
1280 } else if(!status.reachable) {
1282 } else if(strcmp(via, node)) {
1284 } else if(!status.validkey) {
1286 } else if(minmtu > 0) {
1290 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1292 if(only_reachable && !status.reachable) {
1296 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,
1297 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);
1299 if(udp_ping_rtt != -1) {
1300 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1308 case REQ_DUMP_EDGES: {
1309 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);
1312 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1317 float w = 1.0f + 65536.0f / (float)weight;
1319 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1320 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1321 } else if(do_graph == 2) {
1322 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1325 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);
1330 case REQ_DUMP_SUBNETS: {
1331 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1334 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1338 printf("%s owner %s\n", strip_weight(subnet), node);
1342 case REQ_DUMP_CONNECTIONS: {
1343 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1346 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1350 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1355 fprintf(stderr, "Unable to parse dump from tincd.\n");
1360 fprintf(stderr, "Error receiving dump.\n");
1364 static int cmd_purge(int argc, char *argv[]) {
1368 fprintf(stderr, "Too many arguments!\n");
1372 if(!connect_tincd(true)) {
1376 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1378 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1379 fprintf(stderr, "Could not purge old information.\n");
1386 static int cmd_debug(int argc, char *argv[]) {
1388 fprintf(stderr, "Invalid number of arguments.\n");
1392 if(!connect_tincd(true)) {
1396 int debuglevel = atoi(argv[1]);
1399 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1401 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1402 fprintf(stderr, "Could not set debug level.\n");
1406 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1410 static int cmd_retry(int argc, char *argv[]) {
1414 fprintf(stderr, "Too many arguments!\n");
1418 if(!connect_tincd(true)) {
1422 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1424 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1425 fprintf(stderr, "Could not retry outgoing connections.\n");
1432 static int cmd_connect(int argc, char *argv[]) {
1434 fprintf(stderr, "Invalid number of arguments.\n");
1438 if(!check_id(argv[1])) {
1439 fprintf(stderr, "Invalid name for node.\n");
1443 if(!connect_tincd(true)) {
1447 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1449 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1450 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1457 static int cmd_disconnect(int argc, char *argv[]) {
1459 fprintf(stderr, "Invalid number of arguments.\n");
1463 if(!check_id(argv[1])) {
1464 fprintf(stderr, "Invalid name for node.\n");
1468 if(!connect_tincd(true)) {
1472 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1474 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1475 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1482 static int cmd_top(int argc, char *argv[]) {
1486 fprintf(stderr, "Too many arguments!\n");
1492 if(!connect_tincd(true)) {
1499 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1504 static int cmd_pcap(int argc, char *argv[]) {
1506 fprintf(stderr, "Too many arguments!\n");
1510 if(!connect_tincd(true)) {
1514 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1519 static void sigint_handler(int sig) {
1522 if(write(2, "\n", 1) < 0) {
1523 // nothing we can do
1526 shutdown(fd, SHUT_RDWR);
1530 static int cmd_log(int argc, char *argv[]) {
1532 fprintf(stderr, "Too many arguments!\n");
1536 if(!connect_tincd(true)) {
1541 signal(SIGINT, sigint_handler);
1544 bool use_color = use_ansi_escapes(stdout);
1545 log_control(fd, stdout, argc > 1 ? atoi(argv[1]) : DEBUG_UNSET, use_color);
1548 signal(SIGINT, SIG_DFL);
1556 static int cmd_pid(int argc, char *argv[]) {
1560 fprintf(stderr, "Too many arguments!\n");
1564 if(!connect_tincd(true) || !pid) {
1568 printf("%d\n", pid);
1572 size_t rstrip(char *value) {
1573 size_t len = strlen(value);
1575 while(len && strchr("\t\r\n ", value[len - 1])) {
1582 char *get_my_name(bool verbose) {
1583 FILE *f = fopen(tinc_conf, "r");
1587 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1596 while(fgets(buf, sizeof(buf), f)) {
1597 size_t len = strcspn(buf, "\t =");
1599 value += strspn(value, "\t ");
1603 value += strspn(value, "\t ");
1606 if(!rstrip(value)) {
1612 if(strcasecmp(buf, "Name")) {
1618 return replace_name(value);
1625 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1631 static ecdsa_t *get_pubkey(FILE *f) ATTR_MALLOC ATTR_DEALLOCATOR(ecdsa_free);
1632 static ecdsa_t *get_pubkey(FILE *f) {
1636 while(fgets(buf, sizeof(buf), f)) {
1637 size_t len = strcspn(buf, "\t =");
1639 value += strspn(value, "\t ");
1643 value += strspn(value, "\t ");
1646 if(!rstrip(value)) {
1652 if(strcasecmp(buf, "Ed25519PublicKey")) {
1657 return ecdsa_set_base64_public_key(value);
1664 const var_t variables[] = {
1665 /* Server configuration */
1666 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1667 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1668 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1669 {"BindToInterface", VAR_SERVER},
1670 {"Broadcast", VAR_SERVER | VAR_SAFE},
1671 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1672 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1673 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1674 {"Device", VAR_SERVER},
1675 {"DeviceStandby", VAR_SERVER},
1676 {"DeviceType", VAR_SERVER},
1677 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1678 {"Ed25519PrivateKeyFile", VAR_SERVER},
1679 {"ExperimentalProtocol", VAR_SERVER},
1680 {"Forwarding", VAR_SERVER},
1681 {"FWMark", VAR_SERVER},
1682 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1683 {"Hostnames", VAR_SERVER},
1684 {"IffOneQueue", VAR_SERVER},
1685 {"Interface", VAR_SERVER},
1686 {"InvitationExpire", VAR_SERVER},
1687 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1688 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1689 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1690 {"LogLevel", VAR_SERVER},
1691 {"MACExpire", VAR_SERVER | VAR_SAFE},
1692 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1693 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1694 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1695 {"Mode", VAR_SERVER | VAR_SAFE},
1696 {"Name", VAR_SERVER},
1697 {"PingInterval", VAR_SERVER | VAR_SAFE},
1698 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1699 {"PriorityInheritance", VAR_SERVER},
1700 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1701 {"PrivateKeyFile", VAR_SERVER},
1702 {"ProcessPriority", VAR_SERVER},
1703 {"Proxy", VAR_SERVER},
1704 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1705 {"Sandbox", VAR_SERVER},
1706 {"ScriptsExtension", VAR_SERVER},
1707 {"ScriptsInterpreter", VAR_SERVER},
1708 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1709 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1710 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1711 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1712 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1713 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1714 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1715 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1716 {"UDPRcvBuf", VAR_SERVER},
1717 {"UDPSndBuf", VAR_SERVER},
1718 {"UPnP", VAR_SERVER},
1719 {"UPnPDiscoverWait", VAR_SERVER},
1720 {"UPnPRefreshPeriod", VAR_SERVER},
1721 {"VDEGroup", VAR_SERVER},
1722 {"VDEPort", VAR_SERVER},
1723 /* Host configuration */
1724 {"Address", VAR_HOST | VAR_MULTIPLE},
1725 {"Cipher", VAR_SERVER | VAR_HOST},
1726 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1727 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1728 {"Digest", VAR_SERVER | VAR_HOST},
1729 {"Ed25519PublicKey", VAR_HOST},
1730 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1731 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1732 {"MACLength", VAR_SERVER | VAR_HOST},
1733 {"PMTU", VAR_SERVER | VAR_HOST},
1734 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1736 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1737 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1738 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1739 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1740 {"Weight", VAR_HOST | VAR_SAFE},
1744 // Request actual port from tincd
1745 static bool read_actual_port(void) {
1746 pidfile_t *pidfile = read_pidfile();
1749 printf("%s\n", pidfile->port);
1753 fprintf(stderr, "Could not get port from the pidfile.\n");
1758 static int cmd_config(int argc, char *argv[]) {
1760 fprintf(stderr, "Invalid number of arguments.\n");
1764 if(strcasecmp(argv[0], "config")) {
1768 typedef enum { GET, DEL, SET, ADD } action_t;
1769 action_t action = GET;
1771 if(!strcasecmp(argv[1], "get")) {
1773 } else if(!strcasecmp(argv[1], "add")) {
1774 argv++, argc--, action = ADD;
1775 } else if(!strcasecmp(argv[1], "del")) {
1776 argv++, argc--, action = DEL;
1777 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1778 argv++, argc--, action = SET;
1782 fprintf(stderr, "Invalid number of arguments.\n");
1786 // Concatenate the rest of the command line
1787 strncpy(line, argv[1], sizeof(line) - 1);
1789 for(int i = 2; i < argc; i++) {
1790 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1791 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1794 // Liberal parsing into node name, variable name and value.
1800 len = strcspn(line, "\t =");
1802 value += strspn(value, "\t ");
1806 value += strspn(value, "\t ");
1810 variable = strchr(line, '.');
1820 fprintf(stderr, "No variable given.\n");
1824 if((action == SET || action == ADD) && !*value) {
1825 fprintf(stderr, "No value for variable given.\n");
1829 if(action == GET && *value) {
1833 // If port is requested, try reading it from the pidfile and fall back to configs if that fails
1834 if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
1838 /* Some simple checks. */
1840 bool warnonremove = false;
1842 for(int i = 0; variables[i].name; i++) {
1843 if(strcasecmp(variables[i].name, variable)) {
1848 variable = (char *)variables[i].name;
1850 if(!strcasecmp(variable, "Subnet") && *value) {
1853 if(!str2net(&s, value)) {
1854 fprintf(stderr, "Malformed subnet definition %s\n", value);
1858 if(!subnetcheck(s)) {
1859 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1864 /* Discourage use of obsolete variables. */
1866 if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
1868 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1870 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1875 /* Don't put server variables in host config files */
1877 if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
1879 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1881 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1886 /* Should this go into our own host config file? */
1888 if(!node && !(variables[i].type & VAR_SERVER)) {
1889 node = get_my_name(true);
1896 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1897 Turn on warnings when it seems variables might be removed unintentionally. */
1899 if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
1900 warnonremove = true;
1902 } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
1903 warnonremove = true;
1909 if(node && !check_id(node)) {
1910 fprintf(stderr, "Invalid name for node.\n");
1920 if(force || action == GET || action == DEL) {
1921 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1923 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1925 if(node && node != line) {
1933 // Open the right configuration file.
1934 char filename[PATH_MAX];
1937 size_t wrote = (size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1944 if(wrote >= sizeof(filename)) {
1945 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1950 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1953 FILE *f = fopen(filename, "r");
1956 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1960 char tmpfile[PATH_MAX];
1964 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1965 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1969 tf = fopen(tmpfile, "w");
1972 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1978 // Copy the file, making modifications on the fly, unless we are just getting a value.
1982 bool removed = false;
1985 while(fgets(buf1, sizeof(buf1), f)) {
1986 buf1[sizeof(buf1) - 1] = 0;
1987 strncpy(buf2, buf1, sizeof(buf2));
1989 // Parse line in a simple way
1992 size_t len = strcspn(buf2, "\t =");
1993 bvalue = buf2 + len;
1994 bvalue += strspn(bvalue, "\t ");
1996 if(*bvalue == '=') {
1998 bvalue += strspn(bvalue, "\t ");
2005 if(!strcasecmp(buf2, variable)) {
2008 printf("%s\n", bvalue);
2009 } else if(action == DEL) {
2010 if(!*value || !strcasecmp(bvalue, value)) {
2014 } else if(action == SET) {
2015 // Warn if "set" was used for variables that can occur multiple times
2016 if(warnonremove && strcasecmp(bvalue, value)) {
2017 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2020 // Already set? Delete the rest...
2025 // Otherwise, replace.
2026 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2027 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2033 } else if(action == ADD) {
2034 // Check if we've already seen this variable with the same value
2035 if(!strcasecmp(bvalue, value)) {
2042 // Copy original line...
2043 if(fputs(buf1, tf) < 0) {
2044 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2048 // Add newline if it is missing...
2049 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2050 if(fputc('\n', tf) < 0) {
2051 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2058 // Make sure we read everything...
2059 if(ferror(f) || !feof(f)) {
2060 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2065 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2069 // Add new variable if necessary.
2070 if((action == ADD && !found) || (action == SET && !set)) {
2071 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2072 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2081 fprintf(stderr, "No matching configuration variables found.\n");
2086 // Make sure we wrote everything...
2088 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2092 // Could we find what we had to remove?
2093 if((action == GET || action == DEL) && !removed) {
2095 fprintf(stderr, "No configuration variables deleted.\n");
2099 // Replace the configuration file with the new one
2102 if(remove(filename)) {
2103 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2109 if(rename(tmpfile, filename)) {
2110 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2114 // Silently try notifying a running tincd of changes.
2115 if(connect_tincd(false)) {
2116 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2122 static bool try_bind(int port) {
2123 struct addrinfo *ai = NULL, *aip;
2124 struct addrinfo hint = {
2125 .ai_flags = AI_PASSIVE,
2126 .ai_family = AF_UNSPEC,
2127 .ai_socktype = SOCK_STREAM,
2128 .ai_protocol = IPPROTO_TCP,
2131 bool success = true;
2133 snprintf(portstr, sizeof(portstr), "%d", port);
2135 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2139 for(aip = ai; aip; aip = aip->ai_next) {
2140 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2147 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2160 int check_port(const char *name) {
2165 fprintf(stderr, "Warning: could not bind to port 655. ");
2167 for(int i = 0; i < 100; i++) {
2168 uint16_t port = 0x1000 + prng(0x8000);
2170 if(try_bind(port)) {
2171 char filename[PATH_MAX];
2172 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2173 FILE *f = fopen(filename, "a");
2176 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2177 fprintf(stderr, "Please change tinc's Port manually.\n");
2181 fprintf(f, "Port = %d\n", port);
2183 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2188 fprintf(stderr, "Please change tinc's Port manually.\n");
2192 static bool makedir(const char *path, mode_t mode) {
2193 if(mkdir(path, mode) && errno != EEXIST) {
2194 fprintf(stderr, "Could not create directory %s: %s\n", path, strerror(errno));
2201 bool makedirs(tincd_dir_t dirs) {
2202 if(dirs & DIR_CONFBASE && !makedir(confbase, 0777)) {
2206 if(dirs & DIR_CONFDIR && !confbase_given && !makedir(confdir, 0755)) {
2210 if(dirs & DIR_HOSTS && !makedir(hosts_dir, 0777)) {
2214 char path[PATH_MAX];
2216 if(dirs & DIR_INVITATIONS) {
2217 snprintf(path, sizeof(path), "%s" SLASH "invitations", confbase);
2219 if(!makedir(path, 0700)) {
2224 if(dirs & DIR_CACHE) {
2225 snprintf(path, sizeof(path), "%s" SLASH "%s", confbase, "cache");
2227 if(!makedir(path, 0755)) {
2235 static int cmd_init(int argc, char *argv[]) {
2236 if(!access(tinc_conf, F_OK)) {
2237 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2242 fprintf(stderr, "Too many arguments!\n");
2244 } else if(argc < 2) {
2247 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2249 if(!fgets(buf, sizeof(buf), stdin)) {
2250 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2254 size_t len = rstrip(buf);
2257 fprintf(stderr, "No name given!\n");
2263 fprintf(stderr, "No Name given!\n");
2267 name = strdup(argv[1]);
2270 fprintf(stderr, "No Name given!\n");
2275 if(!check_id(name)) {
2276 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2280 if(!makedirs(DIR_HOSTS | DIR_CONFBASE | DIR_CONFDIR | DIR_CACHE)) {
2284 FILE *f = fopen(tinc_conf, "w");
2287 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2291 fprintf(f, "Name = %s\n", name);
2294 #ifndef DISABLE_LEGACY
2296 if(!rsa_keygen(2048, false)) {
2302 if(!ed25519_keygen(false)) {
2308 #ifndef HAVE_WINDOWS
2309 char filename[PATH_MAX];
2310 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2312 if(access(filename, F_OK)) {
2313 FILE *f = fopenmask(filename, "w", 0777);
2316 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2320 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");
2330 static int cmd_generate_keys(int argc, char *argv[]) {
2331 #ifdef DISABLE_LEGACY
2339 fprintf(stderr, "Too many arguments!\n");
2344 name = get_my_name(false);
2347 #ifndef DISABLE_LEGACY
2349 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2355 if(!ed25519_keygen(true)) {
2362 #ifndef DISABLE_LEGACY
2363 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2365 fprintf(stderr, "Too many arguments!\n");
2370 name = get_my_name(false);
2373 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2377 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2381 fprintf(stderr, "Too many arguments!\n");
2386 name = get_my_name(false);
2389 return !ed25519_keygen(true);
2392 static int cmd_help(int argc, char *argv[]) {
2400 static int cmd_version(int argc, char *argv[]) {
2404 fprintf(stderr, "Too many arguments!\n");
2412 static int cmd_info(int argc, char *argv[]) {
2414 fprintf(stderr, "Invalid number of arguments.\n");
2418 if(!connect_tincd(true)) {
2422 return info(fd, argv[1]);
2425 static const char *conffiles[] = {
2436 static int cmd_edit(int argc, char *argv[]) {
2438 fprintf(stderr, "Invalid number of arguments.\n");
2442 char filename[PATH_MAX] = "";
2444 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2445 for(int i = 0; conffiles[i]; i++) {
2446 if(!strcmp(argv[1], conffiles[i])) {
2447 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2456 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2457 char *dash = strchr(argv[1], '-');
2462 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2463 fprintf(stderr, "Invalid configuration filename.\n");
2470 #ifndef HAVE_WINDOWS
2471 const char *editor = getenv("VISUAL");
2474 editor = getenv("EDITOR");
2481 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2483 xasprintf(&command, "edit \"%s\"", filename);
2485 int result = system(command);
2492 // Silently try notifying a running tincd of changes.
2493 if(connect_tincd(false)) {
2494 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2500 static int export(const char *name, FILE *out) {
2501 char filename[PATH_MAX];
2502 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2503 FILE *in = fopen(filename, "r");
2506 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2510 fprintf(out, "Name = %s\n", name);
2513 while(fgets(buf, sizeof(buf), in)) {
2514 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2520 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2529 static int cmd_export(int argc, char *argv[]) {
2533 fprintf(stderr, "Too many arguments!\n");
2537 char *name = get_my_name(true);
2543 int result = export(name, stdout);
2553 static int cmd_export_all(int argc, char *argv[]) {
2557 fprintf(stderr, "Too many arguments!\n");
2561 DIR *dir = opendir(hosts_dir);
2564 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2572 while((ent = readdir(dir))) {
2573 if(!check_id(ent->d_name)) {
2580 printf("\n#---------------------------------------------------------------#\n");
2583 result |= export(ent->d_name, stdout);
2595 static int cmd_import(int argc, char *argv[]) {
2599 fprintf(stderr, "Too many arguments!\n");
2608 char filename[PATH_MAX] = "";
2610 bool firstline = true;
2612 while(fgets(buf, sizeof(buf), in)) {
2613 if(sscanf(buf, "Name = %4095s", name) == 1) {
2616 if(!check_id(name)) {
2617 fprintf(stderr, "Invalid Name in input!\n");
2625 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2626 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2630 if(!force && !access(filename, F_OK)) {
2631 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2636 out = fopen(filename, "w");
2639 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2645 } else if(firstline) {
2646 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2651 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2656 if(fputs(buf, out) < 0) {
2657 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2668 fprintf(stderr, "Imported %d host configuration files.\n", count);
2671 fprintf(stderr, "No host configuration files imported.\n");
2676 static int cmd_exchange(int argc, char *argv[]) {
2677 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2680 static int cmd_exchange_all(int argc, char *argv[]) {
2681 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2684 static int switch_network(char *name) {
2685 if(strcmp(name, ".")) {
2686 if(!check_netname(name, false)) {
2687 fprintf(stderr, "Invalid character in netname!\n");
2691 if(!check_netname(name, true)) {
2692 fprintf(stderr, "Warning: unsafe character in netname!\n");
2702 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2709 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2710 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2711 xasprintf(&prompt, "%s> ", identname);
2716 static int cmd_network(int argc, char *argv[]) {
2718 fprintf(stderr, "Too many arguments!\n");
2723 return switch_network(argv[1]);
2726 DIR *dir = opendir(confdir);
2729 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2735 while((ent = readdir(dir))) {
2736 if(*ent->d_name == '.') {
2740 if(!strcmp(ent->d_name, "tinc.conf")) {
2745 char fname[PATH_MAX];
2746 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2748 if(!access(fname, R_OK)) {
2749 printf("%s\n", ent->d_name);
2758 static int cmd_fsck(int argc, char *argv[]) {
2762 fprintf(stderr, "Too many arguments!\n");
2766 return fsck(orig_argv[0]);
2769 static void *readfile(FILE *in, size_t *len) {
2771 size_t bufsize = 4096;
2772 char *buf = xmalloc(bufsize);
2775 size_t read = fread(buf + count, 1, bufsize - count, in);
2783 if(count >= bufsize) {
2785 buf = xrealloc(buf, bufsize);
2796 static int cmd_sign(int argc, char *argv[]) {
2798 fprintf(stderr, "Too many arguments!\n");
2803 name = get_my_name(true);
2810 char fname[PATH_MAX];
2811 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2812 FILE *fp = fopen(fname, "r");
2815 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2819 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2822 fprintf(stderr, "Could not read private key from %s\n", fname);
2832 in = fopen(argv[1], "rb");
2835 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2844 char *data = readfile(in, &len);
2851 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2856 // Ensure we sign our name and current time as well
2857 long t = time(NULL);
2859 xasprintf(&trailer, " %s %ld", name, t);
2860 size_t trailer_len = strlen(trailer);
2862 data = xrealloc(data, len + trailer_len);
2863 memcpy(data + len, trailer, trailer_len);
2868 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2869 fprintf(stderr, "Error generating signature\n");
2875 b64encode_tinc(sig, sig, 64);
2878 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2879 fwrite(data, len, 1, stdout);
2885 static int cmd_verify(int argc, char *argv[]) {
2887 fprintf(stderr, "Not enough arguments!\n");
2892 fprintf(stderr, "Too many arguments!\n");
2896 char *node = argv[1];
2898 if(!strcmp(node, ".")) {
2900 name = get_my_name(true);
2908 } else if(!strcmp(node, "*")) {
2911 if(!check_id(node)) {
2912 fprintf(stderr, "Invalid node name\n");
2920 in = fopen(argv[2], "rb");
2923 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2931 char *data = readfile(in, &len);
2938 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2942 char *newline = memchr(data, '\n', len);
2944 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2945 fprintf(stderr, "Invalid input\n");
2951 size_t skip = newline - data;
2953 char signer[MAX_STRING_SIZE] = "";
2954 char sig[MAX_STRING_SIZE] = "";
2957 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2958 fprintf(stderr, "Invalid input\n");
2963 if(node && strcmp(node, signer)) {
2964 fprintf(stderr, "Signature is not made by %s\n", node);
2974 xasprintf(&trailer, " %s %ld", signer, t);
2975 size_t trailer_len = strlen(trailer);
2977 data = xrealloc(data, len + trailer_len);
2978 memcpy(data + len, trailer, trailer_len);
2981 newline = data + skip;
2983 char fname[PATH_MAX];
2984 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2985 FILE *fp = fopen(fname, "r");
2988 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2993 ecdsa_t *key = get_pubkey(fp);
2997 key = ecdsa_read_pem_public_key(fp);
3001 fprintf(stderr, "Could not read public key from %s\n", fname);
3009 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
3010 fprintf(stderr, "Invalid signature\n");
3018 fwrite(newline, len - (newline - data), 1, stdout);
3024 static const struct {
3025 const char *command;
3026 int (*function)(int argc, char *argv[]);
3029 {"start", cmd_start, false},
3030 {"stop", cmd_stop, false},
3031 {"restart", cmd_restart, false},
3032 {"reload", cmd_reload, false},
3033 {"dump", cmd_dump, false},
3034 {"list", cmd_dump, false},
3035 {"purge", cmd_purge, false},
3036 {"debug", cmd_debug, false},
3037 {"retry", cmd_retry, false},
3038 {"connect", cmd_connect, false},
3039 {"disconnect", cmd_disconnect, false},
3040 {"top", cmd_top, false},
3041 {"pcap", cmd_pcap, false},
3042 {"log", cmd_log, false},
3043 {"pid", cmd_pid, false},
3044 {"config", cmd_config, true},
3045 {"add", cmd_config, false},
3046 {"del", cmd_config, false},
3047 {"get", cmd_config, false},
3048 {"set", cmd_config, false},
3049 {"init", cmd_init, false},
3050 {"generate-keys", cmd_generate_keys, false},
3051 #ifndef DISABLE_LEGACY
3052 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3054 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3055 {"help", cmd_help, false},
3056 {"version", cmd_version, false},
3057 {"info", cmd_info, false},
3058 {"edit", cmd_edit, false},
3059 {"export", cmd_export, false},
3060 {"export-all", cmd_export_all, false},
3061 {"import", cmd_import, false},
3062 {"exchange", cmd_exchange, false},
3063 {"exchange-all", cmd_exchange_all, false},
3064 {"invite", cmd_invite, false},
3065 {"join", cmd_join, false},
3066 {"network", cmd_network, false},
3067 {"fsck", cmd_fsck, false},
3068 {"sign", cmd_sign, false},
3069 {"verify", cmd_verify, false},
3070 {NULL, NULL, false},
3073 #ifdef HAVE_READLINE
3074 static char *complete_command(const char *text, int state) {
3083 while(commands[i].command) {
3084 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3085 return xstrdup(commands[i].command);
3094 static char *complete_dump(const char *text, int state) {
3095 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3105 if(!strncasecmp(matches[i], text, strlen(text))) {
3106 return xstrdup(matches[i]);
3115 static char *complete_config(const char *text, int state) {
3124 while(variables[i].name) {
3125 char *dot = strchr(text, '.');
3128 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3130 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3134 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3135 return xstrdup(variables[i].name);
3145 static char *complete_info(const char *text, int state) {
3151 if(!connect_tincd(false)) {
3155 // Check the list of nodes
3156 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3157 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3160 while(recvline(fd, line, sizeof(line))) {
3162 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3175 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3179 if(!strncmp(item, text, strlen(text))) {
3180 return xstrdup(strip_weight(item));
3187 static char *complete_nothing(const char *text, int state) {
3193 static char **completion(const char *text, int start, int end) {
3195 char **matches = NULL;
3198 matches = rl_completion_matches(text, complete_command);
3199 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3200 matches = rl_completion_matches(text, complete_dump);
3201 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3202 matches = rl_completion_matches(text, complete_config);
3203 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3204 matches = rl_completion_matches(text, complete_config);
3205 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3206 matches = rl_completion_matches(text, complete_config);
3207 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3208 matches = rl_completion_matches(text, complete_config);
3209 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3210 matches = rl_completion_matches(text, complete_info);
3217 static int cmd_shell(int argc, char *argv[]) {
3218 xasprintf(&prompt, "%s> ", identname);
3222 int maxargs = argc + 16;
3223 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3225 for(int i = 0; i < argc; i++) {
3229 #ifdef HAVE_READLINE
3230 rl_readline_name = "tinc";
3231 rl_basic_word_break_characters = "\t\n ";
3232 rl_completion_entry_function = complete_nothing;
3233 rl_attempted_completion_function = completion;
3234 rl_filename_completion_desired = 0;
3239 #ifdef HAVE_READLINE
3244 line = readline(prompt);
3245 copy = line ? xstrdup(line) : NULL;
3247 line = fgets(buf, sizeof(buf), stdin);
3253 fputs(prompt, stdout);
3256 line = fgets(buf, sizeof(buf), stdin);
3263 /* Ignore comments */
3272 char *p = line + strspn(line, " \t\n");
3273 char *next = strtok(p, " \t\n");
3276 if(nargc >= maxargs) {
3278 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3283 next = strtok(NULL, " \t\n");
3290 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3291 #ifdef HAVE_READLINE
3300 for(int i = 0; commands[i].command; i++) {
3301 if(!strcasecmp(nargv[argc], commands[i].command)) {
3302 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3308 #ifdef HAVE_READLINE
3317 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3322 #ifdef HAVE_READLINE
3334 static void cleanup(void) {
3340 static int run_command(int argc, char *argv[]) {
3341 if(optind >= argc) {
3342 return cmd_shell(argc, argv);
3345 for(int i = 0; commands[i].command; i++) {
3346 if(!strcasecmp(argv[optind], commands[i].command)) {
3347 return commands[i].function(argc - optind, argv + optind);
3351 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3356 int main(int argc, char *argv[]) {
3357 program_name = argv[0];
3359 tty = isatty(0) && isatty(1);
3361 if(!parse_options(argc, argv)) {
3366 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3367 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3381 static struct WSAData wsa_state;
3383 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3384 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3390 gettimeofday(&now, NULL);
3395 sandbox_set_level(SANDBOX_NORMAL);
3398 int result = run_command(argc, argv);