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 int cmd_init(int argc, char *argv[]) {
2193 if(!access(tinc_conf, F_OK)) {
2194 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2199 fprintf(stderr, "Too many arguments!\n");
2201 } else if(argc < 2) {
2204 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2206 if(!fgets(buf, sizeof(buf), stdin)) {
2207 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2211 size_t len = rstrip(buf);
2214 fprintf(stderr, "No name given!\n");
2220 fprintf(stderr, "No Name given!\n");
2224 name = strdup(argv[1]);
2227 fprintf(stderr, "No Name given!\n");
2232 if(!check_id(name)) {
2233 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2237 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2238 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2242 if(mkdir(confbase, 0777) && errno != EEXIST) {
2243 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2247 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2248 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2252 FILE *f = fopen(tinc_conf, "w");
2255 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2259 fprintf(f, "Name = %s\n", name);
2262 #ifndef DISABLE_LEGACY
2264 if(!rsa_keygen(2048, false)) {
2270 if(!ed25519_keygen(false)) {
2276 #ifndef HAVE_WINDOWS
2277 char filename[PATH_MAX];
2278 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2280 if(access(filename, F_OK)) {
2281 FILE *f = fopenmask(filename, "w", 0777);
2284 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2288 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");
2298 static int cmd_generate_keys(int argc, char *argv[]) {
2299 #ifdef DISABLE_LEGACY
2307 fprintf(stderr, "Too many arguments!\n");
2312 name = get_my_name(false);
2315 #ifndef DISABLE_LEGACY
2317 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2323 if(!ed25519_keygen(true)) {
2330 #ifndef DISABLE_LEGACY
2331 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2333 fprintf(stderr, "Too many arguments!\n");
2338 name = get_my_name(false);
2341 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2345 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2349 fprintf(stderr, "Too many arguments!\n");
2354 name = get_my_name(false);
2357 return !ed25519_keygen(true);
2360 static int cmd_help(int argc, char *argv[]) {
2368 static int cmd_version(int argc, char *argv[]) {
2372 fprintf(stderr, "Too many arguments!\n");
2380 static int cmd_info(int argc, char *argv[]) {
2382 fprintf(stderr, "Invalid number of arguments.\n");
2386 if(!connect_tincd(true)) {
2390 return info(fd, argv[1]);
2393 static const char *conffiles[] = {
2404 static int cmd_edit(int argc, char *argv[]) {
2406 fprintf(stderr, "Invalid number of arguments.\n");
2410 char filename[PATH_MAX] = "";
2412 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2413 for(int i = 0; conffiles[i]; i++) {
2414 if(!strcmp(argv[1], conffiles[i])) {
2415 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2424 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2425 char *dash = strchr(argv[1], '-');
2430 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2431 fprintf(stderr, "Invalid configuration filename.\n");
2438 #ifndef HAVE_WINDOWS
2439 const char *editor = getenv("VISUAL");
2442 editor = getenv("EDITOR");
2449 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2451 xasprintf(&command, "edit \"%s\"", filename);
2453 int result = system(command);
2460 // Silently try notifying a running tincd of changes.
2461 if(connect_tincd(false)) {
2462 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2468 static int export(const char *name, FILE *out) {
2469 char filename[PATH_MAX];
2470 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2471 FILE *in = fopen(filename, "r");
2474 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2478 fprintf(out, "Name = %s\n", name);
2481 while(fgets(buf, sizeof(buf), in)) {
2482 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2488 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2497 static int cmd_export(int argc, char *argv[]) {
2501 fprintf(stderr, "Too many arguments!\n");
2505 char *name = get_my_name(true);
2511 int result = export(name, stdout);
2521 static int cmd_export_all(int argc, char *argv[]) {
2525 fprintf(stderr, "Too many arguments!\n");
2529 DIR *dir = opendir(hosts_dir);
2532 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2540 while((ent = readdir(dir))) {
2541 if(!check_id(ent->d_name)) {
2548 printf("#---------------------------------------------------------------#\n");
2551 result |= export(ent->d_name, stdout);
2563 static int cmd_import(int argc, char *argv[]) {
2567 fprintf(stderr, "Too many arguments!\n");
2576 char filename[PATH_MAX] = "";
2578 bool firstline = true;
2580 while(fgets(buf, sizeof(buf), in)) {
2581 if(sscanf(buf, "Name = %4095s", name) == 1) {
2584 if(!check_id(name)) {
2585 fprintf(stderr, "Invalid Name in input!\n");
2593 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2594 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2598 if(!force && !access(filename, F_OK)) {
2599 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2604 out = fopen(filename, "w");
2607 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2613 } else if(firstline) {
2614 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2619 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2624 if(fputs(buf, out) < 0) {
2625 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2636 fprintf(stderr, "Imported %d host configuration files.\n", count);
2639 fprintf(stderr, "No host configuration files imported.\n");
2644 static int cmd_exchange(int argc, char *argv[]) {
2645 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2648 static int cmd_exchange_all(int argc, char *argv[]) {
2649 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2652 static int switch_network(char *name) {
2653 if(strcmp(name, ".")) {
2654 if(!check_netname(name, false)) {
2655 fprintf(stderr, "Invalid character in netname!\n");
2659 if(!check_netname(name, true)) {
2660 fprintf(stderr, "Warning: unsafe character in netname!\n");
2670 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2677 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2678 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2679 xasprintf(&prompt, "%s> ", identname);
2684 static int cmd_network(int argc, char *argv[]) {
2686 fprintf(stderr, "Too many arguments!\n");
2691 return switch_network(argv[1]);
2694 DIR *dir = opendir(confdir);
2697 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2703 while((ent = readdir(dir))) {
2704 if(*ent->d_name == '.') {
2708 if(!strcmp(ent->d_name, "tinc.conf")) {
2713 char fname[PATH_MAX];
2714 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2716 if(!access(fname, R_OK)) {
2717 printf("%s\n", ent->d_name);
2726 static int cmd_fsck(int argc, char *argv[]) {
2730 fprintf(stderr, "Too many arguments!\n");
2734 return fsck(orig_argv[0]);
2737 static void *readfile(FILE *in, size_t *len) {
2739 size_t bufsize = 4096;
2740 char *buf = xmalloc(bufsize);
2743 size_t read = fread(buf + count, 1, bufsize - count, in);
2751 if(count >= bufsize) {
2753 buf = xrealloc(buf, bufsize);
2764 static int cmd_sign(int argc, char *argv[]) {
2766 fprintf(stderr, "Too many arguments!\n");
2771 name = get_my_name(true);
2778 char fname[PATH_MAX];
2779 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2780 FILE *fp = fopen(fname, "r");
2783 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2787 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2790 fprintf(stderr, "Could not read private key from %s\n", fname);
2800 in = fopen(argv[1], "rb");
2803 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2812 char *data = readfile(in, &len);
2819 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2824 // Ensure we sign our name and current time as well
2825 long t = time(NULL);
2827 xasprintf(&trailer, " %s %ld", name, t);
2828 size_t trailer_len = strlen(trailer);
2830 data = xrealloc(data, len + trailer_len);
2831 memcpy(data + len, trailer, trailer_len);
2836 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2837 fprintf(stderr, "Error generating signature\n");
2843 b64encode_tinc(sig, sig, 64);
2846 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2847 fwrite(data, len, 1, stdout);
2853 static int cmd_verify(int argc, char *argv[]) {
2855 fprintf(stderr, "Not enough arguments!\n");
2860 fprintf(stderr, "Too many arguments!\n");
2864 char *node = argv[1];
2866 if(!strcmp(node, ".")) {
2868 name = get_my_name(true);
2876 } else if(!strcmp(node, "*")) {
2879 if(!check_id(node)) {
2880 fprintf(stderr, "Invalid node name\n");
2888 in = fopen(argv[2], "rb");
2891 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2899 char *data = readfile(in, &len);
2906 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2910 char *newline = memchr(data, '\n', len);
2912 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2913 fprintf(stderr, "Invalid input\n");
2919 size_t skip = newline - data;
2921 char signer[MAX_STRING_SIZE] = "";
2922 char sig[MAX_STRING_SIZE] = "";
2925 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2926 fprintf(stderr, "Invalid input\n");
2931 if(node && strcmp(node, signer)) {
2932 fprintf(stderr, "Signature is not made by %s\n", node);
2942 xasprintf(&trailer, " %s %ld", signer, t);
2943 size_t trailer_len = strlen(trailer);
2945 data = xrealloc(data, len + trailer_len);
2946 memcpy(data + len, trailer, trailer_len);
2949 newline = data + skip;
2951 char fname[PATH_MAX];
2952 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2953 FILE *fp = fopen(fname, "r");
2956 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2961 ecdsa_t *key = get_pubkey(fp);
2965 key = ecdsa_read_pem_public_key(fp);
2969 fprintf(stderr, "Could not read public key from %s\n", fname);
2977 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2978 fprintf(stderr, "Invalid signature\n");
2986 fwrite(newline, len - (newline - data), 1, stdout);
2992 static const struct {
2993 const char *command;
2994 int (*function)(int argc, char *argv[]);
2997 {"start", cmd_start, false},
2998 {"stop", cmd_stop, false},
2999 {"restart", cmd_restart, false},
3000 {"reload", cmd_reload, false},
3001 {"dump", cmd_dump, false},
3002 {"list", cmd_dump, false},
3003 {"purge", cmd_purge, false},
3004 {"debug", cmd_debug, false},
3005 {"retry", cmd_retry, false},
3006 {"connect", cmd_connect, false},
3007 {"disconnect", cmd_disconnect, false},
3008 {"top", cmd_top, false},
3009 {"pcap", cmd_pcap, false},
3010 {"log", cmd_log, false},
3011 {"pid", cmd_pid, false},
3012 {"config", cmd_config, true},
3013 {"add", cmd_config, false},
3014 {"del", cmd_config, false},
3015 {"get", cmd_config, false},
3016 {"set", cmd_config, false},
3017 {"init", cmd_init, false},
3018 {"generate-keys", cmd_generate_keys, false},
3019 #ifndef DISABLE_LEGACY
3020 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3022 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3023 {"help", cmd_help, false},
3024 {"version", cmd_version, false},
3025 {"info", cmd_info, false},
3026 {"edit", cmd_edit, false},
3027 {"export", cmd_export, false},
3028 {"export-all", cmd_export_all, false},
3029 {"import", cmd_import, false},
3030 {"exchange", cmd_exchange, false},
3031 {"exchange-all", cmd_exchange_all, false},
3032 {"invite", cmd_invite, false},
3033 {"join", cmd_join, false},
3034 {"network", cmd_network, false},
3035 {"fsck", cmd_fsck, false},
3036 {"sign", cmd_sign, false},
3037 {"verify", cmd_verify, false},
3038 {NULL, NULL, false},
3041 #ifdef HAVE_READLINE
3042 static char *complete_command(const char *text, int state) {
3051 while(commands[i].command) {
3052 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3053 return xstrdup(commands[i].command);
3062 static char *complete_dump(const char *text, int state) {
3063 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3073 if(!strncasecmp(matches[i], text, strlen(text))) {
3074 return xstrdup(matches[i]);
3083 static char *complete_config(const char *text, int state) {
3092 while(variables[i].name) {
3093 char *dot = strchr(text, '.');
3096 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3098 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3102 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3103 return xstrdup(variables[i].name);
3113 static char *complete_info(const char *text, int state) {
3119 if(!connect_tincd(false)) {
3123 // Check the list of nodes
3124 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3125 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3128 while(recvline(fd, line, sizeof(line))) {
3130 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3143 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3147 if(!strncmp(item, text, strlen(text))) {
3148 return xstrdup(strip_weight(item));
3155 static char *complete_nothing(const char *text, int state) {
3161 static char **completion(const char *text, int start, int end) {
3163 char **matches = NULL;
3166 matches = rl_completion_matches(text, complete_command);
3167 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3168 matches = rl_completion_matches(text, complete_dump);
3169 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3170 matches = rl_completion_matches(text, complete_config);
3171 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3172 matches = rl_completion_matches(text, complete_config);
3173 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3174 matches = rl_completion_matches(text, complete_config);
3175 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3176 matches = rl_completion_matches(text, complete_config);
3177 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3178 matches = rl_completion_matches(text, complete_info);
3185 static int cmd_shell(int argc, char *argv[]) {
3186 xasprintf(&prompt, "%s> ", identname);
3190 int maxargs = argc + 16;
3191 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3193 for(int i = 0; i < argc; i++) {
3197 #ifdef HAVE_READLINE
3198 rl_readline_name = "tinc";
3199 rl_basic_word_break_characters = "\t\n ";
3200 rl_completion_entry_function = complete_nothing;
3201 rl_attempted_completion_function = completion;
3202 rl_filename_completion_desired = 0;
3207 #ifdef HAVE_READLINE
3212 line = readline(prompt);
3213 copy = line ? xstrdup(line) : NULL;
3215 line = fgets(buf, sizeof(buf), stdin);
3221 fputs(prompt, stdout);
3224 line = fgets(buf, sizeof(buf), stdin);
3231 /* Ignore comments */
3240 char *p = line + strspn(line, " \t\n");
3241 char *next = strtok(p, " \t\n");
3244 if(nargc >= maxargs) {
3246 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3251 next = strtok(NULL, " \t\n");
3258 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3259 #ifdef HAVE_READLINE
3268 for(int i = 0; commands[i].command; i++) {
3269 if(!strcasecmp(nargv[argc], commands[i].command)) {
3270 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3276 #ifdef HAVE_READLINE
3285 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3290 #ifdef HAVE_READLINE
3302 static void cleanup(void) {
3308 static int run_command(int argc, char *argv[]) {
3309 if(optind >= argc) {
3310 return cmd_shell(argc, argv);
3313 for(int i = 0; commands[i].command; i++) {
3314 if(!strcasecmp(argv[optind], commands[i].command)) {
3315 return commands[i].function(argc - optind, argv + optind);
3319 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3324 int main(int argc, char *argv[]) {
3325 program_name = argv[0];
3327 tty = isatty(0) && isatty(1);
3329 if(!parse_options(argc, argv)) {
3334 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3335 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3349 static struct WSAData wsa_state;
3351 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3352 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3358 gettimeofday(&now, NULL);
3363 sandbox_set_level(SANDBOX_NORMAL);
3366 int result = run_command(argc, argv);