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"
48 #define MSG_NOSIGNAL 0
51 static char **orig_argv;
53 /* If nonzero, display usage information and exit. */
54 static bool show_help = false;
56 /* If nonzero, print the version on standard output and exit. */
57 static bool show_version = false;
59 static char *name = NULL;
60 static char controlcookie[1025];
61 char *tinc_conf = NULL;
62 char *hosts_dir = NULL;
65 // Horrible global variables...
74 bool confbasegiven = false;
75 char *scriptinterpreter = NULL;
76 static char defaultextension[] = "";
77 char *scriptextension = defaultextension;
83 static struct option const long_options[] = {
84 {"batch", no_argument, NULL, 'b'},
85 {"config", required_argument, NULL, 'c'},
86 {"net", required_argument, NULL, 'n'},
87 {"help", no_argument, NULL, 1},
88 {"version", no_argument, NULL, 2},
89 {"pidfile", required_argument, NULL, 3},
90 {"force", no_argument, NULL, 4},
94 static void version(void) {
95 static const char *message =
96 "%s version %s (built %s %s, protocol %d.%d)\n"
104 #ifndef DISABLE_LEGACY
108 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
109 "See the AUTHORS file for a complete list.\n"
111 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
112 "and you are welcome to redistribute it under certain conditions;\n"
113 "see the file COPYING for details.\n";
115 printf(message, PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
118 static void usage(bool status) {
120 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
122 static const char *message =
123 "Usage: %s [options] command\n"
125 "Valid options are:\n"
126 " -b, --batch Don't ask for anything (non-interactive mode).\n"
127 " -c, --config=DIR Read configuration options from DIR.\n"
128 " -n, --net=NETNAME Connect to net NETNAME.\n"
129 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
130 " --force Force some commands to work despite warnings.\n"
131 " --help Display this help and exit.\n"
132 " --version Output version information and exit.\n"
134 "Valid commands are:\n"
135 " init [name] Create initial configuration files.\n"
136 " get VARIABLE Print current value of VARIABLE\n"
137 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
138 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
139 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
140 " start [tincd options] Start tincd.\n"
141 " stop Stop tincd.\n"
142 " restart [tincd options] Restart tincd.\n"
143 " reload Partially reload configuration of running tincd.\n"
144 " pid Show PID of currently running tincd.\n"
145 #ifdef DISABLE_LEGACY
146 " generate-keys Generate a new Ed25519 public/private key pair.\n"
148 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
149 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
151 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
152 " dump Dump a list of one of the following things:\n"
153 " [reachable] nodes - all known nodes in the VPN\n"
154 " edges - all known connections in the VPN\n"
155 " subnets - all known subnets in the VPN\n"
156 " connections - all meta connections with ourself\n"
157 " [di]graph - graph of the VPN in dotty format\n"
158 " invitations - outstanding invitations\n"
159 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
160 " purge Purge unreachable nodes\n"
161 " debug N Set debug level\n"
162 " retry Retry all outgoing connections\n"
163 " disconnect NODE Close meta connection with NODE\n"
165 " top Show real-time statistics\n"
167 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
168 " log [level] Dump log output [up to the specified level]\n"
169 " export Export host configuration of local node to standard output\n"
170 " export-all Export all host configuration files to standard output\n"
171 " import Import host configuration file(s) from standard input\n"
172 " exchange Same as export followed by import\n"
173 " exchange-all Same as export-all followed by import\n"
174 " invite NODE [...] Generate an invitation for NODE\n"
175 " join INVITATION Join a VPN using an INVITATION\n"
176 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
177 " fsck Check the configuration files for problems.\n"
178 " sign [FILE] Generate a signed version of a file.\n"
179 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
181 "Report bugs to tinc@tinc-vpn.org.\n";
183 printf(message, program_name);
187 static bool parse_options(int argc, char **argv) {
189 int option_index = 0;
191 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
193 case 0: /* long option */
200 case 'c': /* config file */
202 confbase = xstrdup(optarg);
203 confbasegiven = true;
206 case 'n': /* net name given */
208 netname = xstrdup(optarg);
211 case 1: /* show help */
215 case 2: /* show version */
219 case 3: /* open control socket here */
221 pidfilename = xstrdup(optarg);
228 case '?': /* wrong options */
238 if(!netname && (netname = getenv("NETNAME"))) {
239 netname = xstrdup(netname);
242 /* netname "." is special: a "top-level name" */
244 if(netname && (!*netname || !strcmp(netname, "."))) {
249 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
250 fprintf(stderr, "Invalid character in netname!\n");
258 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
260 char directory[PATH_MAX] = ".";
266 /* Check stdin and stdout */
268 /* Ask for a file and/or directory name. */
269 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
271 if(fgets(buf, sizeof(buf), stdin) == NULL) {
272 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
276 size_t len = strlen(buf);
289 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
292 if(filename[0] != '/') {
295 /* The directory is a relative path or a filename. */
296 if(!getcwd(directory, sizeof(directory))) {
297 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
301 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
302 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
314 disable_old_keys(filename, what);
316 /* Open it first to keep the inode busy */
318 r = fopenmask(filename, mode, perms);
321 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
329 Generate a public/private Ed25519 key pair, and ask for a file to store
332 static bool ed25519_keygen(bool ask) {
335 char fname[PATH_MAX];
337 fprintf(stderr, "Generating Ed25519 key pair:\n");
339 if(!(key = ecdsa_generate())) {
340 fprintf(stderr, "Error during key generation!\n");
343 fprintf(stderr, "Done.\n");
346 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
347 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
353 if(!ecdsa_write_pem_private_key(key, f)) {
354 fprintf(stderr, "Error writing private key!\n");
361 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
363 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
366 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
372 char *pubkey = ecdsa_get_base64_public_key(key);
373 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
391 #ifndef DISABLE_LEGACY
393 Generate a public/private RSA key pair, and ask for a file to store
396 static bool rsa_keygen(int bits, bool ask) {
399 char fname[PATH_MAX];
401 // Make sure the key size is a multiple of 8 bits.
404 // Make sure that a valid key size is used.
405 if(bits < 1024 || bits > 8192) {
406 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
408 } else if(bits < 2048) {
409 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
412 fprintf(stderr, "Generating %d bits keys:\n", bits);
414 if(!(key = rsa_generate(bits, 0x10001))) {
415 fprintf(stderr, "Error during key generation!\n");
418 fprintf(stderr, "Done.\n");
421 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
422 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
428 if(!rsa_write_pem_private_key(key, f)) {
429 fprintf(stderr, "Error writing private key!\n");
436 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
438 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
441 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
447 if(!rsa_write_pem_public_key(key, f)) {
448 fprintf(stderr, "Error writing public key!\n");
471 bool recvline(int fd, char *line, size_t len) {
472 char *newline = NULL;
478 while(!(newline = memchr(buffer, '\n', blen))) {
479 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
481 if(nrecv == -1 && sockerrno == EINTR) {
483 } else if(nrecv <= 0) {
490 if((size_t)(newline - buffer) >= len) {
494 len = newline - buffer;
496 memcpy(line, buffer, len);
498 memmove(buffer, newline + 1, blen - len - 1);
504 static bool recvdata(int fd, char *data, size_t len) {
506 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
508 if(nrecv == -1 && sockerrno == EINTR) {
510 } else if(nrecv <= 0) {
517 memcpy(data, buffer, len);
518 memmove(buffer, buffer + len, blen - len);
524 bool sendline(int fd, const char *format, ...) {
525 static char buffer[4096];
530 va_start(ap, format);
531 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
532 buffer[sizeof(buffer) - 1] = 0;
535 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
543 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
545 if(nsend == -1 && sockerrno == EINTR) {
547 } else if(nsend <= 0) {
558 static void pcap(int fd, FILE *out, uint32_t snaplen) {
559 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
567 uint32_t tz_accuracy;
574 snaplen ? snaplen : sizeof(data),
587 fwrite(&header, sizeof(header), 1, out);
592 while(recvline(fd, line, sizeof(line))) {
595 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
596 gettimeofday(&tv, NULL);
598 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
602 if(!recvdata(fd, data, len)) {
606 packet.tv_sec = tv.tv_sec;
607 packet.tv_usec = tv.tv_usec;
609 packet.origlen = len;
610 fwrite(&packet, sizeof(packet), 1, out);
611 fwrite(data, len, 1, out);
616 static void log_control(int fd, FILE *out, int level, bool use_color) {
617 sendline(fd, "%d %d %d %d", CONTROL, REQ_LOG, level, use_color);
622 while(recvline(fd, line, sizeof(line))) {
624 int n = sscanf(line, "%d %d %d", &code, &req, &len);
626 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
630 if(!recvdata(fd, data, len)) {
634 fwrite(data, len, 1, out);
640 static bool stop_tincd(void) {
641 if(!connect_tincd(true)) {
645 sendline(fd, "%d %d", CONTROL, REQ_STOP);
647 while(recvline(fd, line, sizeof(line))) {
648 // wait for tincd to close the connection...
659 static bool remove_service(void) {
660 SC_HANDLE manager = NULL;
661 SC_HANDLE service = NULL;
662 SERVICE_STATUS status = {0};
663 bool success = false;
665 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
668 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
672 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
675 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
676 success = stop_tincd();
678 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
684 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
685 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
687 fprintf(stderr, "%s service stopped\n", identname);
690 if(!DeleteService(service)) {
691 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
700 CloseServiceHandle(service);
704 CloseServiceHandle(manager);
708 fprintf(stderr, "%s service removed\n", identname);
715 bool connect_tincd(bool verbose) {
720 struct timeval tv = {0, 0};
722 if(select(fd + 1, &r, NULL, NULL, &tv)) {
723 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
731 pidfile_t *pidfile = read_pidfile();
735 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
742 strcpy(controlcookie, pidfile->cookie);
747 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
748 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
749 /* clean up the stale socket and pid file */
751 unlink(unixsocketname);
755 struct sockaddr_un sa = {
756 .sun_family = AF_UNIX,
759 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
760 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
764 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
766 fd = socket(AF_UNIX, SOCK_STREAM, 0);
770 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
776 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
778 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
787 struct addrinfo hints = {
788 .ai_family = AF_UNSPEC,
789 .ai_socktype = SOCK_STREAM,
790 .ai_protocol = IPPROTO_TCP,
794 struct addrinfo *res = NULL;
796 if(getaddrinfo(pidfile->host, pidfile->port, &hints, &res) || !res) {
798 fprintf(stderr, "Cannot resolve %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
805 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
809 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
816 unsigned long arg = 0;
818 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
820 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
824 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
826 fprintf(stderr, "Cannot connect to %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
840 static const int one = 1;
841 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
844 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
849 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
851 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
859 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
861 fprintf(stderr, "Could not fully establish control socket connection\n");
873 static int cmd_start(int argc, char *argv[]) {
874 if(connect_tincd(false)) {
876 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
878 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
885 char *slash = strrchr(program_name, '/');
889 if((c = strrchr(program_name, '\\')) > slash) {
896 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
898 c = xstrdup("tincd");
902 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
907 Windows has no real concept of an "argv array". A command line is just one string.
908 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
909 it uses quotes to handle spaces in arguments.
910 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
911 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
912 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
914 xasprintf(&arg0, "\"%s\"", arg0);
916 nargv[nargc++] = arg0;
918 for(int i = 1; i < optind; i++) {
919 nargv[nargc++] = orig_argv[i];
922 for(int i = 1; i < argc; i++) {
923 nargv[nargc++] = argv[i];
927 int status = spawnvp(_P_WAIT, c, nargv);
933 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
939 int pfd[2] = {-1, -1};
941 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
942 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
951 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
960 snprintf(buf, sizeof(buf), "%d %d", pfd[1], use_ansi_escapes(stderr));
961 setenv("TINC_UMBILICAL", buf, true);
962 exit(execvp(c, nargv));
970 signal(SIGINT, SIG_IGN);
973 // Pass all log messages from the umbilical to stderr.
974 // A nul-byte right before closure means tincd started successfully.
979 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
980 failure = buf[len - 1];
986 if(write(2, buf, len) != len) {
987 // Nothing we can do about it.
997 // Make sure the child process is really gone.
999 pid_t result = waitpid(pid, &status, 0);
1002 signal(SIGINT, SIG_DFL);
1005 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1008 fprintf(stderr, "Error starting %s\n", c);
1013 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1017 static int cmd_stop(int argc, char *argv[]) {
1021 fprintf(stderr, "Too many arguments!\n");
1026 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1031 if(kill(pid, SIGTERM)) {
1032 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1036 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1037 waitpid(pid, NULL, 0);
1048 static int cmd_restart(int argc, char *argv[]) {
1050 return cmd_start(argc, argv);
1053 static int cmd_reload(int argc, char *argv[]) {
1057 fprintf(stderr, "Too many arguments!\n");
1061 if(!connect_tincd(true)) {
1065 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1067 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1068 fprintf(stderr, "Could not reload configuration.\n");
1076 static int dump_invitations(void) {
1077 char dname[PATH_MAX];
1078 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1079 DIR *dir = opendir(dname);
1082 if(errno == ENOENT) {
1083 fprintf(stderr, "No outstanding invitations.\n");
1087 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1095 while((ent = readdir(dir))) {
1096 char buf[MAX_STRING_SIZE];
1098 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1102 char fname[PATH_MAX];
1104 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1105 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1109 FILE *f = fopen(fname, "r");
1112 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1118 if(!fgets(buf, sizeof(buf), f)) {
1119 fprintf(stderr, "Invalid invitation file %s\n", fname);
1126 char *eol = buf + strlen(buf);
1128 while(strchr("\t \r\n", *--eol)) {
1132 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1133 fprintf(stderr, "Invalid invitation file %s\n", fname);
1138 printf("%s %s\n", ent->d_name, buf + 7);
1144 fprintf(stderr, "No outstanding invitations.\n");
1150 static int cmd_dump(int argc, char *argv[]) {
1151 bool only_reachable = false;
1153 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1154 if(strcasecmp(argv[2], "nodes")) {
1155 fprintf(stderr, "`reachable' only supported for nodes.\n");
1160 only_reachable = true;
1166 fprintf(stderr, "Invalid number of arguments.\n");
1171 if(!strcasecmp(argv[1], "invitations")) {
1172 return dump_invitations();
1175 if(!connect_tincd(true)) {
1181 if(!strcasecmp(argv[1], "nodes")) {
1182 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1183 } else if(!strcasecmp(argv[1], "edges")) {
1184 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1185 } else if(!strcasecmp(argv[1], "subnets")) {
1186 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1187 } else if(!strcasecmp(argv[1], "connections")) {
1188 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1189 } else if(!strcasecmp(argv[1], "graph")) {
1190 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1191 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1193 } else if(!strcasecmp(argv[1], "digraph")) {
1194 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1195 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1198 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1204 printf("graph {\n");
1205 } else if(do_graph == 2) {
1206 printf("digraph {\n");
1209 while(recvline(fd, line, sizeof(line))) {
1210 char node1[4096], node2[4096];
1211 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1214 if(do_graph && req == REQ_DUMP_NODES) {
1236 char local_host[4096];
1237 char local_port[4096];
1240 int cipher, digest, maclength, compression, distance, socket, weight;
1241 short int pmtu, minmtu, maxmtu;
1242 unsigned int options;
1243 node_status_t status;
1244 long int last_state_change;
1246 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1249 case REQ_DUMP_NODES: {
1250 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);
1253 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1258 const char *color = "black";
1260 if(!strcmp(host, "MYSELF")) {
1262 } else if(!status.reachable) {
1264 } else if(strcmp(via, node)) {
1266 } else if(!status.validkey) {
1268 } else if(minmtu > 0) {
1272 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1274 if(only_reachable && !status.reachable) {
1278 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,
1279 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);
1281 if(udp_ping_rtt != -1) {
1282 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1290 case REQ_DUMP_EDGES: {
1291 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);
1294 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1299 float w = 1.0f + 65536.0f / (float)weight;
1301 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1302 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1303 } else if(do_graph == 2) {
1304 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1307 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);
1312 case REQ_DUMP_SUBNETS: {
1313 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1316 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1320 printf("%s owner %s\n", strip_weight(subnet), node);
1324 case REQ_DUMP_CONNECTIONS: {
1325 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1328 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1332 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1337 fprintf(stderr, "Unable to parse dump from tincd.\n");
1342 fprintf(stderr, "Error receiving dump.\n");
1346 static int cmd_purge(int argc, char *argv[]) {
1350 fprintf(stderr, "Too many arguments!\n");
1354 if(!connect_tincd(true)) {
1358 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1360 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1361 fprintf(stderr, "Could not purge old information.\n");
1368 static int cmd_debug(int argc, char *argv[]) {
1370 fprintf(stderr, "Invalid number of arguments.\n");
1374 if(!connect_tincd(true)) {
1378 int debuglevel = atoi(argv[1]);
1381 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1383 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1384 fprintf(stderr, "Could not set debug level.\n");
1388 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1392 static int cmd_retry(int argc, char *argv[]) {
1396 fprintf(stderr, "Too many arguments!\n");
1400 if(!connect_tincd(true)) {
1404 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1406 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1407 fprintf(stderr, "Could not retry outgoing connections.\n");
1414 static int cmd_connect(int argc, char *argv[]) {
1416 fprintf(stderr, "Invalid number of arguments.\n");
1420 if(!check_id(argv[1])) {
1421 fprintf(stderr, "Invalid name for node.\n");
1425 if(!connect_tincd(true)) {
1429 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1431 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1432 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1439 static int cmd_disconnect(int argc, char *argv[]) {
1441 fprintf(stderr, "Invalid number of arguments.\n");
1445 if(!check_id(argv[1])) {
1446 fprintf(stderr, "Invalid name for node.\n");
1450 if(!connect_tincd(true)) {
1454 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1456 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1457 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1464 static int cmd_top(int argc, char *argv[]) {
1468 fprintf(stderr, "Too many arguments!\n");
1474 if(!connect_tincd(true)) {
1481 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1486 static int cmd_pcap(int argc, char *argv[]) {
1488 fprintf(stderr, "Too many arguments!\n");
1492 if(!connect_tincd(true)) {
1496 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1501 static void sigint_handler(int sig) {
1504 fprintf(stderr, "\n");
1505 shutdown(fd, SHUT_RDWR);
1509 static int cmd_log(int argc, char *argv[]) {
1511 fprintf(stderr, "Too many arguments!\n");
1515 if(!connect_tincd(true)) {
1520 signal(SIGINT, sigint_handler);
1523 bool use_color = use_ansi_escapes(stdout);
1524 log_control(fd, stdout, argc > 1 ? atoi(argv[1]) : DEBUG_UNSET, use_color);
1527 signal(SIGINT, SIG_DFL);
1535 static int cmd_pid(int argc, char *argv[]) {
1539 fprintf(stderr, "Too many arguments!\n");
1543 if(!connect_tincd(true) || !pid) {
1547 printf("%d\n", pid);
1551 size_t rstrip(char *value) {
1552 size_t len = strlen(value);
1554 while(len && strchr("\t\r\n ", value[len - 1])) {
1561 char *get_my_name(bool verbose) {
1562 FILE *f = fopen(tinc_conf, "r");
1566 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1575 while(fgets(buf, sizeof(buf), f)) {
1576 size_t len = strcspn(buf, "\t =");
1578 value += strspn(value, "\t ");
1582 value += strspn(value, "\t ");
1585 if(!rstrip(value)) {
1591 if(strcasecmp(buf, "Name")) {
1597 return replace_name(value);
1604 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1610 ecdsa_t *get_pubkey(FILE *f) {
1614 while(fgets(buf, sizeof(buf), f)) {
1615 size_t len = strcspn(buf, "\t =");
1617 value += strspn(value, "\t ");
1621 value += strspn(value, "\t ");
1624 if(!rstrip(value)) {
1630 if(strcasecmp(buf, "Ed25519PublicKey")) {
1635 return ecdsa_set_base64_public_key(value);
1642 const var_t variables[] = {
1643 /* Server configuration */
1644 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1645 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1646 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1647 {"BindToInterface", VAR_SERVER},
1648 {"Broadcast", VAR_SERVER | VAR_SAFE},
1649 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1650 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1651 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1652 {"Device", VAR_SERVER},
1653 {"DeviceStandby", VAR_SERVER},
1654 {"DeviceType", VAR_SERVER},
1655 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1656 {"Ed25519PrivateKeyFile", VAR_SERVER},
1657 {"ExperimentalProtocol", VAR_SERVER},
1658 {"Forwarding", VAR_SERVER},
1659 {"FWMark", VAR_SERVER},
1660 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1661 {"Hostnames", VAR_SERVER},
1662 {"IffOneQueue", VAR_SERVER},
1663 {"Interface", VAR_SERVER},
1664 {"InvitationExpire", VAR_SERVER},
1665 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1666 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1667 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1668 {"LogLevel", VAR_SERVER},
1669 {"MACExpire", VAR_SERVER | VAR_SAFE},
1670 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1671 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1672 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1673 {"Mode", VAR_SERVER | VAR_SAFE},
1674 {"Name", VAR_SERVER},
1675 {"PingInterval", VAR_SERVER | VAR_SAFE},
1676 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1677 {"PriorityInheritance", VAR_SERVER},
1678 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1679 {"PrivateKeyFile", VAR_SERVER},
1680 {"ProcessPriority", VAR_SERVER},
1681 {"Proxy", VAR_SERVER},
1682 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1683 {"ScriptsExtension", VAR_SERVER},
1684 {"ScriptsInterpreter", VAR_SERVER},
1685 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1686 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1687 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1688 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1689 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1690 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1691 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1692 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1693 {"UDPRcvBuf", VAR_SERVER},
1694 {"UDPSndBuf", VAR_SERVER},
1695 {"UPnP", VAR_SERVER},
1696 {"UPnPDiscoverWait", VAR_SERVER},
1697 {"UPnPRefreshPeriod", VAR_SERVER},
1698 {"VDEGroup", VAR_SERVER},
1699 {"VDEPort", VAR_SERVER},
1700 /* Host configuration */
1701 {"Address", VAR_HOST | VAR_MULTIPLE},
1702 {"Cipher", VAR_SERVER | VAR_HOST},
1703 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1704 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1705 {"Digest", VAR_SERVER | VAR_HOST},
1706 {"Ed25519PublicKey", VAR_HOST},
1707 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1708 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1709 {"MACLength", VAR_SERVER | VAR_HOST},
1710 {"PMTU", VAR_SERVER | VAR_HOST},
1711 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1713 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1714 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1715 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1716 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1717 {"Weight", VAR_HOST | VAR_SAFE},
1721 // Request actual port from tincd
1722 static bool read_actual_port(void) {
1723 pidfile_t *pidfile = read_pidfile();
1726 printf("%s\n", pidfile->port);
1730 fprintf(stderr, "Could not get port from the pidfile.\n");
1735 static int cmd_config(int argc, char *argv[]) {
1737 fprintf(stderr, "Invalid number of arguments.\n");
1741 if(strcasecmp(argv[0], "config")) {
1745 typedef enum { GET, DEL, SET, ADD } action_t;
1746 action_t action = GET;
1748 if(!strcasecmp(argv[1], "get")) {
1750 } else if(!strcasecmp(argv[1], "add")) {
1751 argv++, argc--, action = ADD;
1752 } else if(!strcasecmp(argv[1], "del")) {
1753 argv++, argc--, action = DEL;
1754 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1755 argv++, argc--, action = SET;
1759 fprintf(stderr, "Invalid number of arguments.\n");
1763 // Concatenate the rest of the command line
1764 strncpy(line, argv[1], sizeof(line) - 1);
1766 for(int i = 2; i < argc; i++) {
1767 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1768 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1771 // Liberal parsing into node name, variable name and value.
1777 len = strcspn(line, "\t =");
1779 value += strspn(value, "\t ");
1783 value += strspn(value, "\t ");
1787 variable = strchr(line, '.');
1797 fprintf(stderr, "No variable given.\n");
1801 if((action == SET || action == ADD) && !*value) {
1802 fprintf(stderr, "No value for variable given.\n");
1806 if(action == GET && *value) {
1810 // If port is requested, try reading it from the pidfile and fall back to configs if that fails
1811 if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
1815 /* Some simple checks. */
1817 bool warnonremove = false;
1819 for(int i = 0; variables[i].name; i++) {
1820 if(strcasecmp(variables[i].name, variable)) {
1825 variable = (char *)variables[i].name;
1827 if(!strcasecmp(variable, "Subnet") && *value) {
1830 if(!str2net(&s, value)) {
1831 fprintf(stderr, "Malformed subnet definition %s\n", value);
1835 if(!subnetcheck(s)) {
1836 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1841 /* Discourage use of obsolete variables. */
1843 if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
1845 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1847 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1852 /* Don't put server variables in host config files */
1854 if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
1856 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1858 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1863 /* Should this go into our own host config file? */
1865 if(!node && !(variables[i].type & VAR_SERVER)) {
1866 node = get_my_name(true);
1873 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1874 Turn on warnings when it seems variables might be removed unintentionally. */
1876 if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
1877 warnonremove = true;
1879 } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
1880 warnonremove = true;
1886 if(node && !check_id(node)) {
1887 fprintf(stderr, "Invalid name for node.\n");
1897 if(force || action == GET || action == DEL) {
1898 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1900 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1902 if(node && node != line) {
1910 // Open the right configuration file.
1911 char filename[PATH_MAX];
1914 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node) >= sizeof(filename)) {
1915 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1925 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1928 FILE *f = fopen(filename, "r");
1931 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1935 char tmpfile[PATH_MAX];
1939 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1940 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1944 tf = fopen(tmpfile, "w");
1947 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1953 // Copy the file, making modifications on the fly, unless we are just getting a value.
1957 bool removed = false;
1960 while(fgets(buf1, sizeof(buf1), f)) {
1961 buf1[sizeof(buf1) - 1] = 0;
1962 strncpy(buf2, buf1, sizeof(buf2));
1964 // Parse line in a simple way
1967 size_t len = strcspn(buf2, "\t =");
1968 bvalue = buf2 + len;
1969 bvalue += strspn(bvalue, "\t ");
1971 if(*bvalue == '=') {
1973 bvalue += strspn(bvalue, "\t ");
1980 if(!strcasecmp(buf2, variable)) {
1983 printf("%s\n", bvalue);
1984 } else if(action == DEL) {
1985 if(!*value || !strcasecmp(bvalue, value)) {
1989 } else if(action == SET) {
1990 // Warn if "set" was used for variables that can occur multiple times
1991 if(warnonremove && strcasecmp(bvalue, value)) {
1992 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1995 // Already set? Delete the rest...
2000 // Otherwise, replace.
2001 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2002 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2008 } else if(action == ADD) {
2009 // Check if we've already seen this variable with the same value
2010 if(!strcasecmp(bvalue, value)) {
2017 // Copy original line...
2018 if(fputs(buf1, tf) < 0) {
2019 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2023 // Add newline if it is missing...
2024 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2025 if(fputc('\n', tf) < 0) {
2026 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2033 // Make sure we read everything...
2034 if(ferror(f) || !feof(f)) {
2035 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2040 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2044 // Add new variable if necessary.
2045 if((action == ADD && !found) || (action == SET && !set)) {
2046 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2047 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2056 fprintf(stderr, "No matching configuration variables found.\n");
2061 // Make sure we wrote everything...
2063 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2067 // Could we find what we had to remove?
2068 if((action == GET || action == DEL) && !removed) {
2070 fprintf(stderr, "No configuration variables deleted.\n");
2074 // Replace the configuration file with the new one
2077 if(remove(filename)) {
2078 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2084 if(rename(tmpfile, filename)) {
2085 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2089 // Silently try notifying a running tincd of changes.
2090 if(connect_tincd(false)) {
2091 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2097 static bool try_bind(int port) {
2098 struct addrinfo *ai = NULL, *aip;
2099 struct addrinfo hint = {
2100 .ai_flags = AI_PASSIVE,
2101 .ai_family = AF_UNSPEC,
2102 .ai_socktype = SOCK_STREAM,
2103 .ai_protocol = IPPROTO_TCP,
2106 bool success = true;
2108 snprintf(portstr, sizeof(portstr), "%d", port);
2110 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2114 for(aip = ai; aip; aip = aip->ai_next) {
2115 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2122 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2135 int check_port(const char *name) {
2140 fprintf(stderr, "Warning: could not bind to port 655. ");
2142 for(int i = 0; i < 100; i++) {
2143 uint16_t port = 0x1000 + prng(0x8000);
2145 if(try_bind(port)) {
2146 char filename[PATH_MAX];
2147 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2148 FILE *f = fopen(filename, "a");
2151 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2152 fprintf(stderr, "Please change tinc's Port manually.\n");
2156 fprintf(f, "Port = %d\n", port);
2158 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2163 fprintf(stderr, "Please change tinc's Port manually.\n");
2167 static int cmd_init(int argc, char *argv[]) {
2168 if(!access(tinc_conf, F_OK)) {
2169 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2174 fprintf(stderr, "Too many arguments!\n");
2176 } else if(argc < 2) {
2179 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2181 if(!fgets(buf, sizeof(buf), stdin)) {
2182 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2186 size_t len = rstrip(buf);
2189 fprintf(stderr, "No name given!\n");
2195 fprintf(stderr, "No Name given!\n");
2199 name = strdup(argv[1]);
2202 fprintf(stderr, "No Name given!\n");
2207 if(!check_id(name)) {
2208 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2212 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2213 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2217 if(mkdir(confbase, 0777) && errno != EEXIST) {
2218 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2222 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2223 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2227 FILE *f = fopen(tinc_conf, "w");
2230 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2234 fprintf(f, "Name = %s\n", name);
2237 #ifndef DISABLE_LEGACY
2239 if(!rsa_keygen(2048, false)) {
2245 if(!ed25519_keygen(false)) {
2251 #ifndef HAVE_WINDOWS
2252 char filename[PATH_MAX];
2253 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2255 if(access(filename, F_OK)) {
2256 FILE *f = fopenmask(filename, "w", 0777);
2259 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2263 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");
2273 static int cmd_generate_keys(int argc, char *argv[]) {
2274 #ifdef DISABLE_LEGACY
2282 fprintf(stderr, "Too many arguments!\n");
2287 name = get_my_name(false);
2290 #ifndef DISABLE_LEGACY
2292 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2298 if(!ed25519_keygen(true)) {
2305 #ifndef DISABLE_LEGACY
2306 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2308 fprintf(stderr, "Too many arguments!\n");
2313 name = get_my_name(false);
2316 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2320 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2324 fprintf(stderr, "Too many arguments!\n");
2329 name = get_my_name(false);
2332 return !ed25519_keygen(true);
2335 static int cmd_help(int argc, char *argv[]) {
2343 static int cmd_version(int argc, char *argv[]) {
2347 fprintf(stderr, "Too many arguments!\n");
2355 static int cmd_info(int argc, char *argv[]) {
2357 fprintf(stderr, "Invalid number of arguments.\n");
2361 if(!connect_tincd(true)) {
2365 return info(fd, argv[1]);
2368 static const char *conffiles[] = {
2379 static int cmd_edit(int argc, char *argv[]) {
2381 fprintf(stderr, "Invalid number of arguments.\n");
2385 char filename[PATH_MAX] = "";
2387 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2388 for(int i = 0; conffiles[i]; i++) {
2389 if(!strcmp(argv[1], conffiles[i])) {
2390 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2399 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2400 char *dash = strchr(argv[1], '-');
2405 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2406 fprintf(stderr, "Invalid configuration filename.\n");
2413 #ifndef HAVE_WINDOWS
2414 const char *editor = getenv("VISUAL");
2417 editor = getenv("EDITOR");
2424 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2426 xasprintf(&command, "edit \"%s\"", filename);
2428 int result = system(command);
2435 // Silently try notifying a running tincd of changes.
2436 if(connect_tincd(false)) {
2437 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2443 static int export(const char *name, FILE *out) {
2444 char filename[PATH_MAX];
2445 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2446 FILE *in = fopen(filename, "r");
2449 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2453 fprintf(out, "Name = %s\n", name);
2456 while(fgets(buf, sizeof(buf), in)) {
2457 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2463 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2472 static int cmd_export(int argc, char *argv[]) {
2476 fprintf(stderr, "Too many arguments!\n");
2480 char *name = get_my_name(true);
2486 int result = export(name, stdout);
2496 static int cmd_export_all(int argc, char *argv[]) {
2500 fprintf(stderr, "Too many arguments!\n");
2504 DIR *dir = opendir(hosts_dir);
2507 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2515 while((ent = readdir(dir))) {
2516 if(!check_id(ent->d_name)) {
2523 printf("#---------------------------------------------------------------#\n");
2526 result |= export(ent->d_name, stdout);
2538 static int cmd_import(int argc, char *argv[]) {
2542 fprintf(stderr, "Too many arguments!\n");
2551 char filename[PATH_MAX] = "";
2553 bool firstline = true;
2555 while(fgets(buf, sizeof(buf), in)) {
2556 if(sscanf(buf, "Name = %4095s", name) == 1) {
2559 if(!check_id(name)) {
2560 fprintf(stderr, "Invalid Name in input!\n");
2568 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2569 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2573 if(!force && !access(filename, F_OK)) {
2574 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2579 out = fopen(filename, "w");
2582 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2588 } else if(firstline) {
2589 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2594 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2599 if(fputs(buf, out) < 0) {
2600 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2611 fprintf(stderr, "Imported %d host configuration files.\n", count);
2614 fprintf(stderr, "No host configuration files imported.\n");
2619 static int cmd_exchange(int argc, char *argv[]) {
2620 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2623 static int cmd_exchange_all(int argc, char *argv[]) {
2624 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2627 static int switch_network(char *name) {
2628 if(strcmp(name, ".")) {
2629 if(!check_netname(name, false)) {
2630 fprintf(stderr, "Invalid character in netname!\n");
2634 if(!check_netname(name, true)) {
2635 fprintf(stderr, "Warning: unsafe character in netname!\n");
2645 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2652 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2653 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2654 xasprintf(&prompt, "%s> ", identname);
2659 static int cmd_network(int argc, char *argv[]) {
2661 fprintf(stderr, "Too many arguments!\n");
2666 return switch_network(argv[1]);
2669 DIR *dir = opendir(confdir);
2672 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2678 while((ent = readdir(dir))) {
2679 if(*ent->d_name == '.') {
2683 if(!strcmp(ent->d_name, "tinc.conf")) {
2688 char fname[PATH_MAX];
2689 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2691 if(!access(fname, R_OK)) {
2692 printf("%s\n", ent->d_name);
2701 static int cmd_fsck(int argc, char *argv[]) {
2705 fprintf(stderr, "Too many arguments!\n");
2709 return fsck(orig_argv[0]);
2712 static void *readfile(FILE *in, size_t *len) {
2714 size_t bufsize = 4096;
2715 char *buf = xmalloc(bufsize);
2718 size_t read = fread(buf + count, 1, bufsize - count, in);
2726 if(count >= bufsize) {
2728 buf = xrealloc(buf, bufsize);
2739 static int cmd_sign(int argc, char *argv[]) {
2741 fprintf(stderr, "Too many arguments!\n");
2746 name = get_my_name(true);
2753 char fname[PATH_MAX];
2754 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2755 FILE *fp = fopen(fname, "r");
2758 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2762 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2765 fprintf(stderr, "Could not read private key from %s\n", fname);
2775 in = fopen(argv[1], "rb");
2778 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2787 char *data = readfile(in, &len);
2794 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2799 // Ensure we sign our name and current time as well
2800 long t = time(NULL);
2802 xasprintf(&trailer, " %s %ld", name, t);
2803 size_t trailer_len = strlen(trailer);
2805 data = xrealloc(data, len + trailer_len);
2806 memcpy(data + len, trailer, trailer_len);
2811 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2812 fprintf(stderr, "Error generating signature\n");
2818 b64encode_tinc(sig, sig, 64);
2821 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2822 fwrite(data, len, 1, stdout);
2828 static int cmd_verify(int argc, char *argv[]) {
2830 fprintf(stderr, "Not enough arguments!\n");
2835 fprintf(stderr, "Too many arguments!\n");
2839 char *node = argv[1];
2841 if(!strcmp(node, ".")) {
2843 name = get_my_name(true);
2851 } else if(!strcmp(node, "*")) {
2854 if(!check_id(node)) {
2855 fprintf(stderr, "Invalid node name\n");
2863 in = fopen(argv[2], "rb");
2866 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2874 char *data = readfile(in, &len);
2881 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2885 char *newline = memchr(data, '\n', len);
2887 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2888 fprintf(stderr, "Invalid input\n");
2894 size_t skip = newline - data;
2896 char signer[MAX_STRING_SIZE] = "";
2897 char sig[MAX_STRING_SIZE] = "";
2900 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2901 fprintf(stderr, "Invalid input\n");
2906 if(node && strcmp(node, signer)) {
2907 fprintf(stderr, "Signature is not made by %s\n", node);
2917 xasprintf(&trailer, " %s %ld", signer, t);
2918 size_t trailer_len = strlen(trailer);
2920 data = xrealloc(data, len + trailer_len);
2921 memcpy(data + len, trailer, trailer_len);
2924 newline = data + skip;
2926 char fname[PATH_MAX];
2927 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2928 FILE *fp = fopen(fname, "r");
2931 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2936 ecdsa_t *key = get_pubkey(fp);
2940 key = ecdsa_read_pem_public_key(fp);
2944 fprintf(stderr, "Could not read public key from %s\n", fname);
2952 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2953 fprintf(stderr, "Invalid signature\n");
2961 fwrite(newline, len - (newline - data), 1, stdout);
2967 static const struct {
2968 const char *command;
2969 int (*function)(int argc, char *argv[]);
2972 {"start", cmd_start, false},
2973 {"stop", cmd_stop, false},
2974 {"restart", cmd_restart, false},
2975 {"reload", cmd_reload, false},
2976 {"dump", cmd_dump, false},
2977 {"list", cmd_dump, false},
2978 {"purge", cmd_purge, false},
2979 {"debug", cmd_debug, false},
2980 {"retry", cmd_retry, false},
2981 {"connect", cmd_connect, false},
2982 {"disconnect", cmd_disconnect, false},
2983 {"top", cmd_top, false},
2984 {"pcap", cmd_pcap, false},
2985 {"log", cmd_log, false},
2986 {"pid", cmd_pid, false},
2987 {"config", cmd_config, true},
2988 {"add", cmd_config, false},
2989 {"del", cmd_config, false},
2990 {"get", cmd_config, false},
2991 {"set", cmd_config, false},
2992 {"init", cmd_init, false},
2993 {"generate-keys", cmd_generate_keys, false},
2994 #ifndef DISABLE_LEGACY
2995 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
2997 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
2998 {"help", cmd_help, false},
2999 {"version", cmd_version, false},
3000 {"info", cmd_info, false},
3001 {"edit", cmd_edit, false},
3002 {"export", cmd_export, false},
3003 {"export-all", cmd_export_all, false},
3004 {"import", cmd_import, false},
3005 {"exchange", cmd_exchange, false},
3006 {"exchange-all", cmd_exchange_all, false},
3007 {"invite", cmd_invite, false},
3008 {"join", cmd_join, false},
3009 {"network", cmd_network, false},
3010 {"fsck", cmd_fsck, false},
3011 {"sign", cmd_sign, false},
3012 {"verify", cmd_verify, false},
3013 {NULL, NULL, false},
3016 #ifdef HAVE_READLINE
3017 static char *complete_command(const char *text, int state) {
3026 while(commands[i].command) {
3027 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3028 return xstrdup(commands[i].command);
3037 static char *complete_dump(const char *text, int state) {
3038 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3048 if(!strncasecmp(matches[i], text, strlen(text))) {
3049 return xstrdup(matches[i]);
3058 static char *complete_config(const char *text, int state) {
3067 while(variables[i].name) {
3068 char *dot = strchr(text, '.');
3071 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3073 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3077 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3078 return xstrdup(variables[i].name);
3088 static char *complete_info(const char *text, int state) {
3094 if(!connect_tincd(false)) {
3098 // Check the list of nodes
3099 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3100 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3103 while(recvline(fd, line, sizeof(line))) {
3105 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3118 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3122 if(!strncmp(item, text, strlen(text))) {
3123 return xstrdup(strip_weight(item));
3130 static char *complete_nothing(const char *text, int state) {
3136 static char **completion(const char *text, int start, int end) {
3138 char **matches = NULL;
3141 matches = rl_completion_matches(text, complete_command);
3142 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3143 matches = rl_completion_matches(text, complete_dump);
3144 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3145 matches = rl_completion_matches(text, complete_config);
3146 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3147 matches = rl_completion_matches(text, complete_config);
3148 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3149 matches = rl_completion_matches(text, complete_config);
3150 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3151 matches = rl_completion_matches(text, complete_config);
3152 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3153 matches = rl_completion_matches(text, complete_info);
3160 static int cmd_shell(int argc, char *argv[]) {
3161 xasprintf(&prompt, "%s> ", identname);
3165 int maxargs = argc + 16;
3166 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3168 for(int i = 0; i < argc; i++) {
3172 #ifdef HAVE_READLINE
3173 rl_readline_name = "tinc";
3174 rl_basic_word_break_characters = "\t\n ";
3175 rl_completion_entry_function = complete_nothing;
3176 rl_attempted_completion_function = completion;
3177 rl_filename_completion_desired = 0;
3182 #ifdef HAVE_READLINE
3187 line = readline(prompt);
3188 copy = line ? xstrdup(line) : NULL;
3190 line = fgets(buf, sizeof(buf), stdin);
3196 fputs(prompt, stdout);
3199 line = fgets(buf, sizeof(buf), stdin);
3206 /* Ignore comments */
3215 char *p = line + strspn(line, " \t\n");
3216 char *next = strtok(p, " \t\n");
3219 if(nargc >= maxargs) {
3221 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3226 next = strtok(NULL, " \t\n");
3233 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3234 #ifdef HAVE_READLINE
3243 for(int i = 0; commands[i].command; i++) {
3244 if(!strcasecmp(nargv[argc], commands[i].command)) {
3245 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3251 #ifdef HAVE_READLINE
3260 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3265 #ifdef HAVE_READLINE
3277 static void cleanup(void) {
3283 static int run_command(int argc, char *argv[]) {
3284 if(optind >= argc) {
3285 return cmd_shell(argc, argv);
3288 for(int i = 0; commands[i].command; i++) {
3289 if(!strcasecmp(argv[optind], commands[i].command)) {
3290 return commands[i].function(argc - optind, argv + optind);
3294 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3299 int main(int argc, char *argv[]) {
3300 program_name = argv[0];
3302 tty = isatty(0) && isatty(1);
3304 if(!parse_options(argc, argv)) {
3309 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3310 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3324 static struct WSAData wsa_state;
3326 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3327 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3333 gettimeofday(&now, NULL);
3338 int result = run_command(argc, argv);