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")) {
1735 if(!strcasecmp(argv[1], "get")) {
1737 } else if(!strcasecmp(argv[1], "add")) {
1738 argv++, argc--, action = 1;
1739 } else if(!strcasecmp(argv[1], "del")) {
1740 argv++, argc--, action = -1;
1741 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1742 argv++, argc--, action = 0;
1746 fprintf(stderr, "Invalid number of arguments.\n");
1750 // Concatenate the rest of the command line
1751 strncpy(line, argv[1], sizeof(line) - 1);
1753 for(int i = 2; i < argc; i++) {
1754 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1755 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1758 // Liberal parsing into node name, variable name and value.
1764 len = strcspn(line, "\t =");
1766 value += strspn(value, "\t ");
1770 value += strspn(value, "\t ");
1774 variable = strchr(line, '.');
1784 fprintf(stderr, "No variable given.\n");
1788 if(action >= 0 && !*value) {
1789 fprintf(stderr, "No value for variable given.\n");
1793 if(action < -1 && *value) {
1797 /* Some simple checks. */
1799 bool warnonremove = false;
1801 for(int i = 0; variables[i].name; i++) {
1802 if(strcasecmp(variables[i].name, variable)) {
1807 variable = (char *)variables[i].name;
1809 if(!strcasecmp(variable, "Subnet") && *value) {
1812 if(!str2net(&s, value)) {
1813 fprintf(stderr, "Malformed subnet definition %s\n", value);
1817 if(!subnetcheck(s)) {
1818 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1823 /* Discourage use of obsolete variables. */
1825 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1827 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1829 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1834 /* Don't put server variables in host config files */
1836 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1838 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1840 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1845 /* Should this go into our own host config file? */
1847 if(!node && !(variables[i].type & VAR_SERVER)) {
1848 node = get_my_name(true);
1855 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1856 Turn on warnings when it seems variables might be removed unintentionally. */
1858 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1859 warnonremove = true;
1861 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1862 warnonremove = true;
1868 if(node && !check_id(node)) {
1869 fprintf(stderr, "Invalid name for node.\n");
1879 if(force || action < 0) {
1880 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1882 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1884 if(node && node != line) {
1892 // Open the right configuration file.
1893 char filename[PATH_MAX];
1896 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node) >= sizeof(filename)) {
1897 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1907 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1910 FILE *f = fopen(filename, "r");
1913 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1917 char tmpfile[PATH_MAX];
1921 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1922 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1926 tf = fopen(tmpfile, "w");
1929 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1935 // Copy the file, making modifications on the fly, unless we are just getting a value.
1939 bool removed = false;
1942 while(fgets(buf1, sizeof(buf1), f)) {
1943 buf1[sizeof(buf1) - 1] = 0;
1944 strncpy(buf2, buf1, sizeof(buf2));
1946 // Parse line in a simple way
1949 size_t len = strcspn(buf2, "\t =");
1950 bvalue = buf2 + len;
1951 bvalue += strspn(bvalue, "\t ");
1953 if(*bvalue == '=') {
1955 bvalue += strspn(bvalue, "\t ");
1962 if(!strcasecmp(buf2, variable)) {
1966 printf("%s\n", bvalue);
1968 } else if(action == -1) {
1969 if(!*value || !strcasecmp(bvalue, value)) {
1975 } else if(action == 0) {
1976 // Warn if "set" was used for variables that can occur multiple times
1977 if(warnonremove && strcasecmp(bvalue, value)) {
1978 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1981 // Already set? Delete the rest...
1986 // Otherwise, replace.
1987 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1988 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1995 } else if(action > 0) {
1996 // Check if we've already seen this variable with the same value
1997 if(!strcasecmp(bvalue, value)) {
2004 // Copy original line...
2005 if(fputs(buf1, tf) < 0) {
2006 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2010 // Add newline if it is missing...
2011 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2012 if(fputc('\n', tf) < 0) {
2013 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2020 // Make sure we read everything...
2021 if(ferror(f) || !feof(f)) {
2022 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2027 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2031 // Add new variable if necessary.
2032 if((action > 0 && !found) || (action == 0 && !set)) {
2033 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2034 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2043 fprintf(stderr, "No matching configuration variables found.\n");
2048 // Make sure we wrote everything...
2050 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2054 // Could we find what we had to remove?
2055 if(action < 0 && !removed) {
2057 fprintf(stderr, "No configuration variables deleted.\n");
2061 // Replace the configuration file with the new one
2064 if(remove(filename)) {
2065 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2071 if(rename(tmpfile, filename)) {
2072 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2076 // Silently try notifying a running tincd of changes.
2077 if(connect_tincd(false)) {
2078 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2084 static bool try_bind(int port) {
2085 struct addrinfo *ai = NULL, *aip;
2086 struct addrinfo hint = {
2087 .ai_flags = AI_PASSIVE,
2088 .ai_family = AF_UNSPEC,
2089 .ai_socktype = SOCK_STREAM,
2090 .ai_protocol = IPPROTO_TCP,
2093 bool success = true;
2095 snprintf(portstr, sizeof(portstr), "%d", port);
2097 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2101 for(aip = ai; aip; aip = aip->ai_next) {
2102 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2109 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2122 int check_port(const char *name) {
2127 fprintf(stderr, "Warning: could not bind to port 655. ");
2129 for(int i = 0; i < 100; i++) {
2130 uint16_t port = 0x1000 + prng(0x8000);
2132 if(try_bind(port)) {
2133 char filename[PATH_MAX];
2134 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2135 FILE *f = fopen(filename, "a");
2138 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2139 fprintf(stderr, "Please change tinc's Port manually.\n");
2143 fprintf(f, "Port = %d\n", port);
2145 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2150 fprintf(stderr, "Please change tinc's Port manually.\n");
2154 static int cmd_init(int argc, char *argv[]) {
2155 if(!access(tinc_conf, F_OK)) {
2156 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2161 fprintf(stderr, "Too many arguments!\n");
2163 } else if(argc < 2) {
2166 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2168 if(!fgets(buf, sizeof(buf), stdin)) {
2169 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2173 size_t len = rstrip(buf);
2176 fprintf(stderr, "No name given!\n");
2182 fprintf(stderr, "No Name given!\n");
2186 name = strdup(argv[1]);
2189 fprintf(stderr, "No Name given!\n");
2194 if(!check_id(name)) {
2195 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2199 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2200 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2204 if(mkdir(confbase, 0777) && errno != EEXIST) {
2205 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2209 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2210 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2214 FILE *f = fopen(tinc_conf, "w");
2217 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2221 fprintf(f, "Name = %s\n", name);
2224 #ifndef DISABLE_LEGACY
2226 if(!rsa_keygen(2048, false)) {
2232 if(!ed25519_keygen(false)) {
2238 #ifndef HAVE_WINDOWS
2239 char filename[PATH_MAX];
2240 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2242 if(access(filename, F_OK)) {
2243 FILE *f = fopenmask(filename, "w", 0777);
2246 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2250 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");
2260 static int cmd_generate_keys(int argc, char *argv[]) {
2261 #ifdef DISABLE_LEGACY
2269 fprintf(stderr, "Too many arguments!\n");
2274 name = get_my_name(false);
2277 #ifndef DISABLE_LEGACY
2279 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2285 if(!ed25519_keygen(true)) {
2292 #ifndef DISABLE_LEGACY
2293 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2295 fprintf(stderr, "Too many arguments!\n");
2300 name = get_my_name(false);
2303 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2307 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2311 fprintf(stderr, "Too many arguments!\n");
2316 name = get_my_name(false);
2319 return !ed25519_keygen(true);
2322 static int cmd_help(int argc, char *argv[]) {
2330 static int cmd_version(int argc, char *argv[]) {
2334 fprintf(stderr, "Too many arguments!\n");
2342 static int cmd_info(int argc, char *argv[]) {
2344 fprintf(stderr, "Invalid number of arguments.\n");
2348 if(!connect_tincd(true)) {
2352 return info(fd, argv[1]);
2355 static const char *conffiles[] = {
2366 static int cmd_edit(int argc, char *argv[]) {
2368 fprintf(stderr, "Invalid number of arguments.\n");
2372 char filename[PATH_MAX] = "";
2374 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2375 for(int i = 0; conffiles[i]; i++) {
2376 if(!strcmp(argv[1], conffiles[i])) {
2377 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2386 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2387 char *dash = strchr(argv[1], '-');
2392 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2393 fprintf(stderr, "Invalid configuration filename.\n");
2400 #ifndef HAVE_WINDOWS
2401 const char *editor = getenv("VISUAL");
2404 editor = getenv("EDITOR");
2411 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2413 xasprintf(&command, "edit \"%s\"", filename);
2415 int result = system(command);
2422 // Silently try notifying a running tincd of changes.
2423 if(connect_tincd(false)) {
2424 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2430 static int export(const char *name, FILE *out) {
2431 char filename[PATH_MAX];
2432 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2433 FILE *in = fopen(filename, "r");
2436 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2440 fprintf(out, "Name = %s\n", name);
2443 while(fgets(buf, sizeof(buf), in)) {
2444 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2450 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2459 static int cmd_export(int argc, char *argv[]) {
2463 fprintf(stderr, "Too many arguments!\n");
2467 char *name = get_my_name(true);
2473 int result = export(name, stdout);
2483 static int cmd_export_all(int argc, char *argv[]) {
2487 fprintf(stderr, "Too many arguments!\n");
2491 DIR *dir = opendir(hosts_dir);
2494 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2502 while((ent = readdir(dir))) {
2503 if(!check_id(ent->d_name)) {
2510 printf("#---------------------------------------------------------------#\n");
2513 result |= export(ent->d_name, stdout);
2525 static int cmd_import(int argc, char *argv[]) {
2529 fprintf(stderr, "Too many arguments!\n");
2538 char filename[PATH_MAX] = "";
2540 bool firstline = true;
2542 while(fgets(buf, sizeof(buf), in)) {
2543 if(sscanf(buf, "Name = %4095s", name) == 1) {
2546 if(!check_id(name)) {
2547 fprintf(stderr, "Invalid Name in input!\n");
2555 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2556 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2560 if(!force && !access(filename, F_OK)) {
2561 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2566 out = fopen(filename, "w");
2569 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2575 } else if(firstline) {
2576 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2581 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2586 if(fputs(buf, out) < 0) {
2587 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2598 fprintf(stderr, "Imported %d host configuration files.\n", count);
2601 fprintf(stderr, "No host configuration files imported.\n");
2606 static int cmd_exchange(int argc, char *argv[]) {
2607 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2610 static int cmd_exchange_all(int argc, char *argv[]) {
2611 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2614 static int switch_network(char *name) {
2615 if(strcmp(name, ".")) {
2616 if(!check_netname(name, false)) {
2617 fprintf(stderr, "Invalid character in netname!\n");
2621 if(!check_netname(name, true)) {
2622 fprintf(stderr, "Warning: unsafe character in netname!\n");
2632 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2639 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2640 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2641 xasprintf(&prompt, "%s> ", identname);
2646 static int cmd_network(int argc, char *argv[]) {
2648 fprintf(stderr, "Too many arguments!\n");
2653 return switch_network(argv[1]);
2656 DIR *dir = opendir(confdir);
2659 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2665 while((ent = readdir(dir))) {
2666 if(*ent->d_name == '.') {
2670 if(!strcmp(ent->d_name, "tinc.conf")) {
2675 char fname[PATH_MAX];
2676 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2678 if(!access(fname, R_OK)) {
2679 printf("%s\n", ent->d_name);
2688 static int cmd_fsck(int argc, char *argv[]) {
2692 fprintf(stderr, "Too many arguments!\n");
2696 return fsck(orig_argv[0]);
2699 static void *readfile(FILE *in, size_t *len) {
2701 size_t bufsize = 4096;
2702 char *buf = xmalloc(bufsize);
2705 size_t read = fread(buf + count, 1, bufsize - count, in);
2713 if(count >= bufsize) {
2715 buf = xrealloc(buf, bufsize);
2726 static int cmd_sign(int argc, char *argv[]) {
2728 fprintf(stderr, "Too many arguments!\n");
2733 name = get_my_name(true);
2740 char fname[PATH_MAX];
2741 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2742 FILE *fp = fopen(fname, "r");
2745 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2749 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2752 fprintf(stderr, "Could not read private key from %s\n", fname);
2762 in = fopen(argv[1], "rb");
2765 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2774 char *data = readfile(in, &len);
2781 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2786 // Ensure we sign our name and current time as well
2787 long t = time(NULL);
2789 xasprintf(&trailer, " %s %ld", name, t);
2790 size_t trailer_len = strlen(trailer);
2792 data = xrealloc(data, len + trailer_len);
2793 memcpy(data + len, trailer, trailer_len);
2798 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2799 fprintf(stderr, "Error generating signature\n");
2805 b64encode_tinc(sig, sig, 64);
2808 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2809 fwrite(data, len, 1, stdout);
2815 static int cmd_verify(int argc, char *argv[]) {
2817 fprintf(stderr, "Not enough arguments!\n");
2822 fprintf(stderr, "Too many arguments!\n");
2826 char *node = argv[1];
2828 if(!strcmp(node, ".")) {
2830 name = get_my_name(true);
2838 } else if(!strcmp(node, "*")) {
2841 if(!check_id(node)) {
2842 fprintf(stderr, "Invalid node name\n");
2850 in = fopen(argv[2], "rb");
2853 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2861 char *data = readfile(in, &len);
2868 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2872 char *newline = memchr(data, '\n', len);
2874 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2875 fprintf(stderr, "Invalid input\n");
2881 size_t skip = newline - data;
2883 char signer[MAX_STRING_SIZE] = "";
2884 char sig[MAX_STRING_SIZE] = "";
2887 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2888 fprintf(stderr, "Invalid input\n");
2893 if(node && strcmp(node, signer)) {
2894 fprintf(stderr, "Signature is not made by %s\n", node);
2904 xasprintf(&trailer, " %s %ld", signer, t);
2905 size_t trailer_len = strlen(trailer);
2907 data = xrealloc(data, len + trailer_len);
2908 memcpy(data + len, trailer, trailer_len);
2911 newline = data + skip;
2913 char fname[PATH_MAX];
2914 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2915 FILE *fp = fopen(fname, "r");
2918 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2923 ecdsa_t *key = get_pubkey(fp);
2927 key = ecdsa_read_pem_public_key(fp);
2931 fprintf(stderr, "Could not read public key from %s\n", fname);
2939 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2940 fprintf(stderr, "Invalid signature\n");
2948 fwrite(newline, len - (newline - data), 1, stdout);
2954 static const struct {
2955 const char *command;
2956 int (*function)(int argc, char *argv[]);
2959 {"start", cmd_start, false},
2960 {"stop", cmd_stop, false},
2961 {"restart", cmd_restart, false},
2962 {"reload", cmd_reload, false},
2963 {"dump", cmd_dump, false},
2964 {"list", cmd_dump, false},
2965 {"purge", cmd_purge, false},
2966 {"debug", cmd_debug, false},
2967 {"retry", cmd_retry, false},
2968 {"connect", cmd_connect, false},
2969 {"disconnect", cmd_disconnect, false},
2970 {"top", cmd_top, false},
2971 {"pcap", cmd_pcap, false},
2972 {"log", cmd_log, false},
2973 {"pid", cmd_pid, false},
2974 {"config", cmd_config, true},
2975 {"add", cmd_config, false},
2976 {"del", cmd_config, false},
2977 {"get", cmd_config, false},
2978 {"set", cmd_config, false},
2979 {"init", cmd_init, false},
2980 {"generate-keys", cmd_generate_keys, false},
2981 #ifndef DISABLE_LEGACY
2982 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
2984 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
2985 {"help", cmd_help, false},
2986 {"version", cmd_version, false},
2987 {"info", cmd_info, false},
2988 {"edit", cmd_edit, false},
2989 {"export", cmd_export, false},
2990 {"export-all", cmd_export_all, false},
2991 {"import", cmd_import, false},
2992 {"exchange", cmd_exchange, false},
2993 {"exchange-all", cmd_exchange_all, false},
2994 {"invite", cmd_invite, false},
2995 {"join", cmd_join, false},
2996 {"network", cmd_network, false},
2997 {"fsck", cmd_fsck, false},
2998 {"sign", cmd_sign, false},
2999 {"verify", cmd_verify, false},
3000 {NULL, NULL, false},
3003 #ifdef HAVE_READLINE
3004 static char *complete_command(const char *text, int state) {
3013 while(commands[i].command) {
3014 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3015 return xstrdup(commands[i].command);
3024 static char *complete_dump(const char *text, int state) {
3025 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3035 if(!strncasecmp(matches[i], text, strlen(text))) {
3036 return xstrdup(matches[i]);
3045 static char *complete_config(const char *text, int state) {
3054 while(variables[i].name) {
3055 char *dot = strchr(text, '.');
3058 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3060 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3064 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3065 return xstrdup(variables[i].name);
3075 static char *complete_info(const char *text, int state) {
3081 if(!connect_tincd(false)) {
3085 // Check the list of nodes
3086 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3087 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3090 while(recvline(fd, line, sizeof(line))) {
3092 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3105 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3109 if(!strncmp(item, text, strlen(text))) {
3110 return xstrdup(strip_weight(item));
3117 static char *complete_nothing(const char *text, int state) {
3123 static char **completion(const char *text, int start, int end) {
3125 char **matches = NULL;
3128 matches = rl_completion_matches(text, complete_command);
3129 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3130 matches = rl_completion_matches(text, complete_dump);
3131 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3132 matches = rl_completion_matches(text, complete_config);
3133 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3134 matches = rl_completion_matches(text, complete_config);
3135 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3136 matches = rl_completion_matches(text, complete_config);
3137 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3138 matches = rl_completion_matches(text, complete_config);
3139 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3140 matches = rl_completion_matches(text, complete_info);
3147 static int cmd_shell(int argc, char *argv[]) {
3148 xasprintf(&prompt, "%s> ", identname);
3152 int maxargs = argc + 16;
3153 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3155 for(int i = 0; i < argc; i++) {
3159 #ifdef HAVE_READLINE
3160 rl_readline_name = "tinc";
3161 rl_basic_word_break_characters = "\t\n ";
3162 rl_completion_entry_function = complete_nothing;
3163 rl_attempted_completion_function = completion;
3164 rl_filename_completion_desired = 0;
3169 #ifdef HAVE_READLINE
3174 line = readline(prompt);
3175 copy = line ? xstrdup(line) : NULL;
3177 line = fgets(buf, sizeof(buf), stdin);
3183 fputs(prompt, stdout);
3186 line = fgets(buf, sizeof(buf), stdin);
3193 /* Ignore comments */
3202 char *p = line + strspn(line, " \t\n");
3203 char *next = strtok(p, " \t\n");
3206 if(nargc >= maxargs) {
3208 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3213 next = strtok(NULL, " \t\n");
3220 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3221 #ifdef HAVE_READLINE
3230 for(int i = 0; commands[i].command; i++) {
3231 if(!strcasecmp(nargv[argc], commands[i].command)) {
3232 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3238 #ifdef HAVE_READLINE
3247 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3252 #ifdef HAVE_READLINE
3264 static void cleanup(void) {
3270 static int run_command(int argc, char *argv[]) {
3271 if(optind >= argc) {
3272 return cmd_shell(argc, argv);
3275 for(int i = 0; commands[i].command; i++) {
3276 if(!strcasecmp(argv[optind], commands[i].command)) {
3277 return commands[i].function(argc - optind, argv + optind);
3281 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3286 int main(int argc, char *argv[]) {
3287 program_name = argv[0];
3289 tty = isatty(0) && isatty(1);
3291 if(!parse_options(argc, argv)) {
3296 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3297 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3311 static struct WSAData wsa_state;
3313 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3314 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3320 gettimeofday(&now, NULL);
3325 int result = run_command(argc, argv);