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"
50 #define MSG_NOSIGNAL 0
53 static char **orig_argv;
55 /* If nonzero, display usage information and exit. */
56 static bool show_help = false;
58 /* If nonzero, print the version on standard output and exit. */
59 static bool show_version = false;
61 static char *name = NULL;
62 static char controlcookie[1025];
63 char *tinc_conf = NULL;
64 char *hosts_dir = NULL;
67 // Horrible global variables...
76 bool confbasegiven = false;
77 char *scriptinterpreter = NULL;
78 static char defaultextension[] = "";
79 char *scriptextension = defaultextension;
83 debug_t debug_level = -1;
85 typedef enum option_t {
91 OPT_CONFIG_FILE = 'c',
101 static struct option const long_options[] = {
102 {"batch", no_argument, NULL, OPT_BATCH},
103 {"config", required_argument, NULL, OPT_CONFIG_FILE},
104 {"net", required_argument, NULL, OPT_NETNAME},
105 {"help", no_argument, NULL, OPT_HELP},
106 {"version", no_argument, NULL, OPT_VERSION},
107 {"pidfile", required_argument, NULL, OPT_PIDFILE},
108 {"force", no_argument, NULL, OPT_FORCE},
112 static void version(void) {
114 "%s version %s (built %s %s, protocol %d.%d)\n"
122 #ifndef DISABLE_LEGACY
129 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
130 "See the AUTHORS file for a complete list.\n"
132 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
133 "and you are welcome to redistribute it under certain conditions;\n"
134 "see the file COPYING for details.\n",
135 PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
138 static void usage(bool status) {
140 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
143 "Usage: %s [options] command\n"
145 "Valid options are:\n"
146 " -b, --batch Don't ask for anything (non-interactive mode).\n"
147 " -c, --config=DIR Read configuration options from DIR.\n"
148 " -n, --net=NETNAME Connect to net NETNAME.\n"
149 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
150 " --force Force some commands to work despite warnings.\n"
151 " --help Display this help and exit.\n"
152 " --version Output version information and exit.\n"
154 "Valid commands are:\n"
155 " init [name] Create initial configuration files.\n"
156 " get VARIABLE Print current value of VARIABLE\n"
157 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
158 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
159 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
160 " start [tincd options] Start tincd.\n"
161 " stop Stop tincd.\n"
162 " restart [tincd options] Restart tincd.\n"
163 " reload Partially reload configuration of running tincd.\n"
164 " pid Show PID of currently running tincd.\n"
165 #ifdef DISABLE_LEGACY
166 " generate-keys Generate a new Ed25519 public/private key pair.\n"
168 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
169 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
171 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
172 " dump Dump a list of one of the following things:\n"
173 " [reachable] nodes - all known nodes in the VPN\n"
174 " edges - all known connections in the VPN\n"
175 " subnets - all known subnets in the VPN\n"
176 " connections - all meta connections with ourself\n"
177 " [di]graph - graph of the VPN in dotty format\n"
178 " invitations - outstanding invitations\n"
179 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
180 " purge Purge unreachable nodes\n"
181 " debug N Set debug level\n"
182 " retry Retry all outgoing connections\n"
183 " disconnect NODE Close meta connection with NODE\n"
185 " top Show real-time statistics\n"
187 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
188 " log [level] Dump log output [up to the specified level]\n"
189 " export Export host configuration of local node to standard output\n"
190 " export-all Export all host configuration files to standard output\n"
191 " import Import host configuration file(s) from standard input\n"
192 " exchange Same as export followed by import\n"
193 " exchange-all Same as export-all followed by import\n"
194 " invite NODE [...] Generate an invitation for NODE\n"
195 " join INVITATION Join a VPN using an INVITATION\n"
196 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
197 " fsck Check the configuration files for problems.\n"
198 " sign [FILE] Generate a signed version of a file.\n"
199 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
201 "Report bugs to tinc@tinc-vpn.org.\n",
206 static bool parse_options(int argc, char **argv) {
208 int option_index = 0;
210 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
211 switch((option_t) r) {
212 case OPT_LONG_OPTION:
224 case OPT_CONFIG_FILE:
226 confbase = xstrdup(optarg);
227 confbasegiven = true;
232 netname = xstrdup(optarg);
245 pidfilename = xstrdup(optarg);
257 if(!netname && (netname = getenv("NETNAME"))) {
258 netname = xstrdup(netname);
261 /* netname "." is special: a "top-level name" */
263 if(netname && (!*netname || !strcmp(netname, "."))) {
268 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
269 fprintf(stderr, "Invalid character in netname!\n");
277 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
279 char directory[PATH_MAX] = ".";
285 /* Check stdin and stdout */
287 /* Ask for a file and/or directory name. */
288 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
290 if(fgets(buf, sizeof(buf), stdin) == NULL) {
291 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
295 size_t len = strlen(buf);
308 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
311 if(filename[0] != '/') {
314 /* The directory is a relative path or a filename. */
315 if(!getcwd(directory, sizeof(directory))) {
316 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
320 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
321 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
333 disable_old_keys(filename, what);
335 /* Open it first to keep the inode busy */
337 r = fopenmask(filename, mode, perms);
340 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
348 Generate a public/private Ed25519 key pair, and ask for a file to store
351 static bool ed25519_keygen(bool ask) {
354 char fname[PATH_MAX];
356 fprintf(stderr, "Generating Ed25519 key pair:\n");
358 if(!(key = ecdsa_generate())) {
359 fprintf(stderr, "Error during key generation!\n");
362 fprintf(stderr, "Done.\n");
365 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
366 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
372 if(!ecdsa_write_pem_private_key(key, f)) {
373 fprintf(stderr, "Error writing private key!\n");
380 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
382 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
385 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
391 char *pubkey = ecdsa_get_base64_public_key(key);
392 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
410 #ifndef DISABLE_LEGACY
412 Generate a public/private RSA key pair, and ask for a file to store
415 static bool rsa_keygen(int bits, bool ask) {
418 char fname[PATH_MAX];
420 // Make sure the key size is a multiple of 8 bits.
423 // Make sure that a valid key size is used.
424 if(bits < 1024 || bits > 8192) {
425 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
427 } else if(bits < 2048) {
428 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
431 fprintf(stderr, "Generating %d bits keys:\n", bits);
433 if(!(key = rsa_generate(bits, 0x10001))) {
434 fprintf(stderr, "Error during key generation!\n");
437 fprintf(stderr, "Done.\n");
440 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
441 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
447 if(!rsa_write_pem_private_key(key, f)) {
448 fprintf(stderr, "Error writing private key!\n");
455 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
457 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
460 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
466 if(!rsa_write_pem_public_key(key, f)) {
467 fprintf(stderr, "Error writing public key!\n");
490 bool recvline(int fd, char *line, size_t len) {
491 char *newline = NULL;
497 while(!(newline = memchr(buffer, '\n', blen))) {
498 if(!wait_socket_recv(fd)) {
502 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
504 if(nrecv == -1 && sockerrno == EINTR) {
506 } else if(nrecv <= 0) {
513 if((size_t)(newline - buffer) >= len) {
517 len = newline - buffer;
519 memcpy(line, buffer, len);
521 memmove(buffer, newline + 1, blen - len - 1);
527 static bool recvdata(int fd, char *data, size_t len) {
529 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
531 if(nrecv == -1 && sockerrno == EINTR) {
533 } else if(nrecv <= 0) {
540 memcpy(data, buffer, len);
541 memmove(buffer, buffer + len, blen - len);
547 bool sendline(int fd, const char *format, ...) {
548 static char buffer[4096];
553 va_start(ap, format);
554 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
555 buffer[sizeof(buffer) - 1] = 0;
558 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
566 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
568 if(nsend == -1 && sockerrno == EINTR) {
570 } else if(nsend <= 0) {
581 static void pcap(int fd, FILE *out, uint32_t snaplen) {
582 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
590 uint32_t tz_accuracy;
597 snaplen ? snaplen : sizeof(data),
610 fwrite(&header, sizeof(header), 1, out);
615 while(recvline(fd, line, sizeof(line))) {
618 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
619 gettimeofday(&tv, NULL);
621 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
625 if(!recvdata(fd, data, len)) {
629 packet.tv_sec = tv.tv_sec;
630 packet.tv_usec = tv.tv_usec;
632 packet.origlen = len;
633 fwrite(&packet, sizeof(packet), 1, out);
634 fwrite(data, len, 1, out);
639 static void log_control(int fd, FILE *out, int level, bool use_color) {
640 sendline(fd, "%d %d %d %d", CONTROL, REQ_LOG, level, use_color);
645 while(recvline(fd, line, sizeof(line))) {
647 int n = sscanf(line, "%d %d %d", &code, &req, &len);
649 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
653 if(!recvdata(fd, data, len)) {
657 fwrite(data, len, 1, out);
663 static bool stop_tincd(void) {
664 if(!connect_tincd(true)) {
668 sendline(fd, "%d %d", CONTROL, REQ_STOP);
670 while(recvline(fd, line, sizeof(line))) {
671 // wait for tincd to close the connection...
682 static bool remove_service(void) {
683 SC_HANDLE manager = NULL;
684 SC_HANDLE service = NULL;
685 SERVICE_STATUS status = {0};
686 bool success = false;
688 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
691 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
695 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
698 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
699 success = stop_tincd();
701 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
707 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
708 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
710 fprintf(stderr, "%s service stopped\n", identname);
713 if(!DeleteService(service)) {
714 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
723 CloseServiceHandle(service);
727 CloseServiceHandle(manager);
731 fprintf(stderr, "%s service removed\n", identname);
738 bool connect_tincd(bool verbose) {
743 struct timeval tv = {0, 0};
745 if(select(fd + 1, &r, NULL, NULL, &tv)) {
746 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
754 pidfile_t *pidfile = read_pidfile();
758 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
765 strcpy(controlcookie, pidfile->cookie);
770 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
771 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
772 /* clean up the stale socket and pid file */
774 unlink(unixsocketname);
778 struct sockaddr_un sa = {
779 .sun_family = AF_UNIX,
782 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
783 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
787 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
789 fd = socket(AF_UNIX, SOCK_STREAM, 0);
793 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
799 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
801 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
810 struct addrinfo hints = {
811 .ai_family = AF_UNSPEC,
812 .ai_socktype = SOCK_STREAM,
813 .ai_protocol = IPPROTO_TCP,
817 struct addrinfo *res = NULL;
819 if(getaddrinfo(pidfile->host, pidfile->port, &hints, &res) || !res) {
821 fprintf(stderr, "Cannot resolve %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
828 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
832 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
839 unsigned long arg = 0;
841 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
843 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
847 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
849 fprintf(stderr, "Cannot connect to %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
863 static const int one = 1;
864 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
867 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
872 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
874 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
882 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
884 fprintf(stderr, "Could not fully establish control socket connection\n");
896 static int cmd_start(int argc, char *argv[]) {
897 if(connect_tincd(false)) {
899 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
901 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
908 char *slash = strrchr(program_name, '/');
912 if((c = strrchr(program_name, '\\')) > slash) {
919 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
921 c = xstrdup("tincd");
925 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
930 Windows has no real concept of an "argv array". A command line is just one string.
931 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
932 it uses quotes to handle spaces in arguments.
933 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
934 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
935 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
937 xasprintf(&arg0, "\"%s\"", arg0);
939 nargv[nargc++] = arg0;
941 for(int i = 1; i < optind; i++) {
942 nargv[nargc++] = orig_argv[i];
945 for(int i = 1; i < argc; i++) {
946 nargv[nargc++] = argv[i];
950 int status = spawnvp(_P_WAIT, c, nargv);
956 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
962 int pfd[2] = {-1, -1};
964 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
965 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
974 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
983 snprintf(buf, sizeof(buf), "%d %d", pfd[1], use_ansi_escapes(stderr));
984 setenv("TINC_UMBILICAL", buf, true);
985 exit(execvp(c, nargv));
993 signal(SIGINT, SIG_IGN);
996 // Pass all log messages from the umbilical to stderr.
997 // A nul-byte right before closure means tincd started successfully.
1002 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1003 failure = buf[len - 1];
1009 if(write(2, buf, len) != len) {
1010 // Nothing we can do about it.
1020 // Make sure the child process is really gone.
1022 pid_t result = waitpid(pid, &status, 0);
1025 signal(SIGINT, SIG_DFL);
1028 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1031 fprintf(stderr, "Error starting %s\n", c);
1036 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1040 static int cmd_stop(int argc, char *argv[]) {
1044 fprintf(stderr, "Too many arguments!\n");
1049 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1054 if(kill(pid, SIGTERM)) {
1055 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1059 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1060 waitpid(pid, NULL, 0);
1071 static int cmd_restart(int argc, char *argv[]) {
1073 return cmd_start(argc, argv);
1076 static int cmd_reload(int argc, char *argv[]) {
1080 fprintf(stderr, "Too many arguments!\n");
1084 if(!connect_tincd(true)) {
1088 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1090 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1091 fprintf(stderr, "Could not reload configuration.\n");
1099 static int dump_invitations(void) {
1100 char dname[PATH_MAX];
1101 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1102 DIR *dir = opendir(dname);
1105 if(errno == ENOENT) {
1106 fprintf(stderr, "No outstanding invitations.\n");
1110 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1118 while((ent = readdir(dir))) {
1119 char buf[MAX_STRING_SIZE];
1121 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1125 char fname[PATH_MAX];
1127 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1128 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1132 FILE *f = fopen(fname, "r");
1135 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1141 if(!fgets(buf, sizeof(buf), f)) {
1142 fprintf(stderr, "Invalid invitation file %s\n", fname);
1149 char *eol = buf + strlen(buf);
1151 while(strchr("\t \r\n", *--eol)) {
1155 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1156 fprintf(stderr, "Invalid invitation file %s\n", fname);
1161 printf("%s %s\n", ent->d_name, buf + 7);
1167 fprintf(stderr, "No outstanding invitations.\n");
1173 static int cmd_dump(int argc, char *argv[]) {
1174 bool only_reachable = false;
1176 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1177 if(strcasecmp(argv[2], "nodes")) {
1178 fprintf(stderr, "`reachable' only supported for nodes.\n");
1183 only_reachable = true;
1189 fprintf(stderr, "Invalid number of arguments.\n");
1194 if(!strcasecmp(argv[1], "invitations")) {
1195 return dump_invitations();
1198 if(!connect_tincd(true)) {
1204 if(!strcasecmp(argv[1], "nodes")) {
1205 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1206 } else if(!strcasecmp(argv[1], "edges")) {
1207 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1208 } else if(!strcasecmp(argv[1], "subnets")) {
1209 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1210 } else if(!strcasecmp(argv[1], "connections")) {
1211 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1212 } else if(!strcasecmp(argv[1], "graph")) {
1213 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1214 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1216 } else if(!strcasecmp(argv[1], "digraph")) {
1217 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1218 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1221 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1227 printf("graph {\n");
1228 } else if(do_graph == 2) {
1229 printf("digraph {\n");
1232 while(recvline(fd, line, sizeof(line))) {
1233 char node1[4096], node2[4096];
1234 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1237 if(do_graph && req == REQ_DUMP_NODES) {
1259 char local_host[4096];
1260 char local_port[4096];
1263 int cipher, digest, maclength, compression, distance, socket, weight;
1264 short int pmtu, minmtu, maxmtu;
1265 unsigned int options;
1266 node_status_t status;
1267 long int last_state_change;
1269 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1272 case REQ_DUMP_NODES: {
1273 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);
1276 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1281 const char *color = "black";
1283 if(!strcmp(host, "MYSELF")) {
1285 } else if(!status.reachable) {
1287 } else if(strcmp(via, node)) {
1289 } else if(!status.validkey) {
1291 } else if(minmtu > 0) {
1295 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1297 if(only_reachable && !status.reachable) {
1301 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,
1302 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);
1304 if(udp_ping_rtt != -1) {
1305 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1313 case REQ_DUMP_EDGES: {
1314 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);
1317 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1322 float w = 1.0f + 65536.0f / (float)weight;
1324 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1325 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1326 } else if(do_graph == 2) {
1327 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1330 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);
1335 case REQ_DUMP_SUBNETS: {
1336 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1339 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1343 printf("%s owner %s\n", strip_weight(subnet), node);
1347 case REQ_DUMP_CONNECTIONS: {
1348 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1351 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1355 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1360 fprintf(stderr, "Unable to parse dump from tincd.\n");
1365 fprintf(stderr, "Error receiving dump.\n");
1369 static int cmd_purge(int argc, char *argv[]) {
1373 fprintf(stderr, "Too many arguments!\n");
1377 if(!connect_tincd(true)) {
1381 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1383 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1384 fprintf(stderr, "Could not purge old information.\n");
1391 static int cmd_debug(int argc, char *argv[]) {
1393 fprintf(stderr, "Invalid number of arguments.\n");
1397 if(!connect_tincd(true)) {
1401 int debuglevel = atoi(argv[1]);
1404 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1406 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1407 fprintf(stderr, "Could not set debug level.\n");
1411 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1415 static int cmd_retry(int argc, char *argv[]) {
1419 fprintf(stderr, "Too many arguments!\n");
1423 if(!connect_tincd(true)) {
1427 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1429 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1430 fprintf(stderr, "Could not retry outgoing connections.\n");
1437 static int cmd_connect(int argc, char *argv[]) {
1439 fprintf(stderr, "Invalid number of arguments.\n");
1443 if(!check_id(argv[1])) {
1444 fprintf(stderr, "Invalid name for node.\n");
1448 if(!connect_tincd(true)) {
1452 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1454 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1455 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1462 static int cmd_disconnect(int argc, char *argv[]) {
1464 fprintf(stderr, "Invalid number of arguments.\n");
1468 if(!check_id(argv[1])) {
1469 fprintf(stderr, "Invalid name for node.\n");
1473 if(!connect_tincd(true)) {
1477 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1479 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1480 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1487 static int cmd_top(int argc, char *argv[]) {
1491 fprintf(stderr, "Too many arguments!\n");
1497 if(!connect_tincd(true)) {
1504 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1509 static int cmd_pcap(int argc, char *argv[]) {
1511 fprintf(stderr, "Too many arguments!\n");
1515 if(!connect_tincd(true)) {
1519 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1524 static void sigint_handler(int sig) {
1527 if(write(2, "\n", 1) < 0) {
1528 // nothing we can do
1531 shutdown(fd, SHUT_RDWR);
1535 static int cmd_log(int argc, char *argv[]) {
1537 fprintf(stderr, "Too many arguments!\n");
1541 if(!connect_tincd(true)) {
1546 signal(SIGINT, sigint_handler);
1549 bool use_color = use_ansi_escapes(stdout);
1550 log_control(fd, stdout, argc > 1 ? atoi(argv[1]) : DEBUG_UNSET, use_color);
1553 signal(SIGINT, SIG_DFL);
1561 static int cmd_pid(int argc, char *argv[]) {
1565 fprintf(stderr, "Too many arguments!\n");
1569 if(!connect_tincd(true) || !pid) {
1573 printf("%d\n", pid);
1577 size_t rstrip(char *value) {
1578 size_t len = strlen(value);
1580 while(len && strchr("\t\r\n ", value[len - 1])) {
1587 char *get_my_name(bool verbose) {
1588 FILE *f = fopen(tinc_conf, "r");
1592 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1601 while(fgets(buf, sizeof(buf), f)) {
1602 size_t len = strcspn(buf, "\t =");
1604 value += strspn(value, "\t ");
1608 value += strspn(value, "\t ");
1611 if(!rstrip(value)) {
1617 if(strcasecmp(buf, "Name")) {
1623 return replace_name(value);
1630 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1636 static ecdsa_t *get_pubkey(FILE *f) ATTR_MALLOC ATTR_DEALLOCATOR(ecdsa_free);
1637 static ecdsa_t *get_pubkey(FILE *f) {
1641 while(fgets(buf, sizeof(buf), f)) {
1642 size_t len = strcspn(buf, "\t =");
1644 value += strspn(value, "\t ");
1648 value += strspn(value, "\t ");
1651 if(!rstrip(value)) {
1657 if(strcasecmp(buf, "Ed25519PublicKey")) {
1662 return ecdsa_set_base64_public_key(value);
1669 const var_t variables[] = {
1670 /* Server configuration */
1671 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1672 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1673 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1674 {"BindToInterface", VAR_SERVER},
1675 {"Broadcast", VAR_SERVER | VAR_SAFE},
1676 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1677 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1678 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1679 {"Device", VAR_SERVER},
1680 {"DeviceStandby", VAR_SERVER},
1681 {"DeviceType", VAR_SERVER},
1682 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1683 {"Ed25519PrivateKeyFile", VAR_SERVER},
1684 {"ExperimentalProtocol", VAR_SERVER},
1685 {"Forwarding", VAR_SERVER},
1686 {"FWMark", VAR_SERVER},
1687 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1688 {"Hostnames", VAR_SERVER},
1689 {"IffOneQueue", VAR_SERVER},
1690 {"Interface", VAR_SERVER},
1691 {"InvitationExpire", VAR_SERVER},
1692 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1693 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1694 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1695 {"LogLevel", VAR_SERVER},
1696 {"MACExpire", VAR_SERVER | VAR_SAFE},
1697 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1698 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1699 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1700 {"Mode", VAR_SERVER | VAR_SAFE},
1701 {"Name", VAR_SERVER},
1702 {"PingInterval", VAR_SERVER | VAR_SAFE},
1703 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1704 {"PriorityInheritance", VAR_SERVER},
1705 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1706 {"PrivateKeyFile", VAR_SERVER},
1707 {"ProcessPriority", VAR_SERVER},
1708 {"Proxy", VAR_SERVER},
1709 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1710 {"Sandbox", VAR_SERVER},
1711 {"ScriptsExtension", VAR_SERVER},
1712 {"ScriptsInterpreter", VAR_SERVER},
1713 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1714 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1715 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1716 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1717 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1718 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1719 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1720 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1721 {"UDPRcvBuf", VAR_SERVER},
1722 {"UDPSndBuf", VAR_SERVER},
1723 {"UPnP", VAR_SERVER},
1724 {"UPnPDiscoverWait", VAR_SERVER},
1725 {"UPnPRefreshPeriod", VAR_SERVER},
1726 {"VDEGroup", VAR_SERVER},
1727 {"VDEPort", VAR_SERVER},
1728 /* Host configuration */
1729 {"Address", VAR_HOST | VAR_MULTIPLE},
1730 {"Cipher", VAR_SERVER | VAR_HOST},
1731 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1732 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1733 {"Digest", VAR_SERVER | VAR_HOST},
1734 {"Ed25519PublicKey", VAR_HOST},
1735 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1736 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1737 {"MACLength", VAR_SERVER | VAR_HOST},
1738 {"PMTU", VAR_SERVER | VAR_HOST},
1739 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1741 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1742 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1743 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1744 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1745 {"Weight", VAR_HOST | VAR_SAFE},
1749 // Request actual port from tincd
1750 static bool read_actual_port(void) {
1751 pidfile_t *pidfile = read_pidfile();
1754 printf("%s\n", pidfile->port);
1758 fprintf(stderr, "Could not get port from the pidfile.\n");
1763 static int cmd_config(int argc, char *argv[]) {
1765 fprintf(stderr, "Invalid number of arguments.\n");
1769 if(strcasecmp(argv[0], "config")) {
1773 typedef enum { GET, DEL, SET, ADD } action_t;
1774 action_t action = GET;
1776 if(!strcasecmp(argv[1], "get")) {
1778 } else if(!strcasecmp(argv[1], "add")) {
1779 argv++, argc--, action = ADD;
1780 } else if(!strcasecmp(argv[1], "del")) {
1781 argv++, argc--, action = DEL;
1782 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1783 argv++, argc--, action = SET;
1787 fprintf(stderr, "Invalid number of arguments.\n");
1791 // Concatenate the rest of the command line
1792 strncpy(line, argv[1], sizeof(line) - 1);
1794 for(int i = 2; i < argc; i++) {
1795 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1796 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1799 // Liberal parsing into node name, variable name and value.
1805 len = strcspn(line, "\t =");
1807 value += strspn(value, "\t ");
1811 value += strspn(value, "\t ");
1815 variable = strchr(line, '.');
1825 fprintf(stderr, "No variable given.\n");
1829 if((action == SET || action == ADD) && !*value) {
1830 fprintf(stderr, "No value for variable given.\n");
1834 if(action == GET && *value) {
1838 // If port is requested, try reading it from the pidfile and fall back to configs if that fails
1839 if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
1843 /* Some simple checks. */
1845 bool warnonremove = false;
1847 for(int i = 0; variables[i].name; i++) {
1848 if(strcasecmp(variables[i].name, variable)) {
1853 variable = (char *)variables[i].name;
1855 if(!strcasecmp(variable, "Subnet") && *value) {
1858 if(!str2net(&s, value)) {
1859 fprintf(stderr, "Malformed subnet definition %s\n", value);
1863 if(!subnetcheck(s)) {
1864 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1869 /* Discourage use of obsolete variables. */
1871 if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
1873 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1875 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1880 /* Don't put server variables in host config files */
1882 if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
1884 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1886 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1891 /* Should this go into our own host config file? */
1893 if(!node && !(variables[i].type & VAR_SERVER)) {
1894 node = get_my_name(true);
1901 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1902 Turn on warnings when it seems variables might be removed unintentionally. */
1904 if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
1905 warnonremove = true;
1907 } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
1908 warnonremove = true;
1914 if(node && !check_id(node)) {
1915 fprintf(stderr, "Invalid name for node.\n");
1925 if(force || action == GET || action == DEL) {
1926 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1928 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1930 if(node && node != line) {
1938 // Open the right configuration file.
1939 char filename[PATH_MAX];
1942 size_t wrote = (size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1949 if(wrote >= sizeof(filename)) {
1950 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1955 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1958 FILE *f = fopen(filename, "r");
1961 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1965 char tmpfile[PATH_MAX];
1969 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1970 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1974 tf = fopen(tmpfile, "w");
1977 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1983 // Copy the file, making modifications on the fly, unless we are just getting a value.
1987 bool removed = false;
1990 while(fgets(buf1, sizeof(buf1), f)) {
1991 buf1[sizeof(buf1) - 1] = 0;
1992 strncpy(buf2, buf1, sizeof(buf2));
1994 // Parse line in a simple way
1997 size_t len = strcspn(buf2, "\t =");
1998 bvalue = buf2 + len;
1999 bvalue += strspn(bvalue, "\t ");
2001 if(*bvalue == '=') {
2003 bvalue += strspn(bvalue, "\t ");
2010 if(!strcasecmp(buf2, variable)) {
2013 printf("%s\n", bvalue);
2014 } else if(action == DEL) {
2015 if(!*value || !strcasecmp(bvalue, value)) {
2019 } else if(action == SET) {
2020 // Warn if "set" was used for variables that can occur multiple times
2021 if(warnonremove && strcasecmp(bvalue, value)) {
2022 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2025 // Already set? Delete the rest...
2030 // Otherwise, replace.
2031 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2032 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2038 } else if(action == ADD) {
2039 // Check if we've already seen this variable with the same value
2040 if(!strcasecmp(bvalue, value)) {
2047 // Copy original line...
2048 if(fputs(buf1, tf) < 0) {
2049 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2053 // Add newline if it is missing...
2054 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2055 if(fputc('\n', tf) < 0) {
2056 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2063 // Make sure we read everything...
2064 if(ferror(f) || !feof(f)) {
2065 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2070 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2074 // Add new variable if necessary.
2075 if((action == ADD && !found) || (action == SET && !set)) {
2076 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2077 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2086 fprintf(stderr, "No matching configuration variables found.\n");
2091 // Make sure we wrote everything...
2093 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2097 // Could we find what we had to remove?
2098 if((action == GET || action == DEL) && !removed) {
2100 fprintf(stderr, "No configuration variables deleted.\n");
2104 // Replace the configuration file with the new one
2107 if(remove(filename)) {
2108 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2114 if(rename(tmpfile, filename)) {
2115 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2119 // Silently try notifying a running tincd of changes.
2120 if(connect_tincd(false)) {
2121 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2127 static bool try_bind(int port) {
2128 struct addrinfo *ai = NULL, *aip;
2129 struct addrinfo hint = {
2130 .ai_flags = AI_PASSIVE,
2131 .ai_family = AF_UNSPEC,
2132 .ai_socktype = SOCK_STREAM,
2133 .ai_protocol = IPPROTO_TCP,
2136 bool success = true;
2138 snprintf(portstr, sizeof(portstr), "%d", port);
2140 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2144 for(aip = ai; aip; aip = aip->ai_next) {
2145 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2152 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2165 int check_port(const char *name) {
2170 fprintf(stderr, "Warning: could not bind to port 655. ");
2172 for(int i = 0; i < 100; i++) {
2173 uint16_t port = 0x1000 + prng(0x8000);
2175 if(try_bind(port)) {
2176 char filename[PATH_MAX];
2177 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2178 FILE *f = fopen(filename, "a");
2181 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2182 fprintf(stderr, "Please change tinc's Port manually.\n");
2186 fprintf(f, "Port = %d\n", port);
2188 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2193 fprintf(stderr, "Please change tinc's Port manually.\n");
2197 static int cmd_init(int argc, char *argv[]) {
2198 if(!access(tinc_conf, F_OK)) {
2199 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2204 fprintf(stderr, "Too many arguments!\n");
2206 } else if(argc < 2) {
2209 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2211 if(!fgets(buf, sizeof(buf), stdin)) {
2212 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2216 size_t len = rstrip(buf);
2219 fprintf(stderr, "No name given!\n");
2225 fprintf(stderr, "No Name given!\n");
2229 name = strdup(argv[1]);
2232 fprintf(stderr, "No Name given!\n");
2237 if(!check_id(name)) {
2238 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2242 if(!makedirs(DIR_HOSTS | DIR_CONFBASE | DIR_CONFDIR | DIR_CACHE)) {
2246 FILE *f = fopen(tinc_conf, "w");
2249 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2253 fprintf(f, "Name = %s\n", name);
2256 #ifndef DISABLE_LEGACY
2258 if(!rsa_keygen(2048, false)) {
2264 if(!ed25519_keygen(false)) {
2270 #ifndef HAVE_WINDOWS
2271 char filename[PATH_MAX];
2272 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2274 if(access(filename, F_OK)) {
2275 FILE *f = fopenmask(filename, "w", 0777);
2278 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2282 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");
2292 static int cmd_generate_keys(int argc, char *argv[]) {
2293 #ifdef DISABLE_LEGACY
2301 fprintf(stderr, "Too many arguments!\n");
2306 name = get_my_name(false);
2309 #ifndef DISABLE_LEGACY
2311 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2317 if(!ed25519_keygen(true)) {
2324 #ifndef DISABLE_LEGACY
2325 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2327 fprintf(stderr, "Too many arguments!\n");
2332 name = get_my_name(false);
2335 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2339 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2343 fprintf(stderr, "Too many arguments!\n");
2348 name = get_my_name(false);
2351 return !ed25519_keygen(true);
2354 static int cmd_help(int argc, char *argv[]) {
2362 static int cmd_version(int argc, char *argv[]) {
2366 fprintf(stderr, "Too many arguments!\n");
2374 static int cmd_info(int argc, char *argv[]) {
2376 fprintf(stderr, "Invalid number of arguments.\n");
2380 if(!connect_tincd(true)) {
2384 return info(fd, argv[1]);
2387 static const char *conffiles[] = {
2398 static int cmd_edit(int argc, char *argv[]) {
2400 fprintf(stderr, "Invalid number of arguments.\n");
2404 char filename[PATH_MAX] = "";
2406 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2407 for(int i = 0; conffiles[i]; i++) {
2408 if(!strcmp(argv[1], conffiles[i])) {
2409 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2418 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2419 char *dash = strchr(argv[1], '-');
2424 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2425 fprintf(stderr, "Invalid configuration filename.\n");
2432 #ifndef HAVE_WINDOWS
2433 const char *editor = getenv("VISUAL");
2436 editor = getenv("EDITOR");
2443 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2445 xasprintf(&command, "edit \"%s\"", filename);
2447 int result = system(command);
2454 // Silently try notifying a running tincd of changes.
2455 if(connect_tincd(false)) {
2456 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2462 static int export(const char *name, FILE *out) {
2463 char filename[PATH_MAX];
2464 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2465 FILE *in = fopen(filename, "r");
2468 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2472 fprintf(out, "Name = %s\n", name);
2475 while(fgets(buf, sizeof(buf), in)) {
2476 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2482 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2491 static int cmd_export(int argc, char *argv[]) {
2495 fprintf(stderr, "Too many arguments!\n");
2499 char *name = get_my_name(true);
2505 int result = export(name, stdout);
2515 static int cmd_export_all(int argc, char *argv[]) {
2519 fprintf(stderr, "Too many arguments!\n");
2523 DIR *dir = opendir(hosts_dir);
2526 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2534 while((ent = readdir(dir))) {
2535 if(!check_id(ent->d_name)) {
2542 printf("\n#---------------------------------------------------------------#\n");
2545 result |= export(ent->d_name, stdout);
2557 static int cmd_import(int argc, char *argv[]) {
2561 fprintf(stderr, "Too many arguments!\n");
2570 char filename[PATH_MAX] = "";
2572 bool firstline = true;
2574 while(fgets(buf, sizeof(buf), in)) {
2575 if(sscanf(buf, "Name = %4095s", name) == 1) {
2578 if(!check_id(name)) {
2579 fprintf(stderr, "Invalid Name in input!\n");
2587 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2588 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2592 if(!force && !access(filename, F_OK)) {
2593 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2598 out = fopen(filename, "w");
2601 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2607 } else if(firstline) {
2608 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2613 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2618 if(fputs(buf, out) < 0) {
2619 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2630 fprintf(stderr, "Imported %d host configuration files.\n", count);
2633 fprintf(stderr, "No host configuration files imported.\n");
2638 static int cmd_exchange(int argc, char *argv[]) {
2639 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2642 static int cmd_exchange_all(int argc, char *argv[]) {
2643 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2646 static int switch_network(char *name) {
2647 if(strcmp(name, ".")) {
2648 if(!check_netname(name, false)) {
2649 fprintf(stderr, "Invalid character in netname!\n");
2653 if(!check_netname(name, true)) {
2654 fprintf(stderr, "Warning: unsafe character in netname!\n");
2664 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2671 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2672 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2673 xasprintf(&prompt, "%s> ", identname);
2678 static int cmd_network(int argc, char *argv[]) {
2680 fprintf(stderr, "Too many arguments!\n");
2685 return switch_network(argv[1]);
2688 DIR *dir = opendir(confdir);
2691 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2697 while((ent = readdir(dir))) {
2698 if(*ent->d_name == '.') {
2702 if(!strcmp(ent->d_name, "tinc.conf")) {
2707 char fname[PATH_MAX];
2708 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2710 if(!access(fname, R_OK)) {
2711 printf("%s\n", ent->d_name);
2720 static int cmd_fsck(int argc, char *argv[]) {
2724 fprintf(stderr, "Too many arguments!\n");
2728 return fsck(orig_argv[0]);
2731 static void *readfile(FILE *in, size_t *len) {
2733 size_t bufsize = 4096;
2734 char *buf = xmalloc(bufsize);
2737 size_t read = fread(buf + count, 1, bufsize - count, in);
2745 if(count >= bufsize) {
2747 buf = xrealloc(buf, bufsize);
2758 static int cmd_sign(int argc, char *argv[]) {
2760 fprintf(stderr, "Too many arguments!\n");
2765 name = get_my_name(true);
2772 char fname[PATH_MAX];
2773 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2774 FILE *fp = fopen(fname, "r");
2777 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2781 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2784 fprintf(stderr, "Could not read private key from %s\n", fname);
2794 in = fopen(argv[1], "rb");
2797 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2806 char *data = readfile(in, &len);
2813 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2818 // Ensure we sign our name and current time as well
2819 long t = time(NULL);
2821 xasprintf(&trailer, " %s %ld", name, t);
2822 size_t trailer_len = strlen(trailer);
2824 data = xrealloc(data, len + trailer_len);
2825 memcpy(data + len, trailer, trailer_len);
2830 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2831 fprintf(stderr, "Error generating signature\n");
2837 b64encode_tinc(sig, sig, 64);
2840 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2841 fwrite(data, len, 1, stdout);
2847 static int cmd_verify(int argc, char *argv[]) {
2849 fprintf(stderr, "Not enough arguments!\n");
2854 fprintf(stderr, "Too many arguments!\n");
2858 char *node = argv[1];
2860 if(!strcmp(node, ".")) {
2862 name = get_my_name(true);
2870 } else if(!strcmp(node, "*")) {
2873 if(!check_id(node)) {
2874 fprintf(stderr, "Invalid node name\n");
2882 in = fopen(argv[2], "rb");
2885 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2893 char *data = readfile(in, &len);
2900 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2904 char *newline = memchr(data, '\n', len);
2906 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2907 fprintf(stderr, "Invalid input\n");
2913 size_t skip = newline - data;
2915 char signer[MAX_STRING_SIZE] = "";
2916 char sig[MAX_STRING_SIZE] = "";
2919 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2920 fprintf(stderr, "Invalid input\n");
2925 if(node && strcmp(node, signer)) {
2926 fprintf(stderr, "Signature is not made by %s\n", node);
2936 xasprintf(&trailer, " %s %ld", signer, t);
2937 size_t trailer_len = strlen(trailer);
2939 data = xrealloc(data, len + trailer_len);
2940 memcpy(data + len, trailer, trailer_len);
2943 newline = data + skip;
2945 char fname[PATH_MAX];
2946 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2947 FILE *fp = fopen(fname, "r");
2950 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2955 ecdsa_t *key = get_pubkey(fp);
2959 key = ecdsa_read_pem_public_key(fp);
2963 fprintf(stderr, "Could not read public key from %s\n", fname);
2971 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2972 fprintf(stderr, "Invalid signature\n");
2980 fwrite(newline, len - (newline - data), 1, stdout);
2986 static const struct {
2987 const char *command;
2988 int (*function)(int argc, char *argv[]);
2991 {"start", cmd_start, false},
2992 {"stop", cmd_stop, false},
2993 {"restart", cmd_restart, false},
2994 {"reload", cmd_reload, false},
2995 {"dump", cmd_dump, false},
2996 {"list", cmd_dump, false},
2997 {"purge", cmd_purge, false},
2998 {"debug", cmd_debug, false},
2999 {"retry", cmd_retry, false},
3000 {"connect", cmd_connect, false},
3001 {"disconnect", cmd_disconnect, false},
3002 {"top", cmd_top, false},
3003 {"pcap", cmd_pcap, false},
3004 {"log", cmd_log, false},
3005 {"pid", cmd_pid, false},
3006 {"config", cmd_config, true},
3007 {"add", cmd_config, false},
3008 {"del", cmd_config, false},
3009 {"get", cmd_config, false},
3010 {"set", cmd_config, false},
3011 {"init", cmd_init, false},
3012 {"generate-keys", cmd_generate_keys, false},
3013 #ifndef DISABLE_LEGACY
3014 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3016 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3017 {"help", cmd_help, false},
3018 {"version", cmd_version, false},
3019 {"info", cmd_info, false},
3020 {"edit", cmd_edit, false},
3021 {"export", cmd_export, false},
3022 {"export-all", cmd_export_all, false},
3023 {"import", cmd_import, false},
3024 {"exchange", cmd_exchange, false},
3025 {"exchange-all", cmd_exchange_all, false},
3026 {"invite", cmd_invite, false},
3027 {"join", cmd_join, false},
3028 {"network", cmd_network, false},
3029 {"fsck", cmd_fsck, false},
3030 {"sign", cmd_sign, false},
3031 {"verify", cmd_verify, false},
3032 {NULL, NULL, false},
3035 #ifdef HAVE_READLINE
3036 static char *complete_command(const char *text, int state) {
3045 while(commands[i].command) {
3046 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3047 return xstrdup(commands[i].command);
3056 static char *complete_dump(const char *text, int state) {
3057 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3067 if(!strncasecmp(matches[i], text, strlen(text))) {
3068 return xstrdup(matches[i]);
3077 static char *complete_config(const char *text, int state) {
3086 while(variables[i].name) {
3087 char *dot = strchr(text, '.');
3090 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3092 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3096 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3097 return xstrdup(variables[i].name);
3107 static char *complete_info(const char *text, int state) {
3113 if(!connect_tincd(false)) {
3117 // Check the list of nodes
3118 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3119 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3122 while(recvline(fd, line, sizeof(line))) {
3124 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3137 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3141 if(!strncmp(item, text, strlen(text))) {
3142 return xstrdup(strip_weight(item));
3149 static char *complete_nothing(const char *text, int state) {
3155 static char **completion(const char *text, int start, int end) {
3157 char **matches = NULL;
3160 matches = rl_completion_matches(text, complete_command);
3161 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3162 matches = rl_completion_matches(text, complete_dump);
3163 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3164 matches = rl_completion_matches(text, complete_config);
3165 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3166 matches = rl_completion_matches(text, complete_config);
3167 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3168 matches = rl_completion_matches(text, complete_config);
3169 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3170 matches = rl_completion_matches(text, complete_config);
3171 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3172 matches = rl_completion_matches(text, complete_info);
3179 static int cmd_shell(int argc, char *argv[]) {
3180 xasprintf(&prompt, "%s> ", identname);
3184 int maxargs = argc + 16;
3185 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3187 for(int i = 0; i < argc; i++) {
3191 #ifdef HAVE_READLINE
3192 rl_readline_name = "tinc";
3193 rl_basic_word_break_characters = "\t\n ";
3194 rl_completion_entry_function = complete_nothing;
3195 rl_attempted_completion_function = completion;
3196 rl_filename_completion_desired = 0;
3201 #ifdef HAVE_READLINE
3206 line = readline(prompt);
3207 copy = line ? xstrdup(line) : NULL;
3209 line = fgets(buf, sizeof(buf), stdin);
3215 fputs(prompt, stdout);
3218 line = fgets(buf, sizeof(buf), stdin);
3225 /* Ignore comments */
3234 char *p = line + strspn(line, " \t\n");
3235 char *next = strtok(p, " \t\n");
3238 if(nargc >= maxargs) {
3240 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3245 next = strtok(NULL, " \t\n");
3252 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3253 #ifdef HAVE_READLINE
3262 for(int i = 0; commands[i].command; i++) {
3263 if(!strcasecmp(nargv[argc], commands[i].command)) {
3264 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3270 #ifdef HAVE_READLINE
3279 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3284 #ifdef HAVE_READLINE
3296 static void cleanup(void) {
3302 static int run_command(int argc, char *argv[]) {
3303 if(optind >= argc) {
3304 return cmd_shell(argc, argv);
3307 for(int i = 0; commands[i].command; i++) {
3308 if(!strcasecmp(argv[optind], commands[i].command)) {
3309 return commands[i].function(argc - optind, argv + optind);
3313 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3318 int main(int argc, char *argv[]) {
3319 program_name = argv[0];
3321 tty = isatty(0) && isatty(1);
3323 if(!parse_options(argc, argv)) {
3328 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3329 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3343 static struct WSAData wsa_state;
3345 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3346 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3352 gettimeofday(&now, NULL);
3357 sandbox_set_level(SANDBOX_NORMAL);
3360 int result = run_command(argc, argv);