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 typedef enum option_t {
89 OPT_CONFIG_FILE = 'c',
99 static struct option const long_options[] = {
100 {"batch", no_argument, NULL, OPT_BATCH},
101 {"config", required_argument, NULL, OPT_CONFIG_FILE},
102 {"net", required_argument, NULL, OPT_NETNAME},
103 {"help", no_argument, NULL, OPT_HELP},
104 {"version", no_argument, NULL, OPT_VERSION},
105 {"pidfile", required_argument, NULL, OPT_PIDFILE},
106 {"force", no_argument, NULL, OPT_FORCE},
110 static void version(void) {
112 "%s version %s (built %s %s, protocol %d.%d)\n"
120 #ifndef DISABLE_LEGACY
124 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
125 "See the AUTHORS file for a complete list.\n"
127 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
128 "and you are welcome to redistribute it under certain conditions;\n"
129 "see the file COPYING for details.\n",
130 PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
133 static void usage(bool status) {
135 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
138 "Usage: %s [options] command\n"
140 "Valid options are:\n"
141 " -b, --batch Don't ask for anything (non-interactive mode).\n"
142 " -c, --config=DIR Read configuration options from DIR.\n"
143 " -n, --net=NETNAME Connect to net NETNAME.\n"
144 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
145 " --force Force some commands to work despite warnings.\n"
146 " --help Display this help and exit.\n"
147 " --version Output version information and exit.\n"
149 "Valid commands are:\n"
150 " init [name] Create initial configuration files.\n"
151 " get VARIABLE Print current value of VARIABLE\n"
152 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
153 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
154 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
155 " start [tincd options] Start tincd.\n"
156 " stop Stop tincd.\n"
157 " restart [tincd options] Restart tincd.\n"
158 " reload Partially reload configuration of running tincd.\n"
159 " pid Show PID of currently running tincd.\n"
160 #ifdef DISABLE_LEGACY
161 " generate-keys Generate a new Ed25519 public/private key pair.\n"
163 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
164 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
166 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
167 " dump Dump a list of one of the following things:\n"
168 " [reachable] nodes - all known nodes in the VPN\n"
169 " edges - all known connections in the VPN\n"
170 " subnets - all known subnets in the VPN\n"
171 " connections - all meta connections with ourself\n"
172 " [di]graph - graph of the VPN in dotty format\n"
173 " invitations - outstanding invitations\n"
174 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
175 " purge Purge unreachable nodes\n"
176 " debug N Set debug level\n"
177 " retry Retry all outgoing connections\n"
178 " disconnect NODE Close meta connection with NODE\n"
180 " top Show real-time statistics\n"
182 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
183 " log [level] Dump log output [up to the specified level]\n"
184 " export Export host configuration of local node to standard output\n"
185 " export-all Export all host configuration files to standard output\n"
186 " import Import host configuration file(s) from standard input\n"
187 " exchange Same as export followed by import\n"
188 " exchange-all Same as export-all followed by import\n"
189 " invite NODE [...] Generate an invitation for NODE\n"
190 " join INVITATION Join a VPN using an INVITATION\n"
191 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
192 " fsck Check the configuration files for problems.\n"
193 " sign [FILE] Generate a signed version of a file.\n"
194 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
196 "Report bugs to tinc@tinc-vpn.org.\n",
201 static bool parse_options(int argc, char **argv) {
203 int option_index = 0;
205 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
206 switch((option_t) r) {
207 case OPT_LONG_OPTION:
219 case OPT_CONFIG_FILE:
221 confbase = xstrdup(optarg);
222 confbasegiven = true;
227 netname = xstrdup(optarg);
240 pidfilename = xstrdup(optarg);
252 if(!netname && (netname = getenv("NETNAME"))) {
253 netname = xstrdup(netname);
256 /* netname "." is special: a "top-level name" */
258 if(netname && (!*netname || !strcmp(netname, "."))) {
263 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
264 fprintf(stderr, "Invalid character in netname!\n");
272 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
274 char directory[PATH_MAX] = ".";
280 /* Check stdin and stdout */
282 /* Ask for a file and/or directory name. */
283 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
285 if(fgets(buf, sizeof(buf), stdin) == NULL) {
286 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
290 size_t len = strlen(buf);
303 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
306 if(filename[0] != '/') {
309 /* The directory is a relative path or a filename. */
310 if(!getcwd(directory, sizeof(directory))) {
311 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
315 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
316 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
328 disable_old_keys(filename, what);
330 /* Open it first to keep the inode busy */
332 r = fopenmask(filename, mode, perms);
335 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
343 Generate a public/private Ed25519 key pair, and ask for a file to store
346 static bool ed25519_keygen(bool ask) {
349 char fname[PATH_MAX];
351 fprintf(stderr, "Generating Ed25519 key pair:\n");
353 if(!(key = ecdsa_generate())) {
354 fprintf(stderr, "Error during key generation!\n");
357 fprintf(stderr, "Done.\n");
360 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
361 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
367 if(!ecdsa_write_pem_private_key(key, f)) {
368 fprintf(stderr, "Error writing private key!\n");
375 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
377 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
380 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
386 char *pubkey = ecdsa_get_base64_public_key(key);
387 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
405 #ifndef DISABLE_LEGACY
407 Generate a public/private RSA key pair, and ask for a file to store
410 static bool rsa_keygen(int bits, bool ask) {
413 char fname[PATH_MAX];
415 // Make sure the key size is a multiple of 8 bits.
418 // Make sure that a valid key size is used.
419 if(bits < 1024 || bits > 8192) {
420 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
422 } else if(bits < 2048) {
423 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
426 fprintf(stderr, "Generating %d bits keys:\n", bits);
428 if(!(key = rsa_generate(bits, 0x10001))) {
429 fprintf(stderr, "Error during key generation!\n");
432 fprintf(stderr, "Done.\n");
435 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
436 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
442 if(!rsa_write_pem_private_key(key, f)) {
443 fprintf(stderr, "Error writing private key!\n");
450 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
452 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
455 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
461 if(!rsa_write_pem_public_key(key, f)) {
462 fprintf(stderr, "Error writing public key!\n");
485 bool recvline(int fd, char *line, size_t len) {
486 char *newline = NULL;
492 while(!(newline = memchr(buffer, '\n', blen))) {
493 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
495 if(nrecv == -1 && sockerrno == EINTR) {
497 } else if(nrecv <= 0) {
504 if((size_t)(newline - buffer) >= len) {
508 len = newline - buffer;
510 memcpy(line, buffer, len);
512 memmove(buffer, newline + 1, blen - len - 1);
518 static bool recvdata(int fd, char *data, size_t len) {
520 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
522 if(nrecv == -1 && sockerrno == EINTR) {
524 } else if(nrecv <= 0) {
531 memcpy(data, buffer, len);
532 memmove(buffer, buffer + len, blen - len);
538 bool sendline(int fd, const char *format, ...) {
539 static char buffer[4096];
544 va_start(ap, format);
545 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
546 buffer[sizeof(buffer) - 1] = 0;
549 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
557 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
559 if(nsend == -1 && sockerrno == EINTR) {
561 } else if(nsend <= 0) {
572 static void pcap(int fd, FILE *out, uint32_t snaplen) {
573 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
581 uint32_t tz_accuracy;
588 snaplen ? snaplen : sizeof(data),
601 fwrite(&header, sizeof(header), 1, out);
606 while(recvline(fd, line, sizeof(line))) {
609 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
610 gettimeofday(&tv, NULL);
612 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
616 if(!recvdata(fd, data, len)) {
620 packet.tv_sec = tv.tv_sec;
621 packet.tv_usec = tv.tv_usec;
623 packet.origlen = len;
624 fwrite(&packet, sizeof(packet), 1, out);
625 fwrite(data, len, 1, out);
630 static void log_control(int fd, FILE *out, int level, bool use_color) {
631 sendline(fd, "%d %d %d %d", CONTROL, REQ_LOG, level, use_color);
636 while(recvline(fd, line, sizeof(line))) {
638 int n = sscanf(line, "%d %d %d", &code, &req, &len);
640 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
644 if(!recvdata(fd, data, len)) {
648 fwrite(data, len, 1, out);
654 static bool stop_tincd(void) {
655 if(!connect_tincd(true)) {
659 sendline(fd, "%d %d", CONTROL, REQ_STOP);
661 while(recvline(fd, line, sizeof(line))) {
662 // wait for tincd to close the connection...
673 static bool remove_service(void) {
674 SC_HANDLE manager = NULL;
675 SC_HANDLE service = NULL;
676 SERVICE_STATUS status = {0};
677 bool success = false;
679 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
682 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
686 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
689 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
690 success = stop_tincd();
692 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
698 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
699 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
701 fprintf(stderr, "%s service stopped\n", identname);
704 if(!DeleteService(service)) {
705 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
714 CloseServiceHandle(service);
718 CloseServiceHandle(manager);
722 fprintf(stderr, "%s service removed\n", identname);
729 bool connect_tincd(bool verbose) {
734 struct timeval tv = {0, 0};
736 if(select(fd + 1, &r, NULL, NULL, &tv)) {
737 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
745 pidfile_t *pidfile = read_pidfile();
749 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
756 strcpy(controlcookie, pidfile->cookie);
761 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
762 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
763 /* clean up the stale socket and pid file */
765 unlink(unixsocketname);
769 struct sockaddr_un sa = {
770 .sun_family = AF_UNIX,
773 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
774 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
778 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
780 fd = socket(AF_UNIX, SOCK_STREAM, 0);
784 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
790 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
792 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
801 struct addrinfo hints = {
802 .ai_family = AF_UNSPEC,
803 .ai_socktype = SOCK_STREAM,
804 .ai_protocol = IPPROTO_TCP,
808 struct addrinfo *res = NULL;
810 if(getaddrinfo(pidfile->host, pidfile->port, &hints, &res) || !res) {
812 fprintf(stderr, "Cannot resolve %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
819 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
823 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
830 unsigned long arg = 0;
832 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
834 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
838 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
840 fprintf(stderr, "Cannot connect to %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
854 static const int one = 1;
855 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
858 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
863 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
865 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
873 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
875 fprintf(stderr, "Could not fully establish control socket connection\n");
887 static int cmd_start(int argc, char *argv[]) {
888 if(connect_tincd(false)) {
890 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
892 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
899 char *slash = strrchr(program_name, '/');
903 if((c = strrchr(program_name, '\\')) > slash) {
910 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
912 c = xstrdup("tincd");
916 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
921 Windows has no real concept of an "argv array". A command line is just one string.
922 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
923 it uses quotes to handle spaces in arguments.
924 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
925 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
926 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
928 xasprintf(&arg0, "\"%s\"", arg0);
930 nargv[nargc++] = arg0;
932 for(int i = 1; i < optind; i++) {
933 nargv[nargc++] = orig_argv[i];
936 for(int i = 1; i < argc; i++) {
937 nargv[nargc++] = argv[i];
941 int status = spawnvp(_P_WAIT, c, nargv);
947 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
953 int pfd[2] = {-1, -1};
955 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
956 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
965 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
974 snprintf(buf, sizeof(buf), "%d %d", pfd[1], use_ansi_escapes(stderr));
975 setenv("TINC_UMBILICAL", buf, true);
976 exit(execvp(c, nargv));
984 signal(SIGINT, SIG_IGN);
987 // Pass all log messages from the umbilical to stderr.
988 // A nul-byte right before closure means tincd started successfully.
993 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
994 failure = buf[len - 1];
1000 if(write(2, buf, len) != len) {
1001 // Nothing we can do about it.
1011 // Make sure the child process is really gone.
1013 pid_t result = waitpid(pid, &status, 0);
1016 signal(SIGINT, SIG_DFL);
1019 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1022 fprintf(stderr, "Error starting %s\n", c);
1027 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1031 static int cmd_stop(int argc, char *argv[]) {
1035 fprintf(stderr, "Too many arguments!\n");
1040 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1045 if(kill(pid, SIGTERM)) {
1046 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1050 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1051 waitpid(pid, NULL, 0);
1062 static int cmd_restart(int argc, char *argv[]) {
1064 return cmd_start(argc, argv);
1067 static int cmd_reload(int argc, char *argv[]) {
1071 fprintf(stderr, "Too many arguments!\n");
1075 if(!connect_tincd(true)) {
1079 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1081 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1082 fprintf(stderr, "Could not reload configuration.\n");
1090 static int dump_invitations(void) {
1091 char dname[PATH_MAX];
1092 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1093 DIR *dir = opendir(dname);
1096 if(errno == ENOENT) {
1097 fprintf(stderr, "No outstanding invitations.\n");
1101 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1109 while((ent = readdir(dir))) {
1110 char buf[MAX_STRING_SIZE];
1112 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1116 char fname[PATH_MAX];
1118 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1119 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1123 FILE *f = fopen(fname, "r");
1126 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1132 if(!fgets(buf, sizeof(buf), f)) {
1133 fprintf(stderr, "Invalid invitation file %s\n", fname);
1140 char *eol = buf + strlen(buf);
1142 while(strchr("\t \r\n", *--eol)) {
1146 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1147 fprintf(stderr, "Invalid invitation file %s\n", fname);
1152 printf("%s %s\n", ent->d_name, buf + 7);
1158 fprintf(stderr, "No outstanding invitations.\n");
1164 static int cmd_dump(int argc, char *argv[]) {
1165 bool only_reachable = false;
1167 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1168 if(strcasecmp(argv[2], "nodes")) {
1169 fprintf(stderr, "`reachable' only supported for nodes.\n");
1174 only_reachable = true;
1180 fprintf(stderr, "Invalid number of arguments.\n");
1185 if(!strcasecmp(argv[1], "invitations")) {
1186 return dump_invitations();
1189 if(!connect_tincd(true)) {
1195 if(!strcasecmp(argv[1], "nodes")) {
1196 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1197 } else if(!strcasecmp(argv[1], "edges")) {
1198 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1199 } else if(!strcasecmp(argv[1], "subnets")) {
1200 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1201 } else if(!strcasecmp(argv[1], "connections")) {
1202 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1203 } else if(!strcasecmp(argv[1], "graph")) {
1204 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1205 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1207 } else if(!strcasecmp(argv[1], "digraph")) {
1208 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1209 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1212 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1218 printf("graph {\n");
1219 } else if(do_graph == 2) {
1220 printf("digraph {\n");
1223 while(recvline(fd, line, sizeof(line))) {
1224 char node1[4096], node2[4096];
1225 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1228 if(do_graph && req == REQ_DUMP_NODES) {
1250 char local_host[4096];
1251 char local_port[4096];
1254 int cipher, digest, maclength, compression, distance, socket, weight;
1255 short int pmtu, minmtu, maxmtu;
1256 unsigned int options;
1257 node_status_t status;
1258 long int last_state_change;
1260 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1263 case REQ_DUMP_NODES: {
1264 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);
1267 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1272 const char *color = "black";
1274 if(!strcmp(host, "MYSELF")) {
1276 } else if(!status.reachable) {
1278 } else if(strcmp(via, node)) {
1280 } else if(!status.validkey) {
1282 } else if(minmtu > 0) {
1286 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1288 if(only_reachable && !status.reachable) {
1292 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,
1293 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);
1295 if(udp_ping_rtt != -1) {
1296 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1304 case REQ_DUMP_EDGES: {
1305 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);
1308 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1313 float w = 1.0f + 65536.0f / (float)weight;
1315 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1316 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1317 } else if(do_graph == 2) {
1318 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1321 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);
1326 case REQ_DUMP_SUBNETS: {
1327 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1330 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1334 printf("%s owner %s\n", strip_weight(subnet), node);
1338 case REQ_DUMP_CONNECTIONS: {
1339 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1342 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1346 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1351 fprintf(stderr, "Unable to parse dump from tincd.\n");
1356 fprintf(stderr, "Error receiving dump.\n");
1360 static int cmd_purge(int argc, char *argv[]) {
1364 fprintf(stderr, "Too many arguments!\n");
1368 if(!connect_tincd(true)) {
1372 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1374 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1375 fprintf(stderr, "Could not purge old information.\n");
1382 static int cmd_debug(int argc, char *argv[]) {
1384 fprintf(stderr, "Invalid number of arguments.\n");
1388 if(!connect_tincd(true)) {
1392 int debuglevel = atoi(argv[1]);
1395 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1397 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1398 fprintf(stderr, "Could not set debug level.\n");
1402 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1406 static int cmd_retry(int argc, char *argv[]) {
1410 fprintf(stderr, "Too many arguments!\n");
1414 if(!connect_tincd(true)) {
1418 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1420 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1421 fprintf(stderr, "Could not retry outgoing connections.\n");
1428 static int cmd_connect(int argc, char *argv[]) {
1430 fprintf(stderr, "Invalid number of arguments.\n");
1434 if(!check_id(argv[1])) {
1435 fprintf(stderr, "Invalid name for node.\n");
1439 if(!connect_tincd(true)) {
1443 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1445 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1446 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1453 static int cmd_disconnect(int argc, char *argv[]) {
1455 fprintf(stderr, "Invalid number of arguments.\n");
1459 if(!check_id(argv[1])) {
1460 fprintf(stderr, "Invalid name for node.\n");
1464 if(!connect_tincd(true)) {
1468 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1470 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1471 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1478 static int cmd_top(int argc, char *argv[]) {
1482 fprintf(stderr, "Too many arguments!\n");
1488 if(!connect_tincd(true)) {
1495 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1500 static int cmd_pcap(int argc, char *argv[]) {
1502 fprintf(stderr, "Too many arguments!\n");
1506 if(!connect_tincd(true)) {
1510 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1515 static void sigint_handler(int sig) {
1518 fprintf(stderr, "\n");
1519 shutdown(fd, SHUT_RDWR);
1523 static int cmd_log(int argc, char *argv[]) {
1525 fprintf(stderr, "Too many arguments!\n");
1529 if(!connect_tincd(true)) {
1534 signal(SIGINT, sigint_handler);
1537 bool use_color = use_ansi_escapes(stdout);
1538 log_control(fd, stdout, argc > 1 ? atoi(argv[1]) : DEBUG_UNSET, use_color);
1541 signal(SIGINT, SIG_DFL);
1549 static int cmd_pid(int argc, char *argv[]) {
1553 fprintf(stderr, "Too many arguments!\n");
1557 if(!connect_tincd(true) || !pid) {
1561 printf("%d\n", pid);
1565 size_t rstrip(char *value) {
1566 size_t len = strlen(value);
1568 while(len && strchr("\t\r\n ", value[len - 1])) {
1575 char *get_my_name(bool verbose) {
1576 FILE *f = fopen(tinc_conf, "r");
1580 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1589 while(fgets(buf, sizeof(buf), f)) {
1590 size_t len = strcspn(buf, "\t =");
1592 value += strspn(value, "\t ");
1596 value += strspn(value, "\t ");
1599 if(!rstrip(value)) {
1605 if(strcasecmp(buf, "Name")) {
1611 return replace_name(value);
1618 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1624 static ecdsa_t *get_pubkey(FILE *f) ATTR_MALLOC ATTR_DEALLOCATOR(ecdsa_free);
1625 static ecdsa_t *get_pubkey(FILE *f) {
1629 while(fgets(buf, sizeof(buf), f)) {
1630 size_t len = strcspn(buf, "\t =");
1632 value += strspn(value, "\t ");
1636 value += strspn(value, "\t ");
1639 if(!rstrip(value)) {
1645 if(strcasecmp(buf, "Ed25519PublicKey")) {
1650 return ecdsa_set_base64_public_key(value);
1657 const var_t variables[] = {
1658 /* Server configuration */
1659 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1660 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1661 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1662 {"BindToInterface", VAR_SERVER},
1663 {"Broadcast", VAR_SERVER | VAR_SAFE},
1664 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1665 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1666 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1667 {"Device", VAR_SERVER},
1668 {"DeviceStandby", VAR_SERVER},
1669 {"DeviceType", VAR_SERVER},
1670 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1671 {"Ed25519PrivateKeyFile", VAR_SERVER},
1672 {"ExperimentalProtocol", VAR_SERVER},
1673 {"Forwarding", VAR_SERVER},
1674 {"FWMark", VAR_SERVER},
1675 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1676 {"Hostnames", VAR_SERVER},
1677 {"IffOneQueue", VAR_SERVER},
1678 {"Interface", VAR_SERVER},
1679 {"InvitationExpire", VAR_SERVER},
1680 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1681 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1682 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1683 {"LogLevel", VAR_SERVER},
1684 {"MACExpire", VAR_SERVER | VAR_SAFE},
1685 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1686 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1687 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1688 {"Mode", VAR_SERVER | VAR_SAFE},
1689 {"Name", VAR_SERVER},
1690 {"PingInterval", VAR_SERVER | VAR_SAFE},
1691 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1692 {"PriorityInheritance", VAR_SERVER},
1693 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1694 {"PrivateKeyFile", VAR_SERVER},
1695 {"ProcessPriority", VAR_SERVER},
1696 {"Proxy", VAR_SERVER},
1697 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1698 {"ScriptsExtension", VAR_SERVER},
1699 {"ScriptsInterpreter", VAR_SERVER},
1700 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1701 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1702 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1703 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1704 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1705 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1706 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1707 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1708 {"UDPRcvBuf", VAR_SERVER},
1709 {"UDPSndBuf", VAR_SERVER},
1710 {"UPnP", VAR_SERVER},
1711 {"UPnPDiscoverWait", VAR_SERVER},
1712 {"UPnPRefreshPeriod", VAR_SERVER},
1713 {"VDEGroup", VAR_SERVER},
1714 {"VDEPort", VAR_SERVER},
1715 /* Host configuration */
1716 {"Address", VAR_HOST | VAR_MULTIPLE},
1717 {"Cipher", VAR_SERVER | VAR_HOST},
1718 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1719 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1720 {"Digest", VAR_SERVER | VAR_HOST},
1721 {"Ed25519PublicKey", VAR_HOST},
1722 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1723 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1724 {"MACLength", VAR_SERVER | VAR_HOST},
1725 {"PMTU", VAR_SERVER | VAR_HOST},
1726 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1728 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1729 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1730 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1731 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1732 {"Weight", VAR_HOST | VAR_SAFE},
1736 // Request actual port from tincd
1737 static bool read_actual_port(void) {
1738 pidfile_t *pidfile = read_pidfile();
1741 printf("%s\n", pidfile->port);
1745 fprintf(stderr, "Could not get port from the pidfile.\n");
1750 static int cmd_config(int argc, char *argv[]) {
1752 fprintf(stderr, "Invalid number of arguments.\n");
1756 if(strcasecmp(argv[0], "config")) {
1760 typedef enum { GET, DEL, SET, ADD } action_t;
1761 action_t action = GET;
1763 if(!strcasecmp(argv[1], "get")) {
1765 } else if(!strcasecmp(argv[1], "add")) {
1766 argv++, argc--, action = ADD;
1767 } else if(!strcasecmp(argv[1], "del")) {
1768 argv++, argc--, action = DEL;
1769 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1770 argv++, argc--, action = SET;
1774 fprintf(stderr, "Invalid number of arguments.\n");
1778 // Concatenate the rest of the command line
1779 strncpy(line, argv[1], sizeof(line) - 1);
1781 for(int i = 2; i < argc; i++) {
1782 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1783 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1786 // Liberal parsing into node name, variable name and value.
1792 len = strcspn(line, "\t =");
1794 value += strspn(value, "\t ");
1798 value += strspn(value, "\t ");
1802 variable = strchr(line, '.');
1812 fprintf(stderr, "No variable given.\n");
1816 if((action == SET || action == ADD) && !*value) {
1817 fprintf(stderr, "No value for variable given.\n");
1821 if(action == GET && *value) {
1825 // If port is requested, try reading it from the pidfile and fall back to configs if that fails
1826 if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
1830 /* Some simple checks. */
1832 bool warnonremove = false;
1834 for(int i = 0; variables[i].name; i++) {
1835 if(strcasecmp(variables[i].name, variable)) {
1840 variable = (char *)variables[i].name;
1842 if(!strcasecmp(variable, "Subnet") && *value) {
1845 if(!str2net(&s, value)) {
1846 fprintf(stderr, "Malformed subnet definition %s\n", value);
1850 if(!subnetcheck(s)) {
1851 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1856 /* Discourage use of obsolete variables. */
1858 if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
1860 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1862 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1867 /* Don't put server variables in host config files */
1869 if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
1871 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1873 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1878 /* Should this go into our own host config file? */
1880 if(!node && !(variables[i].type & VAR_SERVER)) {
1881 node = get_my_name(true);
1888 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1889 Turn on warnings when it seems variables might be removed unintentionally. */
1891 if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
1892 warnonremove = true;
1894 } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
1895 warnonremove = true;
1901 if(node && !check_id(node)) {
1902 fprintf(stderr, "Invalid name for node.\n");
1912 if(force || action == GET || action == DEL) {
1913 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1915 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1917 if(node && node != line) {
1925 // Open the right configuration file.
1926 char filename[PATH_MAX];
1929 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node) >= sizeof(filename)) {
1930 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1940 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1943 FILE *f = fopen(filename, "r");
1946 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1950 char tmpfile[PATH_MAX];
1954 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1955 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1959 tf = fopen(tmpfile, "w");
1962 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1968 // Copy the file, making modifications on the fly, unless we are just getting a value.
1972 bool removed = false;
1975 while(fgets(buf1, sizeof(buf1), f)) {
1976 buf1[sizeof(buf1) - 1] = 0;
1977 strncpy(buf2, buf1, sizeof(buf2));
1979 // Parse line in a simple way
1982 size_t len = strcspn(buf2, "\t =");
1983 bvalue = buf2 + len;
1984 bvalue += strspn(bvalue, "\t ");
1986 if(*bvalue == '=') {
1988 bvalue += strspn(bvalue, "\t ");
1995 if(!strcasecmp(buf2, variable)) {
1998 printf("%s\n", bvalue);
1999 } else if(action == DEL) {
2000 if(!*value || !strcasecmp(bvalue, value)) {
2004 } else if(action == SET) {
2005 // Warn if "set" was used for variables that can occur multiple times
2006 if(warnonremove && strcasecmp(bvalue, value)) {
2007 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2010 // Already set? Delete the rest...
2015 // Otherwise, replace.
2016 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2017 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2023 } else if(action == ADD) {
2024 // Check if we've already seen this variable with the same value
2025 if(!strcasecmp(bvalue, value)) {
2032 // Copy original line...
2033 if(fputs(buf1, tf) < 0) {
2034 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2038 // Add newline if it is missing...
2039 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2040 if(fputc('\n', tf) < 0) {
2041 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2048 // Make sure we read everything...
2049 if(ferror(f) || !feof(f)) {
2050 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2055 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2059 // Add new variable if necessary.
2060 if((action == ADD && !found) || (action == SET && !set)) {
2061 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2062 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2071 fprintf(stderr, "No matching configuration variables found.\n");
2076 // Make sure we wrote everything...
2078 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2082 // Could we find what we had to remove?
2083 if((action == GET || action == DEL) && !removed) {
2085 fprintf(stderr, "No configuration variables deleted.\n");
2089 // Replace the configuration file with the new one
2092 if(remove(filename)) {
2093 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2099 if(rename(tmpfile, filename)) {
2100 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2104 // Silently try notifying a running tincd of changes.
2105 if(connect_tincd(false)) {
2106 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2112 static bool try_bind(int port) {
2113 struct addrinfo *ai = NULL, *aip;
2114 struct addrinfo hint = {
2115 .ai_flags = AI_PASSIVE,
2116 .ai_family = AF_UNSPEC,
2117 .ai_socktype = SOCK_STREAM,
2118 .ai_protocol = IPPROTO_TCP,
2121 bool success = true;
2123 snprintf(portstr, sizeof(portstr), "%d", port);
2125 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2129 for(aip = ai; aip; aip = aip->ai_next) {
2130 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2137 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2150 int check_port(const char *name) {
2155 fprintf(stderr, "Warning: could not bind to port 655. ");
2157 for(int i = 0; i < 100; i++) {
2158 uint16_t port = 0x1000 + prng(0x8000);
2160 if(try_bind(port)) {
2161 char filename[PATH_MAX];
2162 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2163 FILE *f = fopen(filename, "a");
2166 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2167 fprintf(stderr, "Please change tinc's Port manually.\n");
2171 fprintf(f, "Port = %d\n", port);
2173 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2178 fprintf(stderr, "Please change tinc's Port manually.\n");
2182 static int cmd_init(int argc, char *argv[]) {
2183 if(!access(tinc_conf, F_OK)) {
2184 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2189 fprintf(stderr, "Too many arguments!\n");
2191 } else if(argc < 2) {
2194 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2196 if(!fgets(buf, sizeof(buf), stdin)) {
2197 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2201 size_t len = rstrip(buf);
2204 fprintf(stderr, "No name given!\n");
2210 fprintf(stderr, "No Name given!\n");
2214 name = strdup(argv[1]);
2217 fprintf(stderr, "No Name given!\n");
2222 if(!check_id(name)) {
2223 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2227 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2228 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2232 if(mkdir(confbase, 0777) && errno != EEXIST) {
2233 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2237 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2238 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2242 FILE *f = fopen(tinc_conf, "w");
2245 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2249 fprintf(f, "Name = %s\n", name);
2252 #ifndef DISABLE_LEGACY
2254 if(!rsa_keygen(2048, false)) {
2260 if(!ed25519_keygen(false)) {
2266 #ifndef HAVE_WINDOWS
2267 char filename[PATH_MAX];
2268 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2270 if(access(filename, F_OK)) {
2271 FILE *f = fopenmask(filename, "w", 0777);
2274 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2278 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");
2288 static int cmd_generate_keys(int argc, char *argv[]) {
2289 #ifdef DISABLE_LEGACY
2297 fprintf(stderr, "Too many arguments!\n");
2302 name = get_my_name(false);
2305 #ifndef DISABLE_LEGACY
2307 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2313 if(!ed25519_keygen(true)) {
2320 #ifndef DISABLE_LEGACY
2321 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2323 fprintf(stderr, "Too many arguments!\n");
2328 name = get_my_name(false);
2331 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2335 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2339 fprintf(stderr, "Too many arguments!\n");
2344 name = get_my_name(false);
2347 return !ed25519_keygen(true);
2350 static int cmd_help(int argc, char *argv[]) {
2358 static int cmd_version(int argc, char *argv[]) {
2362 fprintf(stderr, "Too many arguments!\n");
2370 static int cmd_info(int argc, char *argv[]) {
2372 fprintf(stderr, "Invalid number of arguments.\n");
2376 if(!connect_tincd(true)) {
2380 return info(fd, argv[1]);
2383 static const char *conffiles[] = {
2394 static int cmd_edit(int argc, char *argv[]) {
2396 fprintf(stderr, "Invalid number of arguments.\n");
2400 char filename[PATH_MAX] = "";
2402 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2403 for(int i = 0; conffiles[i]; i++) {
2404 if(!strcmp(argv[1], conffiles[i])) {
2405 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2414 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2415 char *dash = strchr(argv[1], '-');
2420 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2421 fprintf(stderr, "Invalid configuration filename.\n");
2428 #ifndef HAVE_WINDOWS
2429 const char *editor = getenv("VISUAL");
2432 editor = getenv("EDITOR");
2439 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2441 xasprintf(&command, "edit \"%s\"", filename);
2443 int result = system(command);
2450 // Silently try notifying a running tincd of changes.
2451 if(connect_tincd(false)) {
2452 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2458 static int export(const char *name, FILE *out) {
2459 char filename[PATH_MAX];
2460 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2461 FILE *in = fopen(filename, "r");
2464 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2468 fprintf(out, "Name = %s\n", name);
2471 while(fgets(buf, sizeof(buf), in)) {
2472 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2478 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2487 static int cmd_export(int argc, char *argv[]) {
2491 fprintf(stderr, "Too many arguments!\n");
2495 char *name = get_my_name(true);
2501 int result = export(name, stdout);
2511 static int cmd_export_all(int argc, char *argv[]) {
2515 fprintf(stderr, "Too many arguments!\n");
2519 DIR *dir = opendir(hosts_dir);
2522 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2530 while((ent = readdir(dir))) {
2531 if(!check_id(ent->d_name)) {
2538 printf("#---------------------------------------------------------------#\n");
2541 result |= export(ent->d_name, stdout);
2553 static int cmd_import(int argc, char *argv[]) {
2557 fprintf(stderr, "Too many arguments!\n");
2566 char filename[PATH_MAX] = "";
2568 bool firstline = true;
2570 while(fgets(buf, sizeof(buf), in)) {
2571 if(sscanf(buf, "Name = %4095s", name) == 1) {
2574 if(!check_id(name)) {
2575 fprintf(stderr, "Invalid Name in input!\n");
2583 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2584 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2588 if(!force && !access(filename, F_OK)) {
2589 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2594 out = fopen(filename, "w");
2597 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2603 } else if(firstline) {
2604 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2609 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2614 if(fputs(buf, out) < 0) {
2615 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2626 fprintf(stderr, "Imported %d host configuration files.\n", count);
2629 fprintf(stderr, "No host configuration files imported.\n");
2634 static int cmd_exchange(int argc, char *argv[]) {
2635 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2638 static int cmd_exchange_all(int argc, char *argv[]) {
2639 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2642 static int switch_network(char *name) {
2643 if(strcmp(name, ".")) {
2644 if(!check_netname(name, false)) {
2645 fprintf(stderr, "Invalid character in netname!\n");
2649 if(!check_netname(name, true)) {
2650 fprintf(stderr, "Warning: unsafe character in netname!\n");
2660 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2667 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2668 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2669 xasprintf(&prompt, "%s> ", identname);
2674 static int cmd_network(int argc, char *argv[]) {
2676 fprintf(stderr, "Too many arguments!\n");
2681 return switch_network(argv[1]);
2684 DIR *dir = opendir(confdir);
2687 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2693 while((ent = readdir(dir))) {
2694 if(*ent->d_name == '.') {
2698 if(!strcmp(ent->d_name, "tinc.conf")) {
2703 char fname[PATH_MAX];
2704 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2706 if(!access(fname, R_OK)) {
2707 printf("%s\n", ent->d_name);
2716 static int cmd_fsck(int argc, char *argv[]) {
2720 fprintf(stderr, "Too many arguments!\n");
2724 return fsck(orig_argv[0]);
2727 static void *readfile(FILE *in, size_t *len) {
2729 size_t bufsize = 4096;
2730 char *buf = xmalloc(bufsize);
2733 size_t read = fread(buf + count, 1, bufsize - count, in);
2741 if(count >= bufsize) {
2743 buf = xrealloc(buf, bufsize);
2754 static int cmd_sign(int argc, char *argv[]) {
2756 fprintf(stderr, "Too many arguments!\n");
2761 name = get_my_name(true);
2768 char fname[PATH_MAX];
2769 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2770 FILE *fp = fopen(fname, "r");
2773 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2777 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2780 fprintf(stderr, "Could not read private key from %s\n", fname);
2790 in = fopen(argv[1], "rb");
2793 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2802 char *data = readfile(in, &len);
2809 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2814 // Ensure we sign our name and current time as well
2815 long t = time(NULL);
2817 xasprintf(&trailer, " %s %ld", name, t);
2818 size_t trailer_len = strlen(trailer);
2820 data = xrealloc(data, len + trailer_len);
2821 memcpy(data + len, trailer, trailer_len);
2826 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2827 fprintf(stderr, "Error generating signature\n");
2833 b64encode_tinc(sig, sig, 64);
2836 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2837 fwrite(data, len, 1, stdout);
2843 static int cmd_verify(int argc, char *argv[]) {
2845 fprintf(stderr, "Not enough arguments!\n");
2850 fprintf(stderr, "Too many arguments!\n");
2854 char *node = argv[1];
2856 if(!strcmp(node, ".")) {
2858 name = get_my_name(true);
2866 } else if(!strcmp(node, "*")) {
2869 if(!check_id(node)) {
2870 fprintf(stderr, "Invalid node name\n");
2878 in = fopen(argv[2], "rb");
2881 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2889 char *data = readfile(in, &len);
2896 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2900 char *newline = memchr(data, '\n', len);
2902 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2903 fprintf(stderr, "Invalid input\n");
2909 size_t skip = newline - data;
2911 char signer[MAX_STRING_SIZE] = "";
2912 char sig[MAX_STRING_SIZE] = "";
2915 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2916 fprintf(stderr, "Invalid input\n");
2921 if(node && strcmp(node, signer)) {
2922 fprintf(stderr, "Signature is not made by %s\n", node);
2932 xasprintf(&trailer, " %s %ld", signer, t);
2933 size_t trailer_len = strlen(trailer);
2935 data = xrealloc(data, len + trailer_len);
2936 memcpy(data + len, trailer, trailer_len);
2939 newline = data + skip;
2941 char fname[PATH_MAX];
2942 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2943 FILE *fp = fopen(fname, "r");
2946 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2951 ecdsa_t *key = get_pubkey(fp);
2955 key = ecdsa_read_pem_public_key(fp);
2959 fprintf(stderr, "Could not read public key from %s\n", fname);
2967 if(b64decode_tinc(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2968 fprintf(stderr, "Invalid signature\n");
2976 fwrite(newline, len - (newline - data), 1, stdout);
2982 static const struct {
2983 const char *command;
2984 int (*function)(int argc, char *argv[]);
2987 {"start", cmd_start, false},
2988 {"stop", cmd_stop, false},
2989 {"restart", cmd_restart, false},
2990 {"reload", cmd_reload, false},
2991 {"dump", cmd_dump, false},
2992 {"list", cmd_dump, false},
2993 {"purge", cmd_purge, false},
2994 {"debug", cmd_debug, false},
2995 {"retry", cmd_retry, false},
2996 {"connect", cmd_connect, false},
2997 {"disconnect", cmd_disconnect, false},
2998 {"top", cmd_top, false},
2999 {"pcap", cmd_pcap, false},
3000 {"log", cmd_log, false},
3001 {"pid", cmd_pid, false},
3002 {"config", cmd_config, true},
3003 {"add", cmd_config, false},
3004 {"del", cmd_config, false},
3005 {"get", cmd_config, false},
3006 {"set", cmd_config, false},
3007 {"init", cmd_init, false},
3008 {"generate-keys", cmd_generate_keys, false},
3009 #ifndef DISABLE_LEGACY
3010 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3012 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3013 {"help", cmd_help, false},
3014 {"version", cmd_version, false},
3015 {"info", cmd_info, false},
3016 {"edit", cmd_edit, false},
3017 {"export", cmd_export, false},
3018 {"export-all", cmd_export_all, false},
3019 {"import", cmd_import, false},
3020 {"exchange", cmd_exchange, false},
3021 {"exchange-all", cmd_exchange_all, false},
3022 {"invite", cmd_invite, false},
3023 {"join", cmd_join, false},
3024 {"network", cmd_network, false},
3025 {"fsck", cmd_fsck, false},
3026 {"sign", cmd_sign, false},
3027 {"verify", cmd_verify, false},
3028 {NULL, NULL, false},
3031 #ifdef HAVE_READLINE
3032 static char *complete_command(const char *text, int state) {
3041 while(commands[i].command) {
3042 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3043 return xstrdup(commands[i].command);
3052 static char *complete_dump(const char *text, int state) {
3053 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3063 if(!strncasecmp(matches[i], text, strlen(text))) {
3064 return xstrdup(matches[i]);
3073 static char *complete_config(const char *text, int state) {
3082 while(variables[i].name) {
3083 char *dot = strchr(text, '.');
3086 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3088 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3092 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3093 return xstrdup(variables[i].name);
3103 static char *complete_info(const char *text, int state) {
3109 if(!connect_tincd(false)) {
3113 // Check the list of nodes
3114 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3115 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3118 while(recvline(fd, line, sizeof(line))) {
3120 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3133 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3137 if(!strncmp(item, text, strlen(text))) {
3138 return xstrdup(strip_weight(item));
3145 static char *complete_nothing(const char *text, int state) {
3151 static char **completion(const char *text, int start, int end) {
3153 char **matches = NULL;
3156 matches = rl_completion_matches(text, complete_command);
3157 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3158 matches = rl_completion_matches(text, complete_dump);
3159 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3160 matches = rl_completion_matches(text, complete_config);
3161 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3162 matches = rl_completion_matches(text, complete_config);
3163 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3164 matches = rl_completion_matches(text, complete_config);
3165 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3166 matches = rl_completion_matches(text, complete_config);
3167 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3168 matches = rl_completion_matches(text, complete_info);
3175 static int cmd_shell(int argc, char *argv[]) {
3176 xasprintf(&prompt, "%s> ", identname);
3180 int maxargs = argc + 16;
3181 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3183 for(int i = 0; i < argc; i++) {
3187 #ifdef HAVE_READLINE
3188 rl_readline_name = "tinc";
3189 rl_basic_word_break_characters = "\t\n ";
3190 rl_completion_entry_function = complete_nothing;
3191 rl_attempted_completion_function = completion;
3192 rl_filename_completion_desired = 0;
3197 #ifdef HAVE_READLINE
3202 line = readline(prompt);
3203 copy = line ? xstrdup(line) : NULL;
3205 line = fgets(buf, sizeof(buf), stdin);
3211 fputs(prompt, stdout);
3214 line = fgets(buf, sizeof(buf), stdin);
3221 /* Ignore comments */
3230 char *p = line + strspn(line, " \t\n");
3231 char *next = strtok(p, " \t\n");
3234 if(nargc >= maxargs) {
3236 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3241 next = strtok(NULL, " \t\n");
3248 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3249 #ifdef HAVE_READLINE
3258 for(int i = 0; commands[i].command; i++) {
3259 if(!strcasecmp(nargv[argc], commands[i].command)) {
3260 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3266 #ifdef HAVE_READLINE
3275 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3280 #ifdef HAVE_READLINE
3292 static void cleanup(void) {
3298 static int run_command(int argc, char *argv[]) {
3299 if(optind >= argc) {
3300 return cmd_shell(argc, argv);
3303 for(int i = 0; commands[i].command; i++) {
3304 if(!strcasecmp(argv[optind], commands[i].command)) {
3305 return commands[i].function(argc - optind, argv + optind);
3309 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
3314 int main(int argc, char *argv[]) {
3315 program_name = argv[0];
3317 tty = isatty(0) && isatty(1);
3319 if(!parse_options(argc, argv)) {
3324 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3325 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3339 static struct WSAData wsa_state;
3341 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3342 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3348 gettimeofday(&now, NULL);
3353 int result = run_command(argc, argv);