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 fprintf(stderr, "\n");
1523 shutdown(fd, SHUT_RDWR);
1527 static int cmd_log(int argc, char *argv[]) {
1529 fprintf(stderr, "Too many arguments!\n");
1533 if(!connect_tincd(true)) {
1538 signal(SIGINT, sigint_handler);
1541 bool use_color = use_ansi_escapes(stdout);
1542 log_control(fd, stdout, argc > 1 ? atoi(argv[1]) : DEBUG_UNSET, use_color);
1545 signal(SIGINT, SIG_DFL);
1553 static int cmd_pid(int argc, char *argv[]) {
1557 fprintf(stderr, "Too many arguments!\n");
1561 if(!connect_tincd(true) || !pid) {
1565 printf("%d\n", pid);
1569 size_t rstrip(char *value) {
1570 size_t len = strlen(value);
1572 while(len && strchr("\t\r\n ", value[len - 1])) {
1579 char *get_my_name(bool verbose) {
1580 FILE *f = fopen(tinc_conf, "r");
1584 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1593 while(fgets(buf, sizeof(buf), f)) {
1594 size_t len = strcspn(buf, "\t =");
1596 value += strspn(value, "\t ");
1600 value += strspn(value, "\t ");
1603 if(!rstrip(value)) {
1609 if(strcasecmp(buf, "Name")) {
1615 return replace_name(value);
1622 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1628 static ecdsa_t *get_pubkey(FILE *f) ATTR_MALLOC ATTR_DEALLOCATOR(ecdsa_free);
1629 static ecdsa_t *get_pubkey(FILE *f) {
1633 while(fgets(buf, sizeof(buf), f)) {
1634 size_t len = strcspn(buf, "\t =");
1636 value += strspn(value, "\t ");
1640 value += strspn(value, "\t ");
1643 if(!rstrip(value)) {
1649 if(strcasecmp(buf, "Ed25519PublicKey")) {
1654 return ecdsa_set_base64_public_key(value);
1661 const var_t variables[] = {
1662 /* Server configuration */
1663 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1664 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1665 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1666 {"BindToInterface", VAR_SERVER},
1667 {"Broadcast", VAR_SERVER | VAR_SAFE},
1668 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1669 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1670 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1671 {"Device", VAR_SERVER},
1672 {"DeviceStandby", VAR_SERVER},
1673 {"DeviceType", VAR_SERVER},
1674 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1675 {"Ed25519PrivateKeyFile", VAR_SERVER},
1676 {"ExperimentalProtocol", VAR_SERVER},
1677 {"Forwarding", VAR_SERVER},
1678 {"FWMark", VAR_SERVER},
1679 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1680 {"Hostnames", VAR_SERVER},
1681 {"IffOneQueue", VAR_SERVER},
1682 {"Interface", VAR_SERVER},
1683 {"InvitationExpire", VAR_SERVER},
1684 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1685 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1686 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1687 {"LogLevel", VAR_SERVER},
1688 {"MACExpire", VAR_SERVER | VAR_SAFE},
1689 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1690 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1691 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1692 {"Mode", VAR_SERVER | VAR_SAFE},
1693 {"Name", VAR_SERVER},
1694 {"PingInterval", VAR_SERVER | VAR_SAFE},
1695 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1696 {"PriorityInheritance", VAR_SERVER},
1697 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1698 {"PrivateKeyFile", VAR_SERVER},
1699 {"ProcessPriority", VAR_SERVER},
1700 {"Proxy", VAR_SERVER},
1701 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1702 {"Sandbox", VAR_SERVER},
1703 {"ScriptsExtension", VAR_SERVER},
1704 {"ScriptsInterpreter", VAR_SERVER},
1705 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1706 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1707 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1708 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1709 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1710 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1711 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1712 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1713 {"UDPRcvBuf", VAR_SERVER},
1714 {"UDPSndBuf", VAR_SERVER},
1715 {"UPnP", VAR_SERVER},
1716 {"UPnPDiscoverWait", VAR_SERVER},
1717 {"UPnPRefreshPeriod", VAR_SERVER},
1718 {"VDEGroup", VAR_SERVER},
1719 {"VDEPort", VAR_SERVER},
1720 /* Host configuration */
1721 {"Address", VAR_HOST | VAR_MULTIPLE},
1722 {"Cipher", VAR_SERVER | VAR_HOST},
1723 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1724 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1725 {"Digest", VAR_SERVER | VAR_HOST},
1726 {"Ed25519PublicKey", VAR_HOST},
1727 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1728 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1729 {"MACLength", VAR_SERVER | VAR_HOST},
1730 {"PMTU", VAR_SERVER | VAR_HOST},
1731 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1733 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1734 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1735 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1736 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1737 {"Weight", VAR_HOST | VAR_SAFE},
1741 // Request actual port from tincd
1742 static bool read_actual_port(void) {
1743 pidfile_t *pidfile = read_pidfile();
1746 printf("%s\n", pidfile->port);
1750 fprintf(stderr, "Could not get port from the pidfile.\n");
1755 static int cmd_config(int argc, char *argv[]) {
1757 fprintf(stderr, "Invalid number of arguments.\n");
1761 if(strcasecmp(argv[0], "config")) {
1765 typedef enum { GET, DEL, SET, ADD } action_t;
1766 action_t action = GET;
1768 if(!strcasecmp(argv[1], "get")) {
1770 } else if(!strcasecmp(argv[1], "add")) {
1771 argv++, argc--, action = ADD;
1772 } else if(!strcasecmp(argv[1], "del")) {
1773 argv++, argc--, action = DEL;
1774 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1775 argv++, argc--, action = SET;
1779 fprintf(stderr, "Invalid number of arguments.\n");
1783 // Concatenate the rest of the command line
1784 strncpy(line, argv[1], sizeof(line) - 1);
1786 for(int i = 2; i < argc; i++) {
1787 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1788 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1791 // Liberal parsing into node name, variable name and value.
1797 len = strcspn(line, "\t =");
1799 value += strspn(value, "\t ");
1803 value += strspn(value, "\t ");
1807 variable = strchr(line, '.');
1817 fprintf(stderr, "No variable given.\n");
1821 if((action == SET || action == ADD) && !*value) {
1822 fprintf(stderr, "No value for variable given.\n");
1826 if(action == GET && *value) {
1830 // If port is requested, try reading it from the pidfile and fall back to configs if that fails
1831 if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
1835 /* Some simple checks. */
1837 bool warnonremove = false;
1839 for(int i = 0; variables[i].name; i++) {
1840 if(strcasecmp(variables[i].name, variable)) {
1845 variable = (char *)variables[i].name;
1847 if(!strcasecmp(variable, "Subnet") && *value) {
1850 if(!str2net(&s, value)) {
1851 fprintf(stderr, "Malformed subnet definition %s\n", value);
1855 if(!subnetcheck(s)) {
1856 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1861 /* Discourage use of obsolete variables. */
1863 if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
1865 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1867 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1872 /* Don't put server variables in host config files */
1874 if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
1876 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1878 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1883 /* Should this go into our own host config file? */
1885 if(!node && !(variables[i].type & VAR_SERVER)) {
1886 node = get_my_name(true);
1893 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1894 Turn on warnings when it seems variables might be removed unintentionally. */
1896 if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
1897 warnonremove = true;
1899 } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
1900 warnonremove = true;
1906 if(node && !check_id(node)) {
1907 fprintf(stderr, "Invalid name for node.\n");
1917 if(force || action == GET || action == DEL) {
1918 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1920 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1922 if(node && node != line) {
1930 // Open the right configuration file.
1931 char filename[PATH_MAX];
1934 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node) >= sizeof(filename)) {
1935 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1945 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1948 FILE *f = fopen(filename, "r");
1951 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1955 char tmpfile[PATH_MAX];
1959 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1960 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1964 tf = fopen(tmpfile, "w");
1967 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1973 // Copy the file, making modifications on the fly, unless we are just getting a value.
1977 bool removed = false;
1980 while(fgets(buf1, sizeof(buf1), f)) {
1981 buf1[sizeof(buf1) - 1] = 0;
1982 strncpy(buf2, buf1, sizeof(buf2));
1984 // Parse line in a simple way
1987 size_t len = strcspn(buf2, "\t =");
1988 bvalue = buf2 + len;
1989 bvalue += strspn(bvalue, "\t ");
1991 if(*bvalue == '=') {
1993 bvalue += strspn(bvalue, "\t ");
2000 if(!strcasecmp(buf2, variable)) {
2003 printf("%s\n", bvalue);
2004 } else if(action == DEL) {
2005 if(!*value || !strcasecmp(bvalue, value)) {
2009 } else if(action == SET) {
2010 // Warn if "set" was used for variables that can occur multiple times
2011 if(warnonremove && strcasecmp(bvalue, value)) {
2012 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2015 // Already set? Delete the rest...
2020 // Otherwise, replace.
2021 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2022 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2028 } else if(action == ADD) {
2029 // Check if we've already seen this variable with the same value
2030 if(!strcasecmp(bvalue, value)) {
2037 // Copy original line...
2038 if(fputs(buf1, tf) < 0) {
2039 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2043 // Add newline if it is missing...
2044 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2045 if(fputc('\n', tf) < 0) {
2046 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2053 // Make sure we read everything...
2054 if(ferror(f) || !feof(f)) {
2055 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2060 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2064 // Add new variable if necessary.
2065 if((action == ADD && !found) || (action == SET && !set)) {
2066 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2067 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2076 fprintf(stderr, "No matching configuration variables found.\n");
2081 // Make sure we wrote everything...
2083 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2087 // Could we find what we had to remove?
2088 if((action == GET || action == DEL) && !removed) {
2090 fprintf(stderr, "No configuration variables deleted.\n");
2094 // Replace the configuration file with the new one
2097 if(remove(filename)) {
2098 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2104 if(rename(tmpfile, filename)) {
2105 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2109 // Silently try notifying a running tincd of changes.
2110 if(connect_tincd(false)) {
2111 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2117 static bool try_bind(int port) {
2118 struct addrinfo *ai = NULL, *aip;
2119 struct addrinfo hint = {
2120 .ai_flags = AI_PASSIVE,
2121 .ai_family = AF_UNSPEC,
2122 .ai_socktype = SOCK_STREAM,
2123 .ai_protocol = IPPROTO_TCP,
2126 bool success = true;
2128 snprintf(portstr, sizeof(portstr), "%d", port);
2130 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2134 for(aip = ai; aip; aip = aip->ai_next) {
2135 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2142 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2155 int check_port(const char *name) {
2160 fprintf(stderr, "Warning: could not bind to port 655. ");
2162 for(int i = 0; i < 100; i++) {
2163 uint16_t port = 0x1000 + prng(0x8000);
2165 if(try_bind(port)) {
2166 char filename[PATH_MAX];
2167 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2168 FILE *f = fopen(filename, "a");
2171 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2172 fprintf(stderr, "Please change tinc's Port manually.\n");
2176 fprintf(f, "Port = %d\n", port);
2178 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2183 fprintf(stderr, "Please change tinc's Port manually.\n");
2187 static int cmd_init(int argc, char *argv[]) {
2188 if(!access(tinc_conf, F_OK)) {
2189 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2194 fprintf(stderr, "Too many arguments!\n");
2196 } else if(argc < 2) {
2199 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2201 if(!fgets(buf, sizeof(buf), stdin)) {
2202 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2206 size_t len = rstrip(buf);
2209 fprintf(stderr, "No name given!\n");
2215 fprintf(stderr, "No Name given!\n");
2219 name = strdup(argv[1]);
2222 fprintf(stderr, "No Name given!\n");
2227 if(!check_id(name)) {
2228 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2232 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2233 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2237 if(mkdir(confbase, 0777) && errno != EEXIST) {
2238 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2242 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2243 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2247 FILE *f = fopen(tinc_conf, "w");
2250 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2254 fprintf(f, "Name = %s\n", name);
2257 #ifndef DISABLE_LEGACY
2259 if(!rsa_keygen(2048, false)) {
2265 if(!ed25519_keygen(false)) {
2271 #ifndef HAVE_WINDOWS
2272 char filename[PATH_MAX];
2273 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2275 if(access(filename, F_OK)) {
2276 FILE *f = fopenmask(filename, "w", 0777);
2279 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2283 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");
2293 static int cmd_generate_keys(int argc, char *argv[]) {
2294 #ifdef DISABLE_LEGACY
2302 fprintf(stderr, "Too many arguments!\n");
2307 name = get_my_name(false);
2310 #ifndef DISABLE_LEGACY
2312 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2318 if(!ed25519_keygen(true)) {
2325 #ifndef DISABLE_LEGACY
2326 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2328 fprintf(stderr, "Too many arguments!\n");
2333 name = get_my_name(false);
2336 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2340 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2344 fprintf(stderr, "Too many arguments!\n");
2349 name = get_my_name(false);
2352 return !ed25519_keygen(true);
2355 static int cmd_help(int argc, char *argv[]) {
2363 static int cmd_version(int argc, char *argv[]) {
2367 fprintf(stderr, "Too many arguments!\n");
2375 static int cmd_info(int argc, char *argv[]) {
2377 fprintf(stderr, "Invalid number of arguments.\n");
2381 if(!connect_tincd(true)) {
2385 return info(fd, argv[1]);
2388 static const char *conffiles[] = {
2399 static int cmd_edit(int argc, char *argv[]) {
2401 fprintf(stderr, "Invalid number of arguments.\n");
2405 char filename[PATH_MAX] = "";
2407 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2408 for(int i = 0; conffiles[i]; i++) {
2409 if(!strcmp(argv[1], conffiles[i])) {
2410 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2419 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2420 char *dash = strchr(argv[1], '-');
2425 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2426 fprintf(stderr, "Invalid configuration filename.\n");
2433 #ifndef HAVE_WINDOWS
2434 const char *editor = getenv("VISUAL");
2437 editor = getenv("EDITOR");
2444 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2446 xasprintf(&command, "edit \"%s\"", filename);
2448 int result = system(command);
2455 // Silently try notifying a running tincd of changes.
2456 if(connect_tincd(false)) {
2457 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2463 static int export(const char *name, FILE *out) {
2464 char filename[PATH_MAX];
2465 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2466 FILE *in = fopen(filename, "r");
2469 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2473 fprintf(out, "Name = %s\n", name);
2476 while(fgets(buf, sizeof(buf), in)) {
2477 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2483 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2492 static int cmd_export(int argc, char *argv[]) {
2496 fprintf(stderr, "Too many arguments!\n");
2500 char *name = get_my_name(true);
2506 int result = export(name, stdout);
2516 static int cmd_export_all(int argc, char *argv[]) {
2520 fprintf(stderr, "Too many arguments!\n");
2524 DIR *dir = opendir(hosts_dir);
2527 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2535 while((ent = readdir(dir))) {
2536 if(!check_id(ent->d_name)) {
2543 printf("#---------------------------------------------------------------#\n");
2546 result |= export(ent->d_name, stdout);
2558 static int cmd_import(int argc, char *argv[]) {
2562 fprintf(stderr, "Too many arguments!\n");
2571 char filename[PATH_MAX] = "";
2573 bool firstline = true;
2575 while(fgets(buf, sizeof(buf), in)) {
2576 if(sscanf(buf, "Name = %4095s", name) == 1) {
2579 if(!check_id(name)) {
2580 fprintf(stderr, "Invalid Name in input!\n");
2588 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2589 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2593 if(!force && !access(filename, F_OK)) {
2594 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2599 out = fopen(filename, "w");
2602 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2608 } else if(firstline) {
2609 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2614 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2619 if(fputs(buf, out) < 0) {
2620 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2631 fprintf(stderr, "Imported %d host configuration files.\n", count);
2634 fprintf(stderr, "No host configuration files imported.\n");
2639 static int cmd_exchange(int argc, char *argv[]) {
2640 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2643 static int cmd_exchange_all(int argc, char *argv[]) {
2644 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2647 static int switch_network(char *name) {
2648 if(strcmp(name, ".")) {
2649 if(!check_netname(name, false)) {
2650 fprintf(stderr, "Invalid character in netname!\n");
2654 if(!check_netname(name, true)) {
2655 fprintf(stderr, "Warning: unsafe character in netname!\n");
2665 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2672 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2673 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2674 xasprintf(&prompt, "%s> ", identname);
2679 static int cmd_network(int argc, char *argv[]) {
2681 fprintf(stderr, "Too many arguments!\n");
2686 return switch_network(argv[1]);
2689 DIR *dir = opendir(confdir);
2692 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2698 while((ent = readdir(dir))) {
2699 if(*ent->d_name == '.') {
2703 if(!strcmp(ent->d_name, "tinc.conf")) {
2708 char fname[PATH_MAX];
2709 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2711 if(!access(fname, R_OK)) {
2712 printf("%s\n", ent->d_name);
2721 static int cmd_fsck(int argc, char *argv[]) {
2725 fprintf(stderr, "Too many arguments!\n");
2729 return fsck(orig_argv[0]);
2732 static void *readfile(FILE *in, size_t *len) {
2734 size_t bufsize = 4096;
2735 char *buf = xmalloc(bufsize);
2738 size_t read = fread(buf + count, 1, bufsize - count, in);
2746 if(count >= bufsize) {
2748 buf = xrealloc(buf, bufsize);
2759 static int cmd_sign(int argc, char *argv[]) {
2761 fprintf(stderr, "Too many arguments!\n");
2766 name = get_my_name(true);
2773 char fname[PATH_MAX];
2774 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2775 FILE *fp = fopen(fname, "r");
2778 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2782 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2785 fprintf(stderr, "Could not read private key from %s\n", fname);
2795 in = fopen(argv[1], "rb");
2798 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2807 char *data = readfile(in, &len);
2814 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2819 // Ensure we sign our name and current time as well
2820 long t = time(NULL);
2822 xasprintf(&trailer, " %s %ld", name, t);
2823 size_t trailer_len = strlen(trailer);
2825 data = xrealloc(data, len + trailer_len);
2826 memcpy(data + len, trailer, trailer_len);
2831 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2832 fprintf(stderr, "Error generating signature\n");
2838 b64encode_tinc(sig, sig, 64);
2841 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2842 fwrite(data, len, 1, stdout);
2848 static int cmd_verify(int argc, char *argv[]) {
2850 fprintf(stderr, "Not enough arguments!\n");
2855 fprintf(stderr, "Too many arguments!\n");
2859 char *node = argv[1];
2861 if(!strcmp(node, ".")) {
2863 name = get_my_name(true);
2871 } else if(!strcmp(node, "*")) {
2874 if(!check_id(node)) {
2875 fprintf(stderr, "Invalid node name\n");
2883 in = fopen(argv[2], "rb");
2886 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2894 char *data = readfile(in, &len);
2901 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2905 char *newline = memchr(data, '\n', len);
2907 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2908 fprintf(stderr, "Invalid input\n");
2914 size_t skip = newline - data;
2916 char signer[MAX_STRING_SIZE] = "";
2917 char sig[MAX_STRING_SIZE] = "";
2920 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2921 fprintf(stderr, "Invalid input\n");
2926 if(node && strcmp(node, signer)) {
2927 fprintf(stderr, "Signature is not made by %s\n", node);
2937 xasprintf(&trailer, " %s %ld", signer, t);
2938 size_t trailer_len = strlen(trailer);
2940 data = xrealloc(data, len + trailer_len);
2941 memcpy(data + len, trailer, trailer_len);
2944 newline = data + skip;
2946 char fname[PATH_MAX];
2947 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2948 FILE *fp = fopen(fname, "r");
2951 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2956 ecdsa_t *key = get_pubkey(fp);
2960 key = ecdsa_read_pem_public_key(fp);
2964 fprintf(stderr, "Could not read public key from %s\n", fname);
2972 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2973 fprintf(stderr, "Invalid signature\n");
2981 fwrite(newline, len - (newline - data), 1, stdout);
2987 static const struct {
2988 const char *command;
2989 int (*function)(int argc, char *argv[]);
2992 {"start", cmd_start, false},
2993 {"stop", cmd_stop, false},
2994 {"restart", cmd_restart, false},
2995 {"reload", cmd_reload, false},
2996 {"dump", cmd_dump, false},
2997 {"list", cmd_dump, false},
2998 {"purge", cmd_purge, false},
2999 {"debug", cmd_debug, false},
3000 {"retry", cmd_retry, false},
3001 {"connect", cmd_connect, false},
3002 {"disconnect", cmd_disconnect, false},
3003 {"top", cmd_top, false},
3004 {"pcap", cmd_pcap, false},
3005 {"log", cmd_log, false},
3006 {"pid", cmd_pid, false},
3007 {"config", cmd_config, true},
3008 {"add", cmd_config, false},
3009 {"del", cmd_config, false},
3010 {"get", cmd_config, false},
3011 {"set", cmd_config, false},
3012 {"init", cmd_init, false},
3013 {"generate-keys", cmd_generate_keys, false},
3014 #ifndef DISABLE_LEGACY
3015 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3017 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3018 {"help", cmd_help, false},
3019 {"version", cmd_version, false},
3020 {"info", cmd_info, false},
3021 {"edit", cmd_edit, false},
3022 {"export", cmd_export, false},
3023 {"export-all", cmd_export_all, false},
3024 {"import", cmd_import, false},
3025 {"exchange", cmd_exchange, false},
3026 {"exchange-all", cmd_exchange_all, false},
3027 {"invite", cmd_invite, false},
3028 {"join", cmd_join, false},
3029 {"network", cmd_network, false},
3030 {"fsck", cmd_fsck, false},
3031 {"sign", cmd_sign, false},
3032 {"verify", cmd_verify, false},
3033 {NULL, NULL, false},
3036 #ifdef HAVE_READLINE
3037 static char *complete_command(const char *text, int state) {
3046 while(commands[i].command) {
3047 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3048 return xstrdup(commands[i].command);
3057 static char *complete_dump(const char *text, int state) {
3058 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3068 if(!strncasecmp(matches[i], text, strlen(text))) {
3069 return xstrdup(matches[i]);
3078 static char *complete_config(const char *text, int state) {
3087 while(variables[i].name) {
3088 char *dot = strchr(text, '.');
3091 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3093 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3097 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3098 return xstrdup(variables[i].name);
3108 static char *complete_info(const char *text, int state) {
3114 if(!connect_tincd(false)) {
3118 // Check the list of nodes
3119 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3120 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3123 while(recvline(fd, line, sizeof(line))) {
3125 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3138 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3142 if(!strncmp(item, text, strlen(text))) {
3143 return xstrdup(strip_weight(item));
3150 static char *complete_nothing(const char *text, int state) {
3156 static char **completion(const char *text, int start, int end) {
3158 char **matches = NULL;
3161 matches = rl_completion_matches(text, complete_command);
3162 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3163 matches = rl_completion_matches(text, complete_dump);
3164 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3165 matches = rl_completion_matches(text, complete_config);
3166 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3167 matches = rl_completion_matches(text, complete_config);
3168 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3169 matches = rl_completion_matches(text, complete_config);
3170 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3171 matches = rl_completion_matches(text, complete_config);
3172 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3173 matches = rl_completion_matches(text, complete_info);
3180 static int cmd_shell(int argc, char *argv[]) {
3181 xasprintf(&prompt, "%s> ", identname);
3185 int maxargs = argc + 16;
3186 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3188 for(int i = 0; i < argc; i++) {
3192 #ifdef HAVE_READLINE
3193 rl_readline_name = "tinc";
3194 rl_basic_word_break_characters = "\t\n ";
3195 rl_completion_entry_function = complete_nothing;
3196 rl_attempted_completion_function = completion;
3197 rl_filename_completion_desired = 0;
3202 #ifdef HAVE_READLINE
3207 line = readline(prompt);
3208 copy = line ? xstrdup(line) : NULL;
3210 line = fgets(buf, sizeof(buf), stdin);
3216 fputs(prompt, stdout);
3219 line = fgets(buf, sizeof(buf), stdin);
3226 /* Ignore comments */
3235 char *p = line + strspn(line, " \t\n");
3236 char *next = strtok(p, " \t\n");
3239 if(nargc >= maxargs) {
3241 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3246 next = strtok(NULL, " \t\n");
3253 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3254 #ifdef HAVE_READLINE
3263 for(int i = 0; commands[i].command; i++) {
3264 if(!strcasecmp(nargv[argc], commands[i].command)) {
3265 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3271 #ifdef HAVE_READLINE
3280 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3285 #ifdef HAVE_READLINE
3297 static void cleanup(void) {
3303 static int run_command(int argc, char *argv[]) {
3304 if(optind >= argc) {
3305 return cmd_shell(argc, argv);
3308 for(int i = 0; commands[i].command; i++) {
3309 if(!strcasecmp(argv[optind], commands[i].command)) {
3310 return commands[i].function(argc - optind, argv + optind);
3314 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3319 int main(int argc, char *argv[]) {
3320 program_name = argv[0];
3322 tty = isatty(0) && isatty(1);
3324 if(!parse_options(argc, argv)) {
3329 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3330 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3344 static struct WSAData wsa_state;
3346 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3347 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3353 gettimeofday(&now, NULL);
3358 sandbox_set_level(SANDBOX_NORMAL);
3361 int result = run_command(argc, argv);