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"
46 #define MSG_NOSIGNAL 0
49 static char **orig_argv;
51 /* If nonzero, display usage information and exit. */
52 static bool show_help = false;
54 /* If nonzero, print the version on standard output and exit. */
55 static bool show_version = false;
57 static char *name = NULL;
58 static char controlcookie[1025];
59 char *tinc_conf = NULL;
60 char *hosts_dir = NULL;
63 // Horrible global variables...
72 bool confbasegiven = false;
73 char *scriptinterpreter = NULL;
74 static char defaultextension[] = "";
75 char *scriptextension = defaultextension;
81 static struct option const long_options[] = {
82 {"batch", no_argument, NULL, 'b'},
83 {"config", required_argument, NULL, 'c'},
84 {"net", required_argument, NULL, 'n'},
85 {"help", no_argument, NULL, 1},
86 {"version", no_argument, NULL, 2},
87 {"pidfile", required_argument, NULL, 3},
88 {"force", no_argument, NULL, 4},
92 static void version(void) {
93 static const char *message =
94 "%s version %s (built %s %s, protocol %d.%d)\n"
102 #ifndef DISABLE_LEGACY
106 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
107 "See the AUTHORS file for a complete list.\n"
109 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
110 "and you are welcome to redistribute it under certain conditions;\n"
111 "see the file COPYING for details.\n";
113 printf(message, PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
116 static void usage(bool status) {
118 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
120 static const char *message =
121 "Usage: %s [options] command\n"
123 "Valid options are:\n"
124 " -b, --batch Don't ask for anything (non-interactive mode).\n"
125 " -c, --config=DIR Read configuration options from DIR.\n"
126 " -n, --net=NETNAME Connect to net NETNAME.\n"
127 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
128 " --force Force some commands to work despite warnings.\n"
129 " --help Display this help and exit.\n"
130 " --version Output version information and exit.\n"
132 "Valid commands are:\n"
133 " init [name] Create initial configuration files.\n"
134 " get VARIABLE Print current value of VARIABLE\n"
135 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
136 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
137 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
138 " start [tincd options] Start tincd.\n"
139 " stop Stop tincd.\n"
140 " restart [tincd options] Restart tincd.\n"
141 " reload Partially reload configuration of running tincd.\n"
142 " pid Show PID of currently running tincd.\n"
143 #ifdef DISABLE_LEGACY
144 " generate-keys Generate a new Ed25519 public/private key pair.\n"
146 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
147 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
149 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
150 " dump Dump a list of one of the following things:\n"
151 " [reachable] nodes - all known nodes in the VPN\n"
152 " edges - all known connections in the VPN\n"
153 " subnets - all known subnets in the VPN\n"
154 " connections - all meta connections with ourself\n"
155 " [di]graph - graph of the VPN in dotty format\n"
156 " invitations - outstanding invitations\n"
157 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
158 " purge Purge unreachable nodes\n"
159 " debug N Set debug level\n"
160 " retry Retry all outgoing connections\n"
161 " disconnect NODE Close meta connection with NODE\n"
163 " top Show real-time statistics\n"
165 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
166 " log [level] Dump log output [up to the specified level]\n"
167 " export Export host configuration of local node to standard output\n"
168 " export-all Export all host configuration files to standard output\n"
169 " import Import host configuration file(s) from standard input\n"
170 " exchange Same as export followed by import\n"
171 " exchange-all Same as export-all followed by import\n"
172 " invite NODE [...] Generate an invitation for NODE\n"
173 " join INVITATION Join a VPN using an INVITATION\n"
174 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
175 " fsck Check the configuration files for problems.\n"
176 " sign [FILE] Generate a signed version of a file.\n"
177 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
179 "Report bugs to tinc@tinc-vpn.org.\n";
181 printf(message, program_name);
185 static bool parse_options(int argc, char **argv) {
187 int option_index = 0;
189 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
191 case 0: /* long option */
198 case 'c': /* config file */
200 confbase = xstrdup(optarg);
201 confbasegiven = true;
204 case 'n': /* net name given */
206 netname = xstrdup(optarg);
209 case 1: /* show help */
213 case 2: /* show version */
217 case 3: /* open control socket here */
219 pidfilename = xstrdup(optarg);
226 case '?': /* wrong options */
236 if(!netname && (netname = getenv("NETNAME"))) {
237 netname = xstrdup(netname);
240 /* netname "." is special: a "top-level name" */
242 if(netname && (!*netname || !strcmp(netname, "."))) {
247 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
248 fprintf(stderr, "Invalid character in netname!\n");
256 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
258 char directory[PATH_MAX] = ".";
264 /* Check stdin and stdout */
266 /* Ask for a file and/or directory name. */
267 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
269 if(fgets(buf, sizeof(buf), stdin) == NULL) {
270 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
274 size_t len = strlen(buf);
287 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
290 if(filename[0] != '/') {
293 /* The directory is a relative path or a filename. */
294 if(!getcwd(directory, sizeof(directory))) {
295 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
299 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
300 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
312 disable_old_keys(filename, what);
314 /* Open it first to keep the inode busy */
316 r = fopenmask(filename, mode, perms);
319 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
327 Generate a public/private Ed25519 key pair, and ask for a file to store
330 static bool ed25519_keygen(bool ask) {
333 char fname[PATH_MAX];
335 fprintf(stderr, "Generating Ed25519 key pair:\n");
337 if(!(key = ecdsa_generate())) {
338 fprintf(stderr, "Error during key generation!\n");
341 fprintf(stderr, "Done.\n");
344 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
345 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
351 if(!ecdsa_write_pem_private_key(key, f)) {
352 fprintf(stderr, "Error writing private key!\n");
359 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
361 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
364 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
370 char *pubkey = ecdsa_get_base64_public_key(key);
371 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
389 #ifndef DISABLE_LEGACY
391 Generate a public/private RSA key pair, and ask for a file to store
394 static bool rsa_keygen(int bits, bool ask) {
397 char fname[PATH_MAX];
399 // Make sure the key size is a multiple of 8 bits.
402 // Make sure that a valid key size is used.
403 if(bits < 1024 || bits > 8192) {
404 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
406 } else if(bits < 2048) {
407 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
410 fprintf(stderr, "Generating %d bits keys:\n", bits);
412 if(!(key = rsa_generate(bits, 0x10001))) {
413 fprintf(stderr, "Error during key generation!\n");
416 fprintf(stderr, "Done.\n");
419 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
420 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
426 if(!rsa_write_pem_private_key(key, f)) {
427 fprintf(stderr, "Error writing private key!\n");
434 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
436 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
439 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
445 if(!rsa_write_pem_public_key(key, f)) {
446 fprintf(stderr, "Error writing public key!\n");
469 bool recvline(int fd, char *line, size_t len) {
470 char *newline = NULL;
476 while(!(newline = memchr(buffer, '\n', blen))) {
477 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
479 if(nrecv == -1 && sockerrno == EINTR) {
481 } else if(nrecv <= 0) {
488 if((size_t)(newline - buffer) >= len) {
492 len = newline - buffer;
494 memcpy(line, buffer, len);
496 memmove(buffer, newline + 1, blen - len - 1);
502 static bool recvdata(int fd, char *data, size_t len) {
504 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
506 if(nrecv == -1 && sockerrno == EINTR) {
508 } else if(nrecv <= 0) {
515 memcpy(data, buffer, len);
516 memmove(buffer, buffer + len, blen - len);
522 bool sendline(int fd, const char *format, ...) {
523 static char buffer[4096];
528 va_start(ap, format);
529 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
530 buffer[sizeof(buffer) - 1] = 0;
533 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
541 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
543 if(nsend == -1 && sockerrno == EINTR) {
545 } else if(nsend <= 0) {
556 static void pcap(int fd, FILE *out, uint32_t snaplen) {
557 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
565 uint32_t tz_accuracy;
572 snaplen ? snaplen : sizeof(data),
585 fwrite(&header, sizeof(header), 1, out);
590 while(recvline(fd, line, sizeof(line))) {
593 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
594 gettimeofday(&tv, NULL);
596 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
600 if(!recvdata(fd, data, len)) {
604 packet.tv_sec = tv.tv_sec;
605 packet.tv_usec = tv.tv_usec;
607 packet.origlen = len;
608 fwrite(&packet, sizeof(packet), 1, out);
609 fwrite(data, len, 1, out);
614 static void logcontrol(int fd, FILE *out, int level) {
615 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
619 while(recvline(fd, line, sizeof(line))) {
621 int n = sscanf(line, "%d %d %d", &code, &req, &len);
623 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
627 if(!recvdata(fd, data, len)) {
631 fwrite(data, len, 1, out);
637 static bool stop_tincd(void) {
638 if(!connect_tincd(true)) {
642 sendline(fd, "%d %d", CONTROL, REQ_STOP);
644 while(recvline(fd, line, sizeof(line))) {
645 // wait for tincd to close the connection...
656 static bool remove_service(void) {
657 SC_HANDLE manager = NULL;
658 SC_HANDLE service = NULL;
659 SERVICE_STATUS status = {0};
660 bool success = false;
662 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
665 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
669 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
672 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
673 success = stop_tincd();
675 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
681 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
682 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
684 fprintf(stderr, "%s service stopped\n", identname);
687 if(!DeleteService(service)) {
688 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
697 CloseServiceHandle(service);
701 CloseServiceHandle(manager);
705 fprintf(stderr, "%s service removed\n", identname);
712 bool connect_tincd(bool verbose) {
717 struct timeval tv = {0, 0};
719 if(select(fd + 1, &r, NULL, NULL, &tv)) {
720 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
728 FILE *f = fopen(pidfilename, "r");
732 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
741 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
743 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
754 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
755 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
756 /* clean up the stale socket and pid file */
758 unlink(unixsocketname);
762 struct sockaddr_un sa = {
763 .sun_family = AF_UNIX,
766 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
767 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
771 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
773 fd = socket(AF_UNIX, SOCK_STREAM, 0);
777 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
783 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
785 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
794 struct addrinfo hints = {
795 .ai_family = AF_UNSPEC,
796 .ai_socktype = SOCK_STREAM,
797 .ai_protocol = IPPROTO_TCP,
801 struct addrinfo *res = NULL;
803 if(getaddrinfo(host, port, &hints, &res) || !res) {
805 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
811 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
815 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
821 unsigned long arg = 0;
823 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
825 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
829 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
831 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
843 static const int one = 1;
844 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
847 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
852 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
854 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
862 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
864 fprintf(stderr, "Could not fully establish control socket connection\n");
876 static int cmd_start(int argc, char *argv[]) {
877 if(connect_tincd(false)) {
879 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
881 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
888 char *slash = strrchr(program_name, '/');
892 if((c = strrchr(program_name, '\\')) > slash) {
899 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
901 c = xstrdup("tincd");
905 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
910 Windows has no real concept of an "argv array". A command line is just one string.
911 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
912 it uses quotes to handle spaces in arguments.
913 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
914 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
915 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
917 xasprintf(&arg0, "\"%s\"", arg0);
919 nargv[nargc++] = arg0;
921 for(int i = 1; i < optind; i++) {
922 nargv[nargc++] = orig_argv[i];
925 for(int i = 1; i < argc; i++) {
926 nargv[nargc++] = argv[i];
930 int status = spawnvp(_P_WAIT, c, nargv);
936 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
942 int pfd[2] = {-1, -1};
944 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
945 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
954 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
963 snprintf(buf, sizeof(buf), "%d", pfd[1]);
964 setenv("TINC_UMBILICAL", buf, true);
965 exit(execvp(c, nargv));
973 signal(SIGINT, SIG_IGN);
976 // Pass all log messages from the umbilical to stderr.
977 // A nul-byte right before closure means tincd started successfully.
982 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
983 failure = buf[len - 1];
989 if(write(2, buf, len) != len) {
990 // Nothing we can do about it.
1000 // Make sure the child process is really gone.
1002 pid_t result = waitpid(pid, &status, 0);
1005 signal(SIGINT, SIG_DFL);
1008 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1011 fprintf(stderr, "Error starting %s\n", c);
1016 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1020 static int cmd_stop(int argc, char *argv[]) {
1024 fprintf(stderr, "Too many arguments!\n");
1029 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1034 if(kill(pid, SIGTERM)) {
1035 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1039 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1040 waitpid(pid, NULL, 0);
1051 static int cmd_restart(int argc, char *argv[]) {
1053 return cmd_start(argc, argv);
1056 static int cmd_reload(int argc, char *argv[]) {
1060 fprintf(stderr, "Too many arguments!\n");
1064 if(!connect_tincd(true)) {
1068 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1070 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1071 fprintf(stderr, "Could not reload configuration.\n");
1079 static int dump_invitations(void) {
1080 char dname[PATH_MAX];
1081 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1082 DIR *dir = opendir(dname);
1085 if(errno == ENOENT) {
1086 fprintf(stderr, "No outstanding invitations.\n");
1090 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1098 while((ent = readdir(dir))) {
1099 char buf[MAX_STRING_SIZE];
1101 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1105 char fname[PATH_MAX];
1107 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1108 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1112 FILE *f = fopen(fname, "r");
1115 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1121 if(!fgets(buf, sizeof(buf), f)) {
1122 fprintf(stderr, "Invalid invitation file %s\n", fname);
1129 char *eol = buf + strlen(buf);
1131 while(strchr("\t \r\n", *--eol)) {
1135 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1136 fprintf(stderr, "Invalid invitation file %s\n", fname);
1141 printf("%s %s\n", ent->d_name, buf + 7);
1147 fprintf(stderr, "No outstanding invitations.\n");
1153 static int cmd_dump(int argc, char *argv[]) {
1154 bool only_reachable = false;
1156 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1157 if(strcasecmp(argv[2], "nodes")) {
1158 fprintf(stderr, "`reachable' only supported for nodes.\n");
1163 only_reachable = true;
1169 fprintf(stderr, "Invalid number of arguments.\n");
1174 if(!strcasecmp(argv[1], "invitations")) {
1175 return dump_invitations();
1178 if(!connect_tincd(true)) {
1184 if(!strcasecmp(argv[1], "nodes")) {
1185 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1186 } else if(!strcasecmp(argv[1], "edges")) {
1187 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1188 } else if(!strcasecmp(argv[1], "subnets")) {
1189 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1190 } else if(!strcasecmp(argv[1], "connections")) {
1191 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1192 } else if(!strcasecmp(argv[1], "graph")) {
1193 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1194 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1196 } else if(!strcasecmp(argv[1], "digraph")) {
1197 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1198 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1201 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1207 printf("graph {\n");
1208 } else if(do_graph == 2) {
1209 printf("digraph {\n");
1212 while(recvline(fd, line, sizeof(line))) {
1213 char node1[4096], node2[4096];
1214 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1217 if(do_graph && req == REQ_DUMP_NODES) {
1239 char local_host[4096];
1240 char local_port[4096];
1243 int cipher, digest, maclength, compression, distance, socket, weight;
1244 short int pmtu, minmtu, maxmtu;
1245 unsigned int options;
1246 node_status_t status;
1247 long int last_state_change;
1249 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1252 case REQ_DUMP_NODES: {
1253 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);
1256 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1261 const char *color = "black";
1263 if(!strcmp(host, "MYSELF")) {
1265 } else if(!status.reachable) {
1267 } else if(strcmp(via, node)) {
1269 } else if(!status.validkey) {
1271 } else if(minmtu > 0) {
1275 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1277 if(only_reachable && !status.reachable) {
1281 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,
1282 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);
1284 if(udp_ping_rtt != -1) {
1285 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1293 case REQ_DUMP_EDGES: {
1294 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);
1297 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1302 float w = 1.0f + 65536.0f / (float)weight;
1304 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1305 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1306 } else if(do_graph == 2) {
1307 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1310 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);
1315 case REQ_DUMP_SUBNETS: {
1316 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1319 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1323 printf("%s owner %s\n", strip_weight(subnet), node);
1327 case REQ_DUMP_CONNECTIONS: {
1328 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1331 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1335 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1340 fprintf(stderr, "Unable to parse dump from tincd.\n");
1345 fprintf(stderr, "Error receiving dump.\n");
1349 static int cmd_purge(int argc, char *argv[]) {
1353 fprintf(stderr, "Too many arguments!\n");
1357 if(!connect_tincd(true)) {
1361 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1363 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1364 fprintf(stderr, "Could not purge old information.\n");
1371 static int cmd_debug(int argc, char *argv[]) {
1373 fprintf(stderr, "Invalid number of arguments.\n");
1377 if(!connect_tincd(true)) {
1381 int debuglevel = atoi(argv[1]);
1384 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1386 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1387 fprintf(stderr, "Could not set debug level.\n");
1391 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1395 static int cmd_retry(int argc, char *argv[]) {
1399 fprintf(stderr, "Too many arguments!\n");
1403 if(!connect_tincd(true)) {
1407 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1409 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1410 fprintf(stderr, "Could not retry outgoing connections.\n");
1417 static int cmd_connect(int argc, char *argv[]) {
1419 fprintf(stderr, "Invalid number of arguments.\n");
1423 if(!check_id(argv[1])) {
1424 fprintf(stderr, "Invalid name for node.\n");
1428 if(!connect_tincd(true)) {
1432 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1434 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1435 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1442 static int cmd_disconnect(int argc, char *argv[]) {
1444 fprintf(stderr, "Invalid number of arguments.\n");
1448 if(!check_id(argv[1])) {
1449 fprintf(stderr, "Invalid name for node.\n");
1453 if(!connect_tincd(true)) {
1457 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1459 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1460 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1467 static int cmd_top(int argc, char *argv[]) {
1471 fprintf(stderr, "Too many arguments!\n");
1477 if(!connect_tincd(true)) {
1484 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1489 static int cmd_pcap(int argc, char *argv[]) {
1491 fprintf(stderr, "Too many arguments!\n");
1495 if(!connect_tincd(true)) {
1499 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1504 static void sigint_handler(int sig) {
1507 fprintf(stderr, "\n");
1508 shutdown(fd, SHUT_RDWR);
1512 static int cmd_log(int argc, char *argv[]) {
1514 fprintf(stderr, "Too many arguments!\n");
1518 if(!connect_tincd(true)) {
1523 signal(SIGINT, sigint_handler);
1526 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1529 signal(SIGINT, SIG_DFL);
1537 static int cmd_pid(int argc, char *argv[]) {
1541 fprintf(stderr, "Too many arguments!\n");
1545 if(!connect_tincd(true) || !pid) {
1549 printf("%d\n", pid);
1553 size_t rstrip(char *value) {
1554 size_t len = strlen(value);
1556 while(len && strchr("\t\r\n ", value[len - 1])) {
1563 char *get_my_name(bool verbose) {
1564 FILE *f = fopen(tinc_conf, "r");
1568 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1577 while(fgets(buf, sizeof(buf), f)) {
1578 size_t len = strcspn(buf, "\t =");
1580 value += strspn(value, "\t ");
1584 value += strspn(value, "\t ");
1587 if(!rstrip(value)) {
1593 if(strcasecmp(buf, "Name")) {
1599 return replace_name(value);
1606 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1612 ecdsa_t *get_pubkey(FILE *f) {
1616 while(fgets(buf, sizeof(buf), f)) {
1617 size_t len = strcspn(buf, "\t =");
1619 value += strspn(value, "\t ");
1623 value += strspn(value, "\t ");
1626 if(!rstrip(value)) {
1632 if(strcasecmp(buf, "Ed25519PublicKey")) {
1637 return ecdsa_set_base64_public_key(value);
1644 const var_t variables[] = {
1645 /* Server configuration */
1646 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1647 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1648 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1649 {"BindToInterface", VAR_SERVER},
1650 {"Broadcast", VAR_SERVER | VAR_SAFE},
1651 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1652 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1653 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1654 {"Device", VAR_SERVER},
1655 {"DeviceStandby", VAR_SERVER},
1656 {"DeviceType", VAR_SERVER},
1657 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1658 {"Ed25519PrivateKeyFile", VAR_SERVER},
1659 {"ExperimentalProtocol", VAR_SERVER},
1660 {"Forwarding", VAR_SERVER},
1661 {"FWMark", VAR_SERVER},
1662 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1663 {"Hostnames", VAR_SERVER},
1664 {"IffOneQueue", VAR_SERVER},
1665 {"Interface", VAR_SERVER},
1666 {"InvitationExpire", VAR_SERVER},
1667 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1668 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1669 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1670 {"LogLevel", VAR_SERVER},
1671 {"MACExpire", VAR_SERVER | VAR_SAFE},
1672 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1673 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1674 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1675 {"Mode", VAR_SERVER | VAR_SAFE},
1676 {"Name", VAR_SERVER},
1677 {"PingInterval", VAR_SERVER | VAR_SAFE},
1678 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1679 {"PriorityInheritance", VAR_SERVER},
1680 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1681 {"PrivateKeyFile", VAR_SERVER},
1682 {"ProcessPriority", VAR_SERVER},
1683 {"Proxy", VAR_SERVER},
1684 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1685 {"ScriptsExtension", VAR_SERVER},
1686 {"ScriptsInterpreter", VAR_SERVER},
1687 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1688 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1689 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1690 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1691 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1692 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1693 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1694 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1695 {"UDPRcvBuf", VAR_SERVER},
1696 {"UDPSndBuf", VAR_SERVER},
1697 {"UPnP", VAR_SERVER},
1698 {"UPnPDiscoverWait", VAR_SERVER},
1699 {"UPnPRefreshPeriod", VAR_SERVER},
1700 {"VDEGroup", VAR_SERVER},
1701 {"VDEPort", VAR_SERVER},
1702 /* Host configuration */
1703 {"Address", VAR_HOST | VAR_MULTIPLE},
1704 {"Cipher", VAR_SERVER | VAR_HOST},
1705 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1706 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1707 {"Digest", VAR_SERVER | VAR_HOST},
1708 {"Ed25519PublicKey", VAR_HOST},
1709 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1710 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1711 {"MACLength", VAR_SERVER | VAR_HOST},
1712 {"PMTU", VAR_SERVER | VAR_HOST},
1713 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1715 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1716 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1717 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1718 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1719 {"Weight", VAR_HOST | VAR_SAFE},
1723 static int cmd_config(int argc, char *argv[]) {
1725 fprintf(stderr, "Invalid number of arguments.\n");
1729 if(strcasecmp(argv[0], "config")) {
1733 typedef enum { GET, DEL, SET, ADD } action_t;
1734 action_t action = GET;
1736 if(!strcasecmp(argv[1], "get")) {
1738 } else if(!strcasecmp(argv[1], "add")) {
1739 argv++, argc--, action = ADD;
1740 } else if(!strcasecmp(argv[1], "del")) {
1741 argv++, argc--, action = DEL;
1742 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1743 argv++, argc--, action = SET;
1747 fprintf(stderr, "Invalid number of arguments.\n");
1751 // Concatenate the rest of the command line
1752 strncpy(line, argv[1], sizeof(line) - 1);
1754 for(int i = 2; i < argc; i++) {
1755 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1756 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1759 // Liberal parsing into node name, variable name and value.
1765 len = strcspn(line, "\t =");
1767 value += strspn(value, "\t ");
1771 value += strspn(value, "\t ");
1775 variable = strchr(line, '.');
1785 fprintf(stderr, "No variable given.\n");
1789 if((action == SET || action == ADD) && !*value) {
1790 fprintf(stderr, "No value for variable given.\n");
1794 if(action == GET && *value) {
1798 /* Some simple checks. */
1800 bool warnonremove = false;
1802 for(int i = 0; variables[i].name; i++) {
1803 if(strcasecmp(variables[i].name, variable)) {
1808 variable = (char *)variables[i].name;
1810 if(!strcasecmp(variable, "Subnet") && *value) {
1813 if(!str2net(&s, value)) {
1814 fprintf(stderr, "Malformed subnet definition %s\n", value);
1818 if(!subnetcheck(s)) {
1819 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1824 /* Discourage use of obsolete variables. */
1826 if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
1828 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1830 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1835 /* Don't put server variables in host config files */
1837 if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
1839 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1841 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1846 /* Should this go into our own host config file? */
1848 if(!node && !(variables[i].type & VAR_SERVER)) {
1849 node = get_my_name(true);
1856 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1857 Turn on warnings when it seems variables might be removed unintentionally. */
1859 if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
1860 warnonremove = true;
1862 } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
1863 warnonremove = true;
1869 if(node && !check_id(node)) {
1870 fprintf(stderr, "Invalid name for node.\n");
1880 if(force || action == GET || action == DEL) {
1881 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1883 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1885 if(node && node != line) {
1893 // Open the right configuration file.
1894 char filename[PATH_MAX];
1897 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node) >= sizeof(filename)) {
1898 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1908 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1911 FILE *f = fopen(filename, "r");
1914 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1918 char tmpfile[PATH_MAX];
1922 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1923 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1927 tf = fopen(tmpfile, "w");
1930 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1936 // Copy the file, making modifications on the fly, unless we are just getting a value.
1940 bool removed = false;
1943 while(fgets(buf1, sizeof(buf1), f)) {
1944 buf1[sizeof(buf1) - 1] = 0;
1945 strncpy(buf2, buf1, sizeof(buf2));
1947 // Parse line in a simple way
1950 size_t len = strcspn(buf2, "\t =");
1951 bvalue = buf2 + len;
1952 bvalue += strspn(bvalue, "\t ");
1954 if(*bvalue == '=') {
1956 bvalue += strspn(bvalue, "\t ");
1963 if(!strcasecmp(buf2, variable)) {
1966 printf("%s\n", bvalue);
1967 } else if(action == DEL) {
1968 if(!*value || !strcasecmp(bvalue, value)) {
1972 } else if(action == SET) {
1973 // Warn if "set" was used for variables that can occur multiple times
1974 if(warnonremove && strcasecmp(bvalue, value)) {
1975 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1978 // Already set? Delete the rest...
1983 // Otherwise, replace.
1984 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1985 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1991 } else if(action == ADD) {
1992 // Check if we've already seen this variable with the same value
1993 if(!strcasecmp(bvalue, value)) {
2000 // Copy original line...
2001 if(fputs(buf1, tf) < 0) {
2002 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2006 // Add newline if it is missing...
2007 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2008 if(fputc('\n', tf) < 0) {
2009 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2016 // Make sure we read everything...
2017 if(ferror(f) || !feof(f)) {
2018 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2023 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2027 // Add new variable if necessary.
2028 if((action == ADD && !found) || (action == SET && !set)) {
2029 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2030 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2039 fprintf(stderr, "No matching configuration variables found.\n");
2044 // Make sure we wrote everything...
2046 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2050 // Could we find what we had to remove?
2051 if((action == GET || action == DEL) && !removed) {
2053 fprintf(stderr, "No configuration variables deleted.\n");
2057 // Replace the configuration file with the new one
2060 if(remove(filename)) {
2061 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2067 if(rename(tmpfile, filename)) {
2068 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2072 // Silently try notifying a running tincd of changes.
2073 if(connect_tincd(false)) {
2074 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2080 static bool try_bind(int port) {
2081 struct addrinfo *ai = NULL, *aip;
2082 struct addrinfo hint = {
2083 .ai_flags = AI_PASSIVE,
2084 .ai_family = AF_UNSPEC,
2085 .ai_socktype = SOCK_STREAM,
2086 .ai_protocol = IPPROTO_TCP,
2089 bool success = true;
2091 snprintf(portstr, sizeof(portstr), "%d", port);
2093 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2097 for(aip = ai; aip; aip = aip->ai_next) {
2098 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2105 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2118 int check_port(const char *name) {
2123 fprintf(stderr, "Warning: could not bind to port 655. ");
2125 for(int i = 0; i < 100; i++) {
2126 uint16_t port = 0x1000 + prng(0x8000);
2128 if(try_bind(port)) {
2129 char filename[PATH_MAX];
2130 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2131 FILE *f = fopen(filename, "a");
2134 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2135 fprintf(stderr, "Please change tinc's Port manually.\n");
2139 fprintf(f, "Port = %d\n", port);
2141 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2146 fprintf(stderr, "Please change tinc's Port manually.\n");
2150 static int cmd_init(int argc, char *argv[]) {
2151 if(!access(tinc_conf, F_OK)) {
2152 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2157 fprintf(stderr, "Too many arguments!\n");
2159 } else if(argc < 2) {
2162 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2164 if(!fgets(buf, sizeof(buf), stdin)) {
2165 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2169 size_t len = rstrip(buf);
2172 fprintf(stderr, "No name given!\n");
2178 fprintf(stderr, "No Name given!\n");
2182 name = strdup(argv[1]);
2185 fprintf(stderr, "No Name given!\n");
2190 if(!check_id(name)) {
2191 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2195 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2196 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2200 if(mkdir(confbase, 0777) && errno != EEXIST) {
2201 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2205 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2206 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2210 FILE *f = fopen(tinc_conf, "w");
2213 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2217 fprintf(f, "Name = %s\n", name);
2220 #ifndef DISABLE_LEGACY
2222 if(!rsa_keygen(2048, false)) {
2228 if(!ed25519_keygen(false)) {
2234 #ifndef HAVE_WINDOWS
2235 char filename[PATH_MAX];
2236 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2238 if(access(filename, F_OK)) {
2239 FILE *f = fopenmask(filename, "w", 0777);
2242 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2246 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");
2256 static int cmd_generate_keys(int argc, char *argv[]) {
2257 #ifdef DISABLE_LEGACY
2265 fprintf(stderr, "Too many arguments!\n");
2270 name = get_my_name(false);
2273 #ifndef DISABLE_LEGACY
2275 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2281 if(!ed25519_keygen(true)) {
2288 #ifndef DISABLE_LEGACY
2289 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2291 fprintf(stderr, "Too many arguments!\n");
2296 name = get_my_name(false);
2299 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2303 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2307 fprintf(stderr, "Too many arguments!\n");
2312 name = get_my_name(false);
2315 return !ed25519_keygen(true);
2318 static int cmd_help(int argc, char *argv[]) {
2326 static int cmd_version(int argc, char *argv[]) {
2330 fprintf(stderr, "Too many arguments!\n");
2338 static int cmd_info(int argc, char *argv[]) {
2340 fprintf(stderr, "Invalid number of arguments.\n");
2344 if(!connect_tincd(true)) {
2348 return info(fd, argv[1]);
2351 static const char *conffiles[] = {
2362 static int cmd_edit(int argc, char *argv[]) {
2364 fprintf(stderr, "Invalid number of arguments.\n");
2368 char filename[PATH_MAX] = "";
2370 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2371 for(int i = 0; conffiles[i]; i++) {
2372 if(!strcmp(argv[1], conffiles[i])) {
2373 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2382 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2383 char *dash = strchr(argv[1], '-');
2388 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2389 fprintf(stderr, "Invalid configuration filename.\n");
2396 #ifndef HAVE_WINDOWS
2397 const char *editor = getenv("VISUAL");
2400 editor = getenv("EDITOR");
2407 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2409 xasprintf(&command, "edit \"%s\"", filename);
2411 int result = system(command);
2418 // Silently try notifying a running tincd of changes.
2419 if(connect_tincd(false)) {
2420 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2426 static int export(const char *name, FILE *out) {
2427 char filename[PATH_MAX];
2428 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2429 FILE *in = fopen(filename, "r");
2432 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2436 fprintf(out, "Name = %s\n", name);
2439 while(fgets(buf, sizeof(buf), in)) {
2440 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2446 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2455 static int cmd_export(int argc, char *argv[]) {
2459 fprintf(stderr, "Too many arguments!\n");
2463 char *name = get_my_name(true);
2469 int result = export(name, stdout);
2479 static int cmd_export_all(int argc, char *argv[]) {
2483 fprintf(stderr, "Too many arguments!\n");
2487 DIR *dir = opendir(hosts_dir);
2490 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2498 while((ent = readdir(dir))) {
2499 if(!check_id(ent->d_name)) {
2506 printf("#---------------------------------------------------------------#\n");
2509 result |= export(ent->d_name, stdout);
2521 static int cmd_import(int argc, char *argv[]) {
2525 fprintf(stderr, "Too many arguments!\n");
2534 char filename[PATH_MAX] = "";
2536 bool firstline = true;
2538 while(fgets(buf, sizeof(buf), in)) {
2539 if(sscanf(buf, "Name = %4095s", name) == 1) {
2542 if(!check_id(name)) {
2543 fprintf(stderr, "Invalid Name in input!\n");
2551 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2552 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2556 if(!force && !access(filename, F_OK)) {
2557 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2562 out = fopen(filename, "w");
2565 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2571 } else if(firstline) {
2572 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2577 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2582 if(fputs(buf, out) < 0) {
2583 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2594 fprintf(stderr, "Imported %d host configuration files.\n", count);
2597 fprintf(stderr, "No host configuration files imported.\n");
2602 static int cmd_exchange(int argc, char *argv[]) {
2603 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2606 static int cmd_exchange_all(int argc, char *argv[]) {
2607 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2610 static int switch_network(char *name) {
2611 if(strcmp(name, ".")) {
2612 if(!check_netname(name, false)) {
2613 fprintf(stderr, "Invalid character in netname!\n");
2617 if(!check_netname(name, true)) {
2618 fprintf(stderr, "Warning: unsafe character in netname!\n");
2628 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2635 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2636 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2637 xasprintf(&prompt, "%s> ", identname);
2642 static int cmd_network(int argc, char *argv[]) {
2644 fprintf(stderr, "Too many arguments!\n");
2649 return switch_network(argv[1]);
2652 DIR *dir = opendir(confdir);
2655 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2661 while((ent = readdir(dir))) {
2662 if(*ent->d_name == '.') {
2666 if(!strcmp(ent->d_name, "tinc.conf")) {
2671 char fname[PATH_MAX];
2672 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2674 if(!access(fname, R_OK)) {
2675 printf("%s\n", ent->d_name);
2684 static int cmd_fsck(int argc, char *argv[]) {
2688 fprintf(stderr, "Too many arguments!\n");
2692 return fsck(orig_argv[0]);
2695 static void *readfile(FILE *in, size_t *len) {
2697 size_t bufsize = 4096;
2698 char *buf = xmalloc(bufsize);
2701 size_t read = fread(buf + count, 1, bufsize - count, in);
2709 if(count >= bufsize) {
2711 buf = xrealloc(buf, bufsize);
2722 static int cmd_sign(int argc, char *argv[]) {
2724 fprintf(stderr, "Too many arguments!\n");
2729 name = get_my_name(true);
2736 char fname[PATH_MAX];
2737 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2738 FILE *fp = fopen(fname, "r");
2741 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2745 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2748 fprintf(stderr, "Could not read private key from %s\n", fname);
2758 in = fopen(argv[1], "rb");
2761 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2770 char *data = readfile(in, &len);
2777 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2782 // Ensure we sign our name and current time as well
2783 long t = time(NULL);
2785 xasprintf(&trailer, " %s %ld", name, t);
2786 size_t trailer_len = strlen(trailer);
2788 data = xrealloc(data, len + trailer_len);
2789 memcpy(data + len, trailer, trailer_len);
2794 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2795 fprintf(stderr, "Error generating signature\n");
2801 b64encode_tinc(sig, sig, 64);
2804 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2805 fwrite(data, len, 1, stdout);
2811 static int cmd_verify(int argc, char *argv[]) {
2813 fprintf(stderr, "Not enough arguments!\n");
2818 fprintf(stderr, "Too many arguments!\n");
2822 char *node = argv[1];
2824 if(!strcmp(node, ".")) {
2826 name = get_my_name(true);
2834 } else if(!strcmp(node, "*")) {
2837 if(!check_id(node)) {
2838 fprintf(stderr, "Invalid node name\n");
2846 in = fopen(argv[2], "rb");
2849 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2857 char *data = readfile(in, &len);
2864 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2868 char *newline = memchr(data, '\n', len);
2870 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2871 fprintf(stderr, "Invalid input\n");
2877 size_t skip = newline - data;
2879 char signer[MAX_STRING_SIZE] = "";
2880 char sig[MAX_STRING_SIZE] = "";
2883 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2884 fprintf(stderr, "Invalid input\n");
2889 if(node && strcmp(node, signer)) {
2890 fprintf(stderr, "Signature is not made by %s\n", node);
2900 xasprintf(&trailer, " %s %ld", signer, t);
2901 size_t trailer_len = strlen(trailer);
2903 data = xrealloc(data, len + trailer_len);
2904 memcpy(data + len, trailer, trailer_len);
2907 newline = data + skip;
2909 char fname[PATH_MAX];
2910 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2911 FILE *fp = fopen(fname, "r");
2914 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2919 ecdsa_t *key = get_pubkey(fp);
2923 key = ecdsa_read_pem_public_key(fp);
2927 fprintf(stderr, "Could not read public key from %s\n", fname);
2935 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2936 fprintf(stderr, "Invalid signature\n");
2944 fwrite(newline, len - (newline - data), 1, stdout);
2950 static const struct {
2951 const char *command;
2952 int (*function)(int argc, char *argv[]);
2955 {"start", cmd_start, false},
2956 {"stop", cmd_stop, false},
2957 {"restart", cmd_restart, false},
2958 {"reload", cmd_reload, false},
2959 {"dump", cmd_dump, false},
2960 {"list", cmd_dump, false},
2961 {"purge", cmd_purge, false},
2962 {"debug", cmd_debug, false},
2963 {"retry", cmd_retry, false},
2964 {"connect", cmd_connect, false},
2965 {"disconnect", cmd_disconnect, false},
2966 {"top", cmd_top, false},
2967 {"pcap", cmd_pcap, false},
2968 {"log", cmd_log, false},
2969 {"pid", cmd_pid, false},
2970 {"config", cmd_config, true},
2971 {"add", cmd_config, false},
2972 {"del", cmd_config, false},
2973 {"get", cmd_config, false},
2974 {"set", cmd_config, false},
2975 {"init", cmd_init, false},
2976 {"generate-keys", cmd_generate_keys, false},
2977 #ifndef DISABLE_LEGACY
2978 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
2980 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
2981 {"help", cmd_help, false},
2982 {"version", cmd_version, false},
2983 {"info", cmd_info, false},
2984 {"edit", cmd_edit, false},
2985 {"export", cmd_export, false},
2986 {"export-all", cmd_export_all, false},
2987 {"import", cmd_import, false},
2988 {"exchange", cmd_exchange, false},
2989 {"exchange-all", cmd_exchange_all, false},
2990 {"invite", cmd_invite, false},
2991 {"join", cmd_join, false},
2992 {"network", cmd_network, false},
2993 {"fsck", cmd_fsck, false},
2994 {"sign", cmd_sign, false},
2995 {"verify", cmd_verify, false},
2996 {NULL, NULL, false},
2999 #ifdef HAVE_READLINE
3000 static char *complete_command(const char *text, int state) {
3009 while(commands[i].command) {
3010 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3011 return xstrdup(commands[i].command);
3020 static char *complete_dump(const char *text, int state) {
3021 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3031 if(!strncasecmp(matches[i], text, strlen(text))) {
3032 return xstrdup(matches[i]);
3041 static char *complete_config(const char *text, int state) {
3050 while(variables[i].name) {
3051 char *dot = strchr(text, '.');
3054 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3056 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3060 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3061 return xstrdup(variables[i].name);
3071 static char *complete_info(const char *text, int state) {
3077 if(!connect_tincd(false)) {
3081 // Check the list of nodes
3082 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3083 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3086 while(recvline(fd, line, sizeof(line))) {
3088 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3101 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3105 if(!strncmp(item, text, strlen(text))) {
3106 return xstrdup(strip_weight(item));
3113 static char *complete_nothing(const char *text, int state) {
3119 static char **completion(const char *text, int start, int end) {
3121 char **matches = NULL;
3124 matches = rl_completion_matches(text, complete_command);
3125 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3126 matches = rl_completion_matches(text, complete_dump);
3127 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3128 matches = rl_completion_matches(text, complete_config);
3129 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3130 matches = rl_completion_matches(text, complete_config);
3131 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3132 matches = rl_completion_matches(text, complete_config);
3133 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3134 matches = rl_completion_matches(text, complete_config);
3135 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3136 matches = rl_completion_matches(text, complete_info);
3143 static int cmd_shell(int argc, char *argv[]) {
3144 xasprintf(&prompt, "%s> ", identname);
3148 int maxargs = argc + 16;
3149 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3151 for(int i = 0; i < argc; i++) {
3155 #ifdef HAVE_READLINE
3156 rl_readline_name = "tinc";
3157 rl_basic_word_break_characters = "\t\n ";
3158 rl_completion_entry_function = complete_nothing;
3159 rl_attempted_completion_function = completion;
3160 rl_filename_completion_desired = 0;
3165 #ifdef HAVE_READLINE
3170 line = readline(prompt);
3171 copy = line ? xstrdup(line) : NULL;
3173 line = fgets(buf, sizeof(buf), stdin);
3179 fputs(prompt, stdout);
3182 line = fgets(buf, sizeof(buf), stdin);
3189 /* Ignore comments */
3198 char *p = line + strspn(line, " \t\n");
3199 char *next = strtok(p, " \t\n");
3202 if(nargc >= maxargs) {
3204 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3209 next = strtok(NULL, " \t\n");
3216 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3217 #ifdef HAVE_READLINE
3226 for(int i = 0; commands[i].command; i++) {
3227 if(!strcasecmp(nargv[argc], commands[i].command)) {
3228 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3234 #ifdef HAVE_READLINE
3243 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3248 #ifdef HAVE_READLINE
3260 static void cleanup(void) {
3266 static int run_command(int argc, char *argv[]) {
3267 if(optind >= argc) {
3268 return cmd_shell(argc, argv);
3271 for(int i = 0; commands[i].command; i++) {
3272 if(!strcasecmp(argv[optind], commands[i].command)) {
3273 return commands[i].function(argc - optind, argv + optind);
3277 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3282 int main(int argc, char *argv[]) {
3283 program_name = argv[0];
3285 tty = isatty(0) && isatty(1);
3287 if(!parse_options(argc, argv)) {
3292 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3293 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3307 static struct WSAData wsa_state;
3309 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3310 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3316 gettimeofday(&now, NULL);
3321 int result = run_command(argc, argv);