2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2021 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.
25 #include "readline/readline.h"
26 #include "readline/history.h"
31 #include "control_common.h"
36 #include "invitation.h"
47 #define MSG_NOSIGNAL 0
50 static char **orig_argv;
52 /* If nonzero, display usage information and exit. */
53 static bool show_help = false;
55 /* If nonzero, print the version on standard output and exit. */
56 static bool show_version = false;
58 static char *name = NULL;
59 static char controlcookie[1025];
60 char *tinc_conf = NULL;
61 char *hosts_dir = NULL;
64 // Horrible global variables...
73 bool confbasegiven = false;
74 char *scriptinterpreter = NULL;
75 char *scriptextension = "";
81 static struct option const long_options[] = {
82 {"batch", no_argument, NULL, 'b'},
83 {"config", required_argument, NULL, 'c'},
84 {"net", required_argument, NULL, 'n'},
85 {"help", no_argument, NULL, 1},
86 {"version", no_argument, NULL, 2},
87 {"pidfile", required_argument, NULL, 3},
88 {"force", no_argument, NULL, 4},
92 static void version(void) {
93 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
94 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
102 #ifndef DISABLE_LEGACY
106 printf("Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
107 "See the AUTHORS file for a complete list.\n\n"
108 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
109 "and you are welcome to redistribute it under certain conditions;\n"
110 "see the file COPYING for details.\n");
113 static void usage(bool status) {
115 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
117 printf("Usage: %s [options] command\n\n", program_name);
118 printf("Valid options are:\n"
119 " -b, --batch Don't ask for anything (non-interactive mode).\n"
120 " -c, --config=DIR Read configuration options from DIR.\n"
121 " -n, --net=NETNAME Connect to net NETNAME.\n"
122 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
123 " --force Force some commands to work despite warnings.\n"
124 " --help Display this help and exit.\n"
125 " --version Output version information and exit.\n"
127 "Valid commands are:\n"
128 " init [name] Create initial configuration files.\n"
129 " get VARIABLE Print current value of VARIABLE\n"
130 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
131 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
132 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
133 " start [tincd options] Start tincd.\n"
134 " stop Stop tincd.\n"
135 " restart [tincd options] Restart tincd.\n"
136 " reload Partially reload configuration of running tincd.\n"
137 " pid Show PID of currently running tincd.\n"
138 #ifdef DISABLE_LEGACY
139 " generate-keys Generate a new Ed25519 public/private key pair.\n"
141 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
142 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
144 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
145 " dump Dump a list of one of the following things:\n"
146 " [reachable] nodes - all known nodes in the VPN\n"
147 " edges - all known connections in the VPN\n"
148 " subnets - all known subnets in the VPN\n"
149 " connections - all meta connections with ourself\n"
150 " [di]graph - graph of the VPN in dotty format\n"
151 " invitations - outstanding invitations\n"
152 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
153 " purge Purge unreachable nodes\n"
154 " debug N Set debug level\n"
155 " retry Retry all outgoing connections\n"
156 " disconnect NODE Close meta connection with NODE\n"
158 " top Show real-time statistics\n"
160 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
161 " log [level] Dump log output [up to the specified level]\n"
162 " export Export host configuration of local node to standard output\n"
163 " export-all Export all host configuration files to standard output\n"
164 " import Import host configuration file(s) from standard input\n"
165 " exchange Same as export followed by import\n"
166 " exchange-all Same as export-all followed by import\n"
167 " invite NODE [...] Generate an invitation for NODE\n"
168 " join INVITATION Join a VPN using an INVITATION\n"
169 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
170 " fsck Check the configuration files for problems.\n"
171 " sign [FILE] Generate a signed version of a file.\n"
172 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
174 printf("Report bugs to tinc@tinc-vpn.org.\n");
178 static bool parse_options(int argc, char **argv) {
180 int option_index = 0;
182 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
184 case 0: /* long option */
191 case 'c': /* config file */
193 confbase = xstrdup(optarg);
194 confbasegiven = true;
197 case 'n': /* net name given */
199 netname = xstrdup(optarg);
202 case 1: /* show help */
206 case 2: /* show version */
210 case 3: /* open control socket here */
212 pidfilename = xstrdup(optarg);
219 case '?': /* wrong options */
229 if(!netname && (netname = getenv("NETNAME"))) {
230 netname = xstrdup(netname);
233 /* netname "." is special: a "top-level name" */
235 if(netname && (!*netname || !strcmp(netname, "."))) {
240 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
241 fprintf(stderr, "Invalid character in netname!\n");
249 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
251 char directory[PATH_MAX] = ".";
257 /* Check stdin and stdout */
259 /* Ask for a file and/or directory name. */
260 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
262 if(fgets(buf, sizeof(buf), stdin) == NULL) {
263 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
267 size_t len = strlen(buf);
280 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
283 if(filename[0] != '/') {
285 /* The directory is a relative path or a filename. */
286 getcwd(directory, sizeof(directory));
288 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
289 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
301 disable_old_keys(filename, what);
303 /* Open it first to keep the inode busy */
305 r = fopenmask(filename, mode, perms);
308 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
316 Generate a public/private Ed25519 key pair, and ask for a file to store
319 static bool ed25519_keygen(bool ask) {
322 char fname[PATH_MAX];
324 fprintf(stderr, "Generating Ed25519 key pair:\n");
326 if(!(key = ecdsa_generate())) {
327 fprintf(stderr, "Error during key generation!\n");
330 fprintf(stderr, "Done.\n");
333 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
334 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
340 if(!ecdsa_write_pem_private_key(key, f)) {
341 fprintf(stderr, "Error writing private key!\n");
348 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
350 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
353 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
359 char *pubkey = ecdsa_get_base64_public_key(key);
360 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
378 #ifndef DISABLE_LEGACY
380 Generate a public/private RSA key pair, and ask for a file to store
383 static bool rsa_keygen(int bits, bool ask) {
386 char fname[PATH_MAX];
388 // Make sure the key size is a multiple of 8 bits.
391 // Make sure that a valid key size is used.
392 if(bits < 1024 || bits > 8192) {
393 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
395 } else if(bits < 2048) {
396 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
399 fprintf(stderr, "Generating %d bits keys:\n", bits);
401 if(!(key = rsa_generate(bits, 0x10001))) {
402 fprintf(stderr, "Error during key generation!\n");
405 fprintf(stderr, "Done.\n");
408 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
409 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
415 if(!rsa_write_pem_private_key(key, f)) {
416 fprintf(stderr, "Error writing private key!\n");
423 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
425 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
428 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
434 if(!rsa_write_pem_public_key(key, f)) {
435 fprintf(stderr, "Error writing public key!\n");
458 bool recvline(int fd, char *line, size_t len) {
459 char *newline = NULL;
465 while(!(newline = memchr(buffer, '\n', blen))) {
466 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
468 if(nrecv == -1 && sockerrno == EINTR) {
470 } else if(nrecv <= 0) {
477 if((size_t)(newline - buffer) >= len) {
481 len = newline - buffer;
483 memcpy(line, buffer, len);
485 memmove(buffer, newline + 1, blen - len - 1);
491 static bool recvdata(int fd, char *data, size_t len) {
493 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
495 if(nrecv == -1 && sockerrno == EINTR) {
497 } else if(nrecv <= 0) {
504 memcpy(data, buffer, len);
505 memmove(buffer, buffer + len, blen - len);
511 bool sendline(int fd, char *format, ...) {
512 static char buffer[4096];
517 va_start(ap, format);
518 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
519 buffer[sizeof(buffer) - 1] = 0;
522 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
530 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
532 if(nsend == -1 && sockerrno == EINTR) {
534 } else if(nsend <= 0) {
545 static void pcap(int fd, FILE *out, uint32_t snaplen) {
546 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
554 uint32_t tz_accuracy;
561 snaplen ? snaplen : sizeof(data),
574 fwrite(&header, sizeof(header), 1, out);
579 while(recvline(fd, line, sizeof(line))) {
582 int n = sscanf(line, "%d %d %zd", &code, &req, &len);
583 gettimeofday(&tv, NULL);
585 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
589 if(!recvdata(fd, data, len)) {
593 packet.tv_sec = tv.tv_sec;
594 packet.tv_usec = tv.tv_usec;
596 packet.origlen = len;
597 fwrite(&packet, sizeof(packet), 1, out);
598 fwrite(data, len, 1, out);
603 static void logcontrol(int fd, FILE *out, int level) {
604 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
608 while(recvline(fd, line, sizeof(line))) {
610 int n = sscanf(line, "%d %d %d", &code, &req, &len);
612 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
616 if(!recvdata(fd, data, len)) {
620 fwrite(data, len, 1, out);
626 static bool stop_tincd(void) {
627 if(!connect_tincd(true)) {
631 sendline(fd, "%d %d", CONTROL, REQ_STOP);
633 while(recvline(fd, line, sizeof(line))) {
634 // wait for tincd to close the connection...
645 static bool remove_service(void) {
646 SC_HANDLE manager = NULL;
647 SC_HANDLE service = NULL;
648 SERVICE_STATUS status = {0};
649 bool success = false;
651 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
654 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
658 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
661 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
662 success = stop_tincd();
664 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
670 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
671 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
673 fprintf(stderr, "%s service stopped\n", identname);
676 if(!DeleteService(service)) {
677 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
686 CloseServiceHandle(service);
690 CloseServiceHandle(manager);
694 fprintf(stderr, "%s service removed\n", identname);
701 bool connect_tincd(bool verbose) {
706 struct timeval tv = {0, 0};
708 if(select(fd + 1, &r, NULL, NULL, &tv)) {
709 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
717 FILE *f = fopen(pidfilename, "r");
721 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
730 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
732 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
743 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
744 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
745 /* clean up the stale socket and pid file */
747 unlink(unixsocketname);
751 struct sockaddr_un sa = {
752 .sun_family = AF_UNIX,
755 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
756 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
760 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
762 fd = socket(AF_UNIX, SOCK_STREAM, 0);
766 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
772 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
774 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
783 struct addrinfo hints = {
784 .ai_family = AF_UNSPEC,
785 .ai_socktype = SOCK_STREAM,
786 .ai_protocol = IPPROTO_TCP,
790 struct addrinfo *res = NULL;
792 if(getaddrinfo(host, port, &hints, &res) || !res) {
794 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
800 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
804 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
810 unsigned long arg = 0;
812 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
814 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
818 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
820 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
832 static const int one = 1;
833 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
836 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
841 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
843 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
851 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
853 fprintf(stderr, "Could not fully establish control socket connection\n");
865 static int cmd_start(int argc, char *argv[]) {
866 if(connect_tincd(false)) {
868 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
870 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
877 char *slash = strrchr(program_name, '/');
881 if((c = strrchr(program_name, '\\')) > slash) {
887 char *default_c = "tincd";
890 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
896 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
901 Windows has no real concept of an "argv array". A command line is just one string.
902 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
903 it uses quotes to handle spaces in arguments.
904 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
905 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
906 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
908 xasprintf(&arg0, "\"%s\"", arg0);
910 nargv[nargc++] = arg0;
912 for(int i = 1; i < optind; i++) {
913 nargv[nargc++] = orig_argv[i];
916 for(int i = 1; i < argc; i++) {
917 nargv[nargc++] = argv[i];
921 int status = spawnvp(_P_WAIT, c, nargv);
930 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
936 int pfd[2] = {-1, -1};
938 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
939 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
952 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
965 snprintf(buf, sizeof(buf), "%d", pfd[1]);
966 setenv("TINC_UMBILICAL", buf, true);
967 exit(execvp(c, nargv));
975 signal(SIGINT, SIG_IGN);
978 // Pass all log messages from the umbilical to stderr.
979 // A nul-byte right before closure means tincd started successfully.
984 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
985 failure = buf[len - 1];
1000 // Make sure the child process is really gone.
1002 pid_t result = waitpid(pid, &status, 0);
1005 signal(SIGINT, SIG_DFL);
1008 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1011 fprintf(stderr, "Error starting %s\n", c);
1014 if(c != default_c) {
1018 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1022 static int cmd_stop(int argc, char *argv[]) {
1026 fprintf(stderr, "Too many arguments!\n");
1031 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1036 if(kill(pid, SIGTERM)) {
1037 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1041 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1042 waitpid(pid, NULL, 0);
1053 static int cmd_restart(int argc, char *argv[]) {
1055 return cmd_start(argc, argv);
1058 static int cmd_reload(int argc, char *argv[]) {
1062 fprintf(stderr, "Too many arguments!\n");
1066 if(!connect_tincd(true)) {
1070 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1072 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1073 fprintf(stderr, "Could not reload configuration.\n");
1081 static int dump_invitations(void) {
1082 char dname[PATH_MAX];
1083 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1084 DIR *dir = opendir(dname);
1087 if(errno == ENOENT) {
1088 fprintf(stderr, "No outstanding invitations.\n");
1092 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1100 while((ent = readdir(dir))) {
1101 char buf[MAX_STRING_SIZE];
1103 if(b64decode(ent->d_name, buf, 24) != 18) {
1107 char fname[PATH_MAX];
1109 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1110 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1114 FILE *f = fopen(fname, "r");
1117 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1123 if(!fgets(buf, sizeof(buf), f)) {
1124 fprintf(stderr, "Invalid invitation file %s\n", fname);
1131 char *eol = buf + strlen(buf);
1133 while(strchr("\t \r\n", *--eol)) {
1137 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1138 fprintf(stderr, "Invalid invitation file %s\n", fname);
1143 printf("%s %s\n", ent->d_name, buf + 7);
1149 fprintf(stderr, "No outstanding invitations.\n");
1155 static int cmd_dump(int argc, char *argv[]) {
1156 bool only_reachable = false;
1158 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1159 if(strcasecmp(argv[2], "nodes")) {
1160 fprintf(stderr, "`reachable' only supported for nodes.\n");
1165 only_reachable = true;
1171 fprintf(stderr, "Invalid number of arguments.\n");
1176 if(!strcasecmp(argv[1], "invitations")) {
1177 return dump_invitations();
1180 if(!connect_tincd(true)) {
1186 if(!strcasecmp(argv[1], "nodes")) {
1187 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1188 } else if(!strcasecmp(argv[1], "edges")) {
1189 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1190 } else if(!strcasecmp(argv[1], "subnets")) {
1191 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1192 } else if(!strcasecmp(argv[1], "connections")) {
1193 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1194 } else if(!strcasecmp(argv[1], "graph")) {
1195 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1196 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1198 } else if(!strcasecmp(argv[1], "digraph")) {
1199 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1200 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1203 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1209 printf("graph {\n");
1210 } else if(do_graph == 2) {
1211 printf("digraph {\n");
1214 while(recvline(fd, line, sizeof(line))) {
1215 char node1[4096], node2[4096];
1216 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1219 if(do_graph && req == REQ_DUMP_NODES) {
1241 char local_host[4096];
1242 char local_port[4096];
1245 int cipher, digest, maclength, compression, distance, socket, weight;
1246 short int pmtu, minmtu, maxmtu;
1247 unsigned int options, status_int;
1248 node_status_t status;
1249 long int last_state_change;
1251 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1254 case REQ_DUMP_NODES: {
1255 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %d %d %d %d %x %x %4095s %4095s %d %hd %hd %hd %ld %d %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change, &udp_ping_rtt, &in_packets, &in_bytes, &out_packets, &out_bytes);
1258 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1262 memcpy(&status, &status_int, sizeof(status));
1265 const char *color = "black";
1267 if(!strcmp(host, "MYSELF")) {
1269 } else if(!status.reachable) {
1271 } else if(strcmp(via, node)) {
1273 } else if(!status.validkey) {
1275 } else if(minmtu > 0) {
1279 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1281 if(only_reachable && !status.reachable) {
1285 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,
1286 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu, in_packets, in_bytes, out_packets, out_bytes);
1288 if(udp_ping_rtt != -1) {
1289 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1297 case REQ_DUMP_EDGES: {
1298 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);
1301 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1306 float w = 1.0f + 65536.0f / (float)weight;
1308 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1309 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1310 } else if(do_graph == 2) {
1311 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1314 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);
1319 case REQ_DUMP_SUBNETS: {
1320 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1323 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1327 printf("%s owner %s\n", strip_weight(subnet), node);
1331 case REQ_DUMP_CONNECTIONS: {
1332 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1335 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1339 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1344 fprintf(stderr, "Unable to parse dump from tincd.\n");
1349 fprintf(stderr, "Error receiving dump.\n");
1353 static int cmd_purge(int argc, char *argv[]) {
1357 fprintf(stderr, "Too many arguments!\n");
1361 if(!connect_tincd(true)) {
1365 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1367 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1368 fprintf(stderr, "Could not purge old information.\n");
1375 static int cmd_debug(int argc, char *argv[]) {
1377 fprintf(stderr, "Invalid number of arguments.\n");
1381 if(!connect_tincd(true)) {
1385 int debuglevel = atoi(argv[1]);
1388 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1390 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1391 fprintf(stderr, "Could not set debug level.\n");
1395 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1399 static int cmd_retry(int argc, char *argv[]) {
1403 fprintf(stderr, "Too many arguments!\n");
1407 if(!connect_tincd(true)) {
1411 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1413 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1414 fprintf(stderr, "Could not retry outgoing connections.\n");
1421 static int cmd_connect(int argc, char *argv[]) {
1423 fprintf(stderr, "Invalid number of arguments.\n");
1427 if(!check_id(argv[1])) {
1428 fprintf(stderr, "Invalid name for node.\n");
1432 if(!connect_tincd(true)) {
1436 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1438 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1439 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1446 static int cmd_disconnect(int argc, char *argv[]) {
1448 fprintf(stderr, "Invalid number of arguments.\n");
1452 if(!check_id(argv[1])) {
1453 fprintf(stderr, "Invalid name for node.\n");
1457 if(!connect_tincd(true)) {
1461 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1463 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1464 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1471 static int cmd_top(int argc, char *argv[]) {
1475 fprintf(stderr, "Too many arguments!\n");
1481 if(!connect_tincd(true)) {
1488 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1493 static int cmd_pcap(int argc, char *argv[]) {
1495 fprintf(stderr, "Too many arguments!\n");
1499 if(!connect_tincd(true)) {
1503 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1508 static void sigint_handler(int sig) {
1511 fprintf(stderr, "\n");
1512 shutdown(fd, SHUT_RDWR);
1516 static int cmd_log(int argc, char *argv[]) {
1518 fprintf(stderr, "Too many arguments!\n");
1522 if(!connect_tincd(true)) {
1527 signal(SIGINT, sigint_handler);
1530 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1533 signal(SIGINT, SIG_DFL);
1541 static int cmd_pid(int argc, char *argv[]) {
1545 fprintf(stderr, "Too many arguments!\n");
1549 if(!connect_tincd(true) || !pid) {
1553 printf("%d\n", pid);
1557 size_t rstrip(char *value) {
1558 size_t len = strlen(value);
1560 while(len && strchr("\t\r\n ", value[len - 1])) {
1567 char *get_my_name(bool verbose) {
1568 FILE *f = fopen(tinc_conf, "r");
1572 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1581 while(fgets(buf, sizeof(buf), f)) {
1582 size_t len = strcspn(buf, "\t =");
1584 value += strspn(value, "\t ");
1588 value += strspn(value, "\t ");
1591 if(!rstrip(value)) {
1597 if(strcasecmp(buf, "Name")) {
1603 return replace_name(value);
1610 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1616 ecdsa_t *get_pubkey(FILE *f) {
1620 while(fgets(buf, sizeof(buf), f)) {
1621 size_t len = strcspn(buf, "\t =");
1623 value += strspn(value, "\t ");
1627 value += strspn(value, "\t ");
1630 if(!rstrip(value)) {
1636 if(strcasecmp(buf, "Ed25519PublicKey")) {
1641 return ecdsa_set_base64_public_key(value);
1648 const var_t variables[] = {
1649 /* Server configuration */
1650 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1651 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1652 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1653 {"BindToInterface", VAR_SERVER},
1654 {"Broadcast", VAR_SERVER | VAR_SAFE},
1655 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1656 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1657 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1658 {"Device", VAR_SERVER},
1659 {"DeviceStandby", VAR_SERVER},
1660 {"DeviceType", VAR_SERVER},
1661 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1662 {"Ed25519PrivateKeyFile", VAR_SERVER},
1663 {"ExperimentalProtocol", VAR_SERVER},
1664 {"Forwarding", VAR_SERVER},
1665 {"FWMark", VAR_SERVER},
1666 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1667 {"Hostnames", VAR_SERVER},
1668 {"IffOneQueue", VAR_SERVER},
1669 {"Interface", VAR_SERVER},
1670 {"InvitationExpire", VAR_SERVER},
1671 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1672 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1673 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1674 {"LogLevel", VAR_SERVER},
1675 {"MACExpire", VAR_SERVER | VAR_SAFE},
1676 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1677 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1678 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1679 {"Mode", VAR_SERVER | VAR_SAFE},
1680 {"Name", VAR_SERVER},
1681 {"PingInterval", VAR_SERVER | VAR_SAFE},
1682 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1683 {"PriorityInheritance", VAR_SERVER},
1684 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1685 {"PrivateKeyFile", VAR_SERVER},
1686 {"ProcessPriority", VAR_SERVER},
1687 {"Proxy", VAR_SERVER},
1688 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1689 {"ScriptsExtension", VAR_SERVER},
1690 {"ScriptsInterpreter", VAR_SERVER},
1691 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1692 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1693 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1694 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1695 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1696 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1697 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1698 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1699 {"UDPRcvBuf", VAR_SERVER},
1700 {"UDPSndBuf", VAR_SERVER},
1701 {"UPnP", VAR_SERVER},
1702 {"UPnPDiscoverWait", VAR_SERVER},
1703 {"UPnPRefreshPeriod", VAR_SERVER},
1704 {"VDEGroup", VAR_SERVER},
1705 {"VDEPort", VAR_SERVER},
1706 /* Host configuration */
1707 {"Address", VAR_HOST | VAR_MULTIPLE},
1708 {"Cipher", VAR_SERVER | VAR_HOST},
1709 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1710 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1711 {"Digest", VAR_SERVER | VAR_HOST},
1712 {"Ed25519PublicKey", VAR_HOST},
1713 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1714 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1715 {"MACLength", VAR_SERVER | VAR_HOST},
1716 {"PMTU", VAR_SERVER | VAR_HOST},
1717 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1719 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1720 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1721 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1722 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1723 {"Weight", VAR_HOST | VAR_SAFE},
1727 static int cmd_config(int argc, char *argv[]) {
1729 fprintf(stderr, "Invalid number of arguments.\n");
1733 if(strcasecmp(argv[0], "config")) {
1739 if(!strcasecmp(argv[1], "get")) {
1741 } else if(!strcasecmp(argv[1], "add")) {
1742 argv++, argc--, action = 1;
1743 } else if(!strcasecmp(argv[1], "del")) {
1744 argv++, argc--, action = -1;
1745 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1746 argv++, argc--, action = 0;
1750 fprintf(stderr, "Invalid number of arguments.\n");
1754 // Concatenate the rest of the command line
1755 strncpy(line, argv[1], sizeof(line) - 1);
1757 for(int i = 2; i < argc; i++) {
1758 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1759 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1762 // Liberal parsing into node name, variable name and value.
1768 len = strcspn(line, "\t =");
1770 value += strspn(value, "\t ");
1774 value += strspn(value, "\t ");
1778 variable = strchr(line, '.');
1788 fprintf(stderr, "No variable given.\n");
1792 if(action >= 0 && !*value) {
1793 fprintf(stderr, "No value for variable given.\n");
1797 if(action < -1 && *value) {
1801 /* Some simple checks. */
1803 bool warnonremove = false;
1805 for(int i = 0; variables[i].name; i++) {
1806 if(strcasecmp(variables[i].name, variable)) {
1811 variable = (char *)variables[i].name;
1813 if(!strcasecmp(variable, "Subnet") && *value) {
1816 if(!str2net(&s, value)) {
1817 fprintf(stderr, "Malformed subnet definition %s\n", value);
1821 if(!subnetcheck(s)) {
1822 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1827 /* Discourage use of obsolete variables. */
1829 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1831 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1833 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1838 /* Don't put server variables in host config files */
1840 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1842 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1844 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1849 /* Should this go into our own host config file? */
1851 if(!node && !(variables[i].type & VAR_SERVER)) {
1852 node = get_my_name(true);
1859 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1860 Turn on warnings when it seems variables might be removed unintentionally. */
1862 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1863 warnonremove = true;
1865 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1866 warnonremove = true;
1872 if(node && !check_id(node)) {
1873 fprintf(stderr, "Invalid name for node.\n");
1883 if(force || action < 0) {
1884 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1886 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1888 if(node && node != line) {
1896 // Open the right configuration file.
1897 char filename[PATH_MAX];
1900 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1907 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1910 FILE *f = fopen(filename, "r");
1913 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1917 char tmpfile[PATH_MAX];
1921 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1922 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1926 tf = fopen(tmpfile, "w");
1929 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1935 // Copy the file, making modifications on the fly, unless we are just getting a value.
1939 bool removed = false;
1942 while(fgets(buf1, sizeof(buf1), f)) {
1943 buf1[sizeof(buf1) - 1] = 0;
1944 strncpy(buf2, buf1, sizeof(buf2));
1946 // Parse line in a simple way
1949 size_t len = strcspn(buf2, "\t =");
1950 bvalue = buf2 + len;
1951 bvalue += strspn(bvalue, "\t ");
1953 if(*bvalue == '=') {
1955 bvalue += strspn(bvalue, "\t ");
1962 if(!strcasecmp(buf2, variable)) {
1966 printf("%s\n", bvalue);
1968 } else if(action == -1) {
1969 if(!*value || !strcasecmp(bvalue, value)) {
1975 } else if(action == 0) {
1976 // Warn if "set" was used for variables that can occur multiple times
1977 if(warnonremove && strcasecmp(bvalue, value)) {
1978 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1981 // Already set? Delete the rest...
1986 // Otherwise, replace.
1987 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1988 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1995 } else if(action > 0) {
1996 // Check if we've already seen this variable with the same value
1997 if(!strcasecmp(bvalue, value)) {
2004 // Copy original line...
2005 if(fputs(buf1, tf) < 0) {
2006 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2010 // Add newline if it is missing...
2011 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2012 if(fputc('\n', tf) < 0) {
2013 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2020 // Make sure we read everything...
2021 if(ferror(f) || !feof(f)) {
2022 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2027 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2031 // Add new variable if necessary.
2032 if((action > 0 && !found) || (action == 0 && !set)) {
2033 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2034 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2043 fprintf(stderr, "No matching configuration variables found.\n");
2048 // Make sure we wrote everything...
2050 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2054 // Could we find what we had to remove?
2055 if(action < 0 && !removed) {
2057 fprintf(stderr, "No configuration variables deleted.\n");
2061 // Replace the configuration file with the new one
2064 if(remove(filename)) {
2065 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2071 if(rename(tmpfile, filename)) {
2072 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2076 // Silently try notifying a running tincd of changes.
2077 if(connect_tincd(false)) {
2078 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2084 static bool try_bind(int port) {
2085 struct addrinfo *ai = NULL, *aip;
2086 struct addrinfo hint = {
2087 .ai_flags = AI_PASSIVE,
2088 .ai_family = AF_UNSPEC,
2089 .ai_socktype = SOCK_STREAM,
2090 .ai_protocol = IPPROTO_TCP,
2093 bool success = true;
2095 snprintf(portstr, sizeof(portstr), "%d", port);
2097 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2101 for(aip = ai; aip; aip = aip->ai_next) {
2102 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2109 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2122 int check_port(const char *name) {
2127 fprintf(stderr, "Warning: could not bind to port 655. ");
2129 for(int i = 0; i < 100; i++) {
2130 int port = 0x1000 + (rand() & 0x7fff);
2132 if(try_bind(port)) {
2133 char filename[PATH_MAX];
2134 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2135 FILE *f = fopen(filename, "a");
2138 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2139 fprintf(stderr, "Please change tinc's Port manually.\n");
2143 fprintf(f, "Port = %d\n", port);
2145 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2150 fprintf(stderr, "Please change tinc's Port manually.\n");
2154 static int cmd_init(int argc, char *argv[]) {
2155 if(!access(tinc_conf, F_OK)) {
2156 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2161 fprintf(stderr, "Too many arguments!\n");
2163 } else if(argc < 2) {
2166 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2168 if(!fgets(buf, sizeof(buf), stdin)) {
2169 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2173 size_t len = rstrip(buf);
2176 fprintf(stderr, "No name given!\n");
2182 fprintf(stderr, "No Name given!\n");
2186 name = strdup(argv[1]);
2189 fprintf(stderr, "No Name given!\n");
2194 if(!check_id(name)) {
2195 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2199 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2200 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2204 if(mkdir(confbase, 0777) && errno != EEXIST) {
2205 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2209 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2210 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2214 FILE *f = fopen(tinc_conf, "w");
2217 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2221 fprintf(f, "Name = %s\n", name);
2224 #ifndef DISABLE_LEGACY
2226 if(!rsa_keygen(2048, false)) {
2232 if(!ed25519_keygen(false)) {
2239 char filename[PATH_MAX];
2240 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2242 if(access(filename, F_OK)) {
2243 FILE *f = fopenmask(filename, "w", 0777);
2246 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2250 fprintf(f, "#!/bin/sh\n\necho 'Unconfigured tinc-up script, please edit '$0'!'\n\n#ifconfig $INTERFACE <your vpn IP address> netmask <netmask of whole VPN>\n");
2260 static int cmd_generate_keys(int argc, char *argv[]) {
2261 #ifdef DISABLE_LEGACY
2269 fprintf(stderr, "Too many arguments!\n");
2274 name = get_my_name(false);
2277 #ifndef DISABLE_LEGACY
2279 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2285 if(!ed25519_keygen(true)) {
2292 #ifndef DISABLE_LEGACY
2293 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2295 fprintf(stderr, "Too many arguments!\n");
2300 name = get_my_name(false);
2303 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2307 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2311 fprintf(stderr, "Too many arguments!\n");
2316 name = get_my_name(false);
2319 return !ed25519_keygen(true);
2322 static int cmd_help(int argc, char *argv[]) {
2330 static int cmd_version(int argc, char *argv[]) {
2334 fprintf(stderr, "Too many arguments!\n");
2342 static int cmd_info(int argc, char *argv[]) {
2344 fprintf(stderr, "Invalid number of arguments.\n");
2348 if(!connect_tincd(true)) {
2352 return info(fd, argv[1]);
2355 static const char *conffiles[] = {
2366 static int cmd_edit(int argc, char *argv[]) {
2368 fprintf(stderr, "Invalid number of arguments.\n");
2372 char filename[PATH_MAX] = "";
2374 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2375 for(int i = 0; conffiles[i]; i++) {
2376 if(!strcmp(argv[1], conffiles[i])) {
2377 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2386 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2387 char *dash = strchr(argv[1], '-');
2392 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2393 fprintf(stderr, "Invalid configuration filename.\n");
2401 const char *editor = getenv("VISUAL");
2404 editor = getenv("EDITOR");
2411 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2413 xasprintf(&command, "edit \"%s\"", filename);
2415 int result = system(command);
2422 // Silently try notifying a running tincd of changes.
2423 if(connect_tincd(false)) {
2424 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2430 static int export(const char *name, FILE *out) {
2431 char filename[PATH_MAX];
2432 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2433 FILE *in = fopen(filename, "r");
2436 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2440 fprintf(out, "Name = %s\n", name);
2443 while(fgets(buf, sizeof(buf), in)) {
2444 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2450 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2459 static int cmd_export(int argc, char *argv[]) {
2463 fprintf(stderr, "Too many arguments!\n");
2467 char *name = get_my_name(true);
2473 int result = export(name, stdout);
2483 static int cmd_export_all(int argc, char *argv[]) {
2487 fprintf(stderr, "Too many arguments!\n");
2491 DIR *dir = opendir(hosts_dir);
2494 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2502 while((ent = readdir(dir))) {
2503 if(!check_id(ent->d_name)) {
2510 printf("#---------------------------------------------------------------#\n");
2513 result |= export(ent->d_name, stdout);
2525 static int cmd_import(int argc, char *argv[]) {
2529 fprintf(stderr, "Too many arguments!\n");
2538 char filename[PATH_MAX] = "";
2540 bool firstline = true;
2542 while(fgets(buf, sizeof(buf), in)) {
2543 if(sscanf(buf, "Name = %4095s", name) == 1) {
2546 if(!check_id(name)) {
2547 fprintf(stderr, "Invalid Name in input!\n");
2555 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2556 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2560 if(!force && !access(filename, F_OK)) {
2561 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2566 out = fopen(filename, "w");
2569 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2575 } else if(firstline) {
2576 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2581 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2586 if(fputs(buf, out) < 0) {
2587 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2598 fprintf(stderr, "Imported %d host configuration files.\n", count);
2601 fprintf(stderr, "No host configuration files imported.\n");
2606 static int cmd_exchange(int argc, char *argv[]) {
2607 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2610 static int cmd_exchange_all(int argc, char *argv[]) {
2611 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2614 static int switch_network(char *name) {
2615 if(strcmp(name, ".")) {
2616 if(!check_netname(name, false)) {
2617 fprintf(stderr, "Invalid character in netname!\n");
2621 if(!check_netname(name, true)) {
2622 fprintf(stderr, "Warning: unsafe character in netname!\n");
2632 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2639 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2640 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2641 xasprintf(&prompt, "%s> ", identname);
2646 static int cmd_network(int argc, char *argv[]) {
2648 fprintf(stderr, "Too many arguments!\n");
2653 return switch_network(argv[1]);
2656 DIR *dir = opendir(confdir);
2659 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2665 while((ent = readdir(dir))) {
2666 if(*ent->d_name == '.') {
2670 if(!strcmp(ent->d_name, "tinc.conf")) {
2675 char fname[PATH_MAX];
2676 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2678 if(!access(fname, R_OK)) {
2679 printf("%s\n", ent->d_name);
2688 static int cmd_fsck(int argc, char *argv[]) {
2692 fprintf(stderr, "Too many arguments!\n");
2696 return fsck(orig_argv[0]);
2699 static void *readfile(FILE *in, size_t *len) {
2701 size_t bufsize = 4096;
2702 char *buf = xmalloc(bufsize);
2705 size_t read = fread(buf + count, 1, bufsize - count, in);
2713 if(count >= bufsize) {
2715 buf = xrealloc(buf, bufsize);
2726 static int cmd_sign(int argc, char *argv[]) {
2728 fprintf(stderr, "Too many arguments!\n");
2733 name = get_my_name(true);
2740 char fname[PATH_MAX];
2741 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2742 FILE *fp = fopen(fname, "r");
2745 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2749 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2752 fprintf(stderr, "Could not read private key from %s\n", fname);
2762 in = fopen(argv[1], "rb");
2765 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2774 char *data = readfile(in, &len);
2781 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2786 // Ensure we sign our name and current time as well
2787 long t = time(NULL);
2789 xasprintf(&trailer, " %s %ld", name, t);
2790 size_t trailer_len = strlen(trailer);
2792 data = xrealloc(data, len + trailer_len);
2793 memcpy(data + len, trailer, trailer_len);
2798 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2799 fprintf(stderr, "Error generating signature\n");
2805 b64encode(sig, sig, 64);
2808 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2809 fwrite(data, len, 1, stdout);
2815 static int cmd_verify(int argc, char *argv[]) {
2817 fprintf(stderr, "Not enough arguments!\n");
2822 fprintf(stderr, "Too many arguments!\n");
2826 char *node = argv[1];
2828 if(!strcmp(node, ".")) {
2830 name = get_my_name(true);
2838 } else if(!strcmp(node, "*")) {
2841 if(!check_id(node)) {
2842 fprintf(stderr, "Invalid node name\n");
2850 in = fopen(argv[2], "rb");
2853 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2861 char *data = readfile(in, &len);
2868 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2872 char *newline = memchr(data, '\n', len);
2874 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2875 fprintf(stderr, "Invalid input\n");
2881 size_t skip = newline - data;
2883 char signer[MAX_STRING_SIZE] = "";
2884 char sig[MAX_STRING_SIZE] = "";
2887 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2888 fprintf(stderr, "Invalid input\n");
2893 if(node && strcmp(node, signer)) {
2894 fprintf(stderr, "Signature is not made by %s\n", node);
2904 xasprintf(&trailer, " %s %ld", signer, t);
2905 size_t trailer_len = strlen(trailer);
2907 data = xrealloc(data, len + trailer_len);
2908 memcpy(data + len, trailer, trailer_len);
2911 newline = data + skip;
2913 char fname[PATH_MAX];
2914 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2915 FILE *fp = fopen(fname, "r");
2918 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2923 ecdsa_t *key = get_pubkey(fp);
2927 key = ecdsa_read_pem_public_key(fp);
2931 fprintf(stderr, "Could not read public key from %s\n", fname);
2939 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2940 fprintf(stderr, "Invalid signature\n");
2948 fwrite(newline, len - (newline - data), 1, stdout);
2954 static const struct {
2955 const char *command;
2956 int (*function)(int argc, char *argv[]);
2959 {"start", cmd_start, false},
2960 {"stop", cmd_stop, false},
2961 {"restart", cmd_restart, false},
2962 {"reload", cmd_reload, false},
2963 {"dump", cmd_dump, false},
2964 {"list", cmd_dump, false},
2965 {"purge", cmd_purge, false},
2966 {"debug", cmd_debug, false},
2967 {"retry", cmd_retry, false},
2968 {"connect", cmd_connect, false},
2969 {"disconnect", cmd_disconnect, false},
2970 {"top", cmd_top, false},
2971 {"pcap", cmd_pcap, false},
2972 {"log", cmd_log, false},
2973 {"pid", cmd_pid, false},
2974 {"config", cmd_config, true},
2975 {"add", cmd_config, false},
2976 {"del", cmd_config, false},
2977 {"get", cmd_config, false},
2978 {"set", cmd_config, false},
2979 {"init", cmd_init, false},
2980 {"generate-keys", cmd_generate_keys, false},
2981 #ifndef DISABLE_LEGACY
2982 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
2984 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
2985 {"help", cmd_help, false},
2986 {"version", cmd_version, false},
2987 {"info", cmd_info, false},
2988 {"edit", cmd_edit, false},
2989 {"export", cmd_export, false},
2990 {"export-all", cmd_export_all, false},
2991 {"import", cmd_import, false},
2992 {"exchange", cmd_exchange, false},
2993 {"exchange-all", cmd_exchange_all, false},
2994 {"invite", cmd_invite, false},
2995 {"join", cmd_join, false},
2996 {"network", cmd_network, false},
2997 {"fsck", cmd_fsck, false},
2998 {"sign", cmd_sign, false},
2999 {"verify", cmd_verify, false},
3000 {NULL, NULL, false},
3003 #ifdef HAVE_READLINE
3004 static char *complete_command(const char *text, int state) {
3013 while(commands[i].command) {
3014 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3015 return xstrdup(commands[i].command);
3024 static char *complete_dump(const char *text, int state) {
3025 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3035 if(!strncasecmp(matches[i], text, strlen(text))) {
3036 return xstrdup(matches[i]);
3045 static char *complete_config(const char *text, int state) {
3054 while(variables[i].name) {
3055 char *dot = strchr(text, '.');
3058 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3060 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3064 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3065 return xstrdup(variables[i].name);
3075 static char *complete_info(const char *text, int state) {
3081 if(!connect_tincd(false)) {
3085 // Check the list of nodes
3086 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3087 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3090 while(recvline(fd, line, sizeof(line))) {
3092 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3105 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3109 if(!strncmp(item, text, strlen(text))) {
3110 return xstrdup(strip_weight(item));
3117 static char *complete_nothing(const char *text, int state) {
3123 static char **completion(const char *text, int start, int end) {
3125 char **matches = NULL;
3128 matches = rl_completion_matches(text, complete_command);
3129 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3130 matches = rl_completion_matches(text, complete_dump);
3131 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3132 matches = rl_completion_matches(text, complete_config);
3133 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3134 matches = rl_completion_matches(text, complete_config);
3135 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3136 matches = rl_completion_matches(text, complete_config);
3137 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3138 matches = rl_completion_matches(text, complete_config);
3139 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3140 matches = rl_completion_matches(text, complete_info);
3147 static int cmd_shell(int argc, char *argv[]) {
3148 xasprintf(&prompt, "%s> ", identname);
3152 int maxargs = argc + 16;
3153 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3155 for(int i = 0; i < argc; i++) {
3159 #ifdef HAVE_READLINE
3160 rl_readline_name = "tinc";
3161 rl_completion_entry_function = complete_nothing;
3162 rl_attempted_completion_function = completion;
3163 rl_filename_completion_desired = 0;
3168 #ifdef HAVE_READLINE
3173 rl_basic_word_break_characters = "\t\n ";
3174 line = readline(prompt);
3175 copy = line ? xstrdup(line) : NULL;
3177 line = fgets(buf, sizeof(buf), stdin);
3183 fputs(prompt, stdout);
3186 line = fgets(buf, sizeof(buf), stdin);
3193 /* Ignore comments */
3202 char *p = line + strspn(line, " \t\n");
3203 char *next = strtok(p, " \t\n");
3206 if(nargc >= maxargs) {
3208 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3213 next = strtok(NULL, " \t\n");
3220 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3221 #ifdef HAVE_READLINE
3230 for(int i = 0; commands[i].command; i++) {
3231 if(!strcasecmp(nargv[argc], commands[i].command)) {
3232 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3238 #ifdef HAVE_READLINE
3247 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3252 #ifdef HAVE_READLINE
3264 static void cleanup() {
3270 int main(int argc, char *argv[]) {
3271 program_name = argv[0];
3273 tty = isatty(0) && isatty(1);
3275 if(!parse_options(argc, argv)) {
3280 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3281 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3295 static struct WSAData wsa_state;
3297 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3298 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3304 gettimeofday(&now, NULL);
3305 srand(now.tv_sec + now.tv_usec);
3308 if(optind >= argc) {
3309 return cmd_shell(argc, argv);
3312 for(int i = 0; commands[i].command; i++) {
3313 if(!strcasecmp(argv[optind], commands[i].command)) {
3314 return commands[i].function(argc - optind, argv + optind);
3318 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);