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"
50 #define MSG_NOSIGNAL 0
53 static char **orig_argv;
55 /* If nonzero, display usage information and exit. */
56 static bool show_help = false;
58 /* If nonzero, print the version on standard output and exit. */
59 static bool show_version = false;
61 static char *name = NULL;
62 static char controlcookie[1025];
63 char *tinc_conf = NULL;
64 char *hosts_dir = NULL;
67 // Horrible global variables...
76 bool confbasegiven = false;
77 char *scriptinterpreter = NULL;
78 static char defaultextension[] = "";
79 char *scriptextension = defaultextension;
85 typedef enum option_t {
91 OPT_CONFIG_FILE = 'c',
101 static struct option const long_options[] = {
102 {"batch", no_argument, NULL, OPT_BATCH},
103 {"config", required_argument, NULL, OPT_CONFIG_FILE},
104 {"net", required_argument, NULL, OPT_NETNAME},
105 {"help", no_argument, NULL, OPT_HELP},
106 {"version", no_argument, NULL, OPT_VERSION},
107 {"pidfile", required_argument, NULL, OPT_PIDFILE},
108 {"force", no_argument, NULL, OPT_FORCE},
112 static void version(void) {
114 "%s version %s (built %s %s, protocol %d.%d)\n"
122 #ifndef DISABLE_LEGACY
129 "Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
130 "See the AUTHORS file for a complete list.\n"
132 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
133 "and you are welcome to redistribute it under certain conditions;\n"
134 "see the file COPYING for details.\n",
135 PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
138 static void usage(bool status) {
140 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
143 "Usage: %s [options] command\n"
145 "Valid options are:\n"
146 " -b, --batch Don't ask for anything (non-interactive mode).\n"
147 " -c, --config=DIR Read configuration options from DIR.\n"
148 " -n, --net=NETNAME Connect to net NETNAME.\n"
149 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
150 " --force Force some commands to work despite warnings.\n"
151 " --help Display this help and exit.\n"
152 " --version Output version information and exit.\n"
154 "Valid commands are:\n"
155 " init [name] Create initial configuration files.\n"
156 " get VARIABLE Print current value of VARIABLE\n"
157 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
158 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
159 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
160 " start [tincd options] Start tincd.\n"
161 " stop Stop tincd.\n"
162 " restart [tincd options] Restart tincd.\n"
163 " reload Partially reload configuration of running tincd.\n"
164 " pid Show PID of currently running tincd.\n"
165 #ifdef DISABLE_LEGACY
166 " generate-keys Generate a new Ed25519 public/private key pair.\n"
168 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
169 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
171 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
172 " dump Dump a list of one of the following things:\n"
173 " [reachable] nodes - all known nodes in the VPN\n"
174 " edges - all known connections in the VPN\n"
175 " subnets - all known subnets in the VPN\n"
176 " connections - all meta connections with ourself\n"
177 " [di]graph - graph of the VPN in dotty format\n"
178 " invitations - outstanding invitations\n"
179 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
180 " purge Purge unreachable nodes\n"
181 " debug N Set debug level\n"
182 " retry Retry all outgoing connections\n"
183 " disconnect NODE Close meta connection with NODE\n"
185 " top Show real-time statistics\n"
187 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
188 " log [level] Dump log output [up to the specified level]\n"
189 " export Export host configuration of local node to standard output\n"
190 " export-all Export all host configuration files to standard output\n"
191 " import Import host configuration file(s) from standard input\n"
192 " exchange Same as export followed by import\n"
193 " exchange-all Same as export-all followed by import\n"
194 " invite NODE [...] Generate an invitation for NODE\n"
195 " join INVITATION Join a VPN using an INVITATION\n"
196 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
197 " fsck Check the configuration files for problems.\n"
198 " sign [FILE] Generate a signed version of a file.\n"
199 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
201 "Report bugs to tinc@tinc-vpn.org.\n",
206 static bool parse_options(int argc, char **argv) {
208 int option_index = 0;
210 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
211 switch((option_t) r) {
212 case OPT_LONG_OPTION:
224 case OPT_CONFIG_FILE:
226 confbase = xstrdup(optarg);
227 confbasegiven = true;
232 netname = xstrdup(optarg);
245 pidfilename = xstrdup(optarg);
257 if(!netname && (netname = getenv("NETNAME"))) {
258 netname = xstrdup(netname);
261 /* netname "." is special: a "top-level name" */
263 if(netname && (!*netname || !strcmp(netname, "."))) {
268 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
269 fprintf(stderr, "Invalid character in netname!\n");
277 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
279 char directory[PATH_MAX] = ".";
285 /* Check stdin and stdout */
287 /* Ask for a file and/or directory name. */
288 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
290 if(fgets(buf, sizeof(buf), stdin) == NULL) {
291 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
295 size_t len = strlen(buf);
308 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
311 if(filename[0] != '/') {
314 /* The directory is a relative path or a filename. */
315 if(!getcwd(directory, sizeof(directory))) {
316 fprintf(stderr, "Could not get current directory: %s\n", strerror(errno));
320 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
321 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
333 disable_old_keys(filename, what);
335 /* Open it first to keep the inode busy */
337 r = fopenmask(filename, mode, perms);
340 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
348 Generate a public/private Ed25519 key pair, and ask for a file to store
351 static bool ed25519_keygen(bool ask) {
354 char fname[PATH_MAX];
356 fprintf(stderr, "Generating Ed25519 key pair:\n");
358 if(!(key = ecdsa_generate())) {
359 fprintf(stderr, "Error during key generation!\n");
362 fprintf(stderr, "Done.\n");
365 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
366 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
372 if(!ecdsa_write_pem_private_key(key, f)) {
373 fprintf(stderr, "Error writing private key!\n");
380 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
382 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
385 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
391 char *pubkey = ecdsa_get_base64_public_key(key);
392 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
410 #ifndef DISABLE_LEGACY
412 Generate a public/private RSA key pair, and ask for a file to store
415 static bool rsa_keygen(int bits, bool ask) {
418 char fname[PATH_MAX];
420 // Make sure the key size is a multiple of 8 bits.
423 // Make sure that a valid key size is used.
424 if(bits < 1024 || bits > 8192) {
425 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
427 } else if(bits < 2048) {
428 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
431 fprintf(stderr, "Generating %d bits keys:\n", bits);
433 if(!(key = rsa_generate(bits, 0x10001))) {
434 fprintf(stderr, "Error during key generation!\n");
437 fprintf(stderr, "Done.\n");
440 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
441 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
447 if(!rsa_write_pem_private_key(key, f)) {
448 fprintf(stderr, "Error writing private key!\n");
455 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
457 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
460 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
466 if(!rsa_write_pem_public_key(key, f)) {
467 fprintf(stderr, "Error writing public key!\n");
490 bool recvline(int fd, char *line, size_t len) {
491 char *newline = NULL;
497 while(!(newline = memchr(buffer, '\n', blen))) {
498 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
500 if(nrecv == -1 && sockerrno == EINTR) {
502 } else if(nrecv <= 0) {
509 if((size_t)(newline - buffer) >= len) {
513 len = newline - buffer;
515 memcpy(line, buffer, len);
517 memmove(buffer, newline + 1, blen - len - 1);
523 static bool recvdata(int fd, char *data, size_t len) {
525 ssize_t nrecv = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
527 if(nrecv == -1 && sockerrno == EINTR) {
529 } else if(nrecv <= 0) {
536 memcpy(data, buffer, len);
537 memmove(buffer, buffer + len, blen - len);
543 bool sendline(int fd, const char *format, ...) {
544 static char buffer[4096];
549 va_start(ap, format);
550 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
551 buffer[sizeof(buffer) - 1] = 0;
554 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
562 ssize_t nsend = send(fd, p, blen, MSG_NOSIGNAL);
564 if(nsend == -1 && sockerrno == EINTR) {
566 } else if(nsend <= 0) {
577 static void pcap(int fd, FILE *out, uint32_t snaplen) {
578 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
586 uint32_t tz_accuracy;
593 snaplen ? snaplen : sizeof(data),
606 fwrite(&header, sizeof(header), 1, out);
611 while(recvline(fd, line, sizeof(line))) {
614 int n = sscanf(line, "%d %d %lu", &code, &req, &len);
615 gettimeofday(&tv, NULL);
617 if(n != 3 || code != CONTROL || req != REQ_PCAP || len > sizeof(data)) {
621 if(!recvdata(fd, data, len)) {
625 packet.tv_sec = tv.tv_sec;
626 packet.tv_usec = tv.tv_usec;
628 packet.origlen = len;
629 fwrite(&packet, sizeof(packet), 1, out);
630 fwrite(data, len, 1, out);
635 static void log_control(int fd, FILE *out, int level, bool use_color) {
636 sendline(fd, "%d %d %d %d", CONTROL, REQ_LOG, level, use_color);
641 while(recvline(fd, line, sizeof(line))) {
643 int n = sscanf(line, "%d %d %d", &code, &req, &len);
645 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
649 if(!recvdata(fd, data, len)) {
653 fwrite(data, len, 1, out);
659 static bool stop_tincd(void) {
660 if(!connect_tincd(true)) {
664 sendline(fd, "%d %d", CONTROL, REQ_STOP);
666 while(recvline(fd, line, sizeof(line))) {
667 // wait for tincd to close the connection...
678 static bool remove_service(void) {
679 SC_HANDLE manager = NULL;
680 SC_HANDLE service = NULL;
681 SERVICE_STATUS status = {0};
682 bool success = false;
684 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
687 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
691 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
694 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
695 success = stop_tincd();
697 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
703 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
704 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
706 fprintf(stderr, "%s service stopped\n", identname);
709 if(!DeleteService(service)) {
710 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
719 CloseServiceHandle(service);
723 CloseServiceHandle(manager);
727 fprintf(stderr, "%s service removed\n", identname);
734 bool connect_tincd(bool verbose) {
739 struct timeval tv = {0, 0};
741 if(select(fd + 1, &r, NULL, NULL, &tv)) {
742 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
750 pidfile_t *pidfile = read_pidfile();
754 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
761 strcpy(controlcookie, pidfile->cookie);
766 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
767 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
768 /* clean up the stale socket and pid file */
770 unlink(unixsocketname);
774 struct sockaddr_un sa = {
775 .sun_family = AF_UNIX,
778 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
779 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
783 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
785 fd = socket(AF_UNIX, SOCK_STREAM, 0);
789 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
795 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
797 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
806 struct addrinfo hints = {
807 .ai_family = AF_UNSPEC,
808 .ai_socktype = SOCK_STREAM,
809 .ai_protocol = IPPROTO_TCP,
813 struct addrinfo *res = NULL;
815 if(getaddrinfo(pidfile->host, pidfile->port, &hints, &res) || !res) {
817 fprintf(stderr, "Cannot resolve %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
824 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
828 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
835 unsigned long arg = 0;
837 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
839 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
843 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
845 fprintf(stderr, "Cannot connect to %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
859 static const int one = 1;
860 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
863 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
868 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
870 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
878 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
880 fprintf(stderr, "Could not fully establish control socket connection\n");
892 static int cmd_start(int argc, char *argv[]) {
893 if(connect_tincd(false)) {
895 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
897 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
904 char *slash = strrchr(program_name, '/');
908 if((c = strrchr(program_name, '\\')) > slash) {
915 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
917 c = xstrdup("tincd");
921 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
926 Windows has no real concept of an "argv array". A command line is just one string.
927 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
928 it uses quotes to handle spaces in arguments.
929 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
930 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
931 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
933 xasprintf(&arg0, "\"%s\"", arg0);
935 nargv[nargc++] = arg0;
937 for(int i = 1; i < optind; i++) {
938 nargv[nargc++] = orig_argv[i];
941 for(int i = 1; i < argc; i++) {
942 nargv[nargc++] = argv[i];
946 int status = spawnvp(_P_WAIT, c, nargv);
952 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
958 int pfd[2] = {-1, -1};
960 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
961 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
970 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
979 snprintf(buf, sizeof(buf), "%d %d", pfd[1], use_ansi_escapes(stderr));
980 setenv("TINC_UMBILICAL", buf, true);
981 exit(execvp(c, nargv));
989 signal(SIGINT, SIG_IGN);
992 // Pass all log messages from the umbilical to stderr.
993 // A nul-byte right before closure means tincd started successfully.
998 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
999 failure = buf[len - 1];
1005 if(write(2, buf, len) != len) {
1006 // Nothing we can do about it.
1016 // Make sure the child process is really gone.
1018 pid_t result = waitpid(pid, &status, 0);
1021 signal(SIGINT, SIG_DFL);
1024 bool failed = failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status);
1027 fprintf(stderr, "Error starting %s\n", c);
1032 return failed ? EXIT_FAILURE : EXIT_SUCCESS;
1036 static int cmd_stop(int argc, char *argv[]) {
1040 fprintf(stderr, "Too many arguments!\n");
1045 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1050 if(kill(pid, SIGTERM)) {
1051 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1055 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1056 waitpid(pid, NULL, 0);
1067 static int cmd_restart(int argc, char *argv[]) {
1069 return cmd_start(argc, argv);
1072 static int cmd_reload(int argc, char *argv[]) {
1076 fprintf(stderr, "Too many arguments!\n");
1080 if(!connect_tincd(true)) {
1084 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1086 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1087 fprintf(stderr, "Could not reload configuration.\n");
1095 static int dump_invitations(void) {
1096 char dname[PATH_MAX];
1097 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1098 DIR *dir = opendir(dname);
1101 if(errno == ENOENT) {
1102 fprintf(stderr, "No outstanding invitations.\n");
1106 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1114 while((ent = readdir(dir))) {
1115 char buf[MAX_STRING_SIZE];
1117 if(b64decode_tinc(ent->d_name, buf, 24) != 18) {
1121 char fname[PATH_MAX];
1123 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1124 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1128 FILE *f = fopen(fname, "r");
1131 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1137 if(!fgets(buf, sizeof(buf), f)) {
1138 fprintf(stderr, "Invalid invitation file %s\n", fname);
1145 char *eol = buf + strlen(buf);
1147 while(strchr("\t \r\n", *--eol)) {
1151 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1152 fprintf(stderr, "Invalid invitation file %s\n", fname);
1157 printf("%s %s\n", ent->d_name, buf + 7);
1163 fprintf(stderr, "No outstanding invitations.\n");
1169 static int cmd_dump(int argc, char *argv[]) {
1170 bool only_reachable = false;
1172 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1173 if(strcasecmp(argv[2], "nodes")) {
1174 fprintf(stderr, "`reachable' only supported for nodes.\n");
1179 only_reachable = true;
1185 fprintf(stderr, "Invalid number of arguments.\n");
1190 if(!strcasecmp(argv[1], "invitations")) {
1191 return dump_invitations();
1194 if(!connect_tincd(true)) {
1200 if(!strcasecmp(argv[1], "nodes")) {
1201 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1202 } else if(!strcasecmp(argv[1], "edges")) {
1203 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1204 } else if(!strcasecmp(argv[1], "subnets")) {
1205 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1206 } else if(!strcasecmp(argv[1], "connections")) {
1207 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1208 } else if(!strcasecmp(argv[1], "graph")) {
1209 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1210 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1212 } else if(!strcasecmp(argv[1], "digraph")) {
1213 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1214 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1217 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1223 printf("graph {\n");
1224 } else if(do_graph == 2) {
1225 printf("digraph {\n");
1228 while(recvline(fd, line, sizeof(line))) {
1229 char node1[4096], node2[4096];
1230 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1233 if(do_graph && req == REQ_DUMP_NODES) {
1255 char local_host[4096];
1256 char local_port[4096];
1259 int cipher, digest, maclength, compression, distance, socket, weight;
1260 short int pmtu, minmtu, maxmtu;
1261 unsigned int options;
1262 node_status_t status;
1263 long int last_state_change;
1265 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1268 case REQ_DUMP_NODES: {
1269 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);
1272 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1277 const char *color = "black";
1279 if(!strcmp(host, "MYSELF")) {
1281 } else if(!status.reachable) {
1283 } else if(strcmp(via, node)) {
1285 } else if(!status.validkey) {
1287 } else if(minmtu > 0) {
1291 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1293 if(only_reachable && !status.reachable) {
1297 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,
1298 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);
1300 if(udp_ping_rtt != -1) {
1301 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1309 case REQ_DUMP_EDGES: {
1310 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);
1313 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1318 float w = 1.0f + 65536.0f / (float)weight;
1320 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1321 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1322 } else if(do_graph == 2) {
1323 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1326 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);
1331 case REQ_DUMP_SUBNETS: {
1332 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1335 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1339 printf("%s owner %s\n", strip_weight(subnet), node);
1343 case REQ_DUMP_CONNECTIONS: {
1344 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status.value);
1347 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1351 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status.value);
1356 fprintf(stderr, "Unable to parse dump from tincd.\n");
1361 fprintf(stderr, "Error receiving dump.\n");
1365 static int cmd_purge(int argc, char *argv[]) {
1369 fprintf(stderr, "Too many arguments!\n");
1373 if(!connect_tincd(true)) {
1377 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1379 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1380 fprintf(stderr, "Could not purge old information.\n");
1387 static int cmd_debug(int argc, char *argv[]) {
1389 fprintf(stderr, "Invalid number of arguments.\n");
1393 if(!connect_tincd(true)) {
1397 int debuglevel = atoi(argv[1]);
1400 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1402 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1403 fprintf(stderr, "Could not set debug level.\n");
1407 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1411 static int cmd_retry(int argc, char *argv[]) {
1415 fprintf(stderr, "Too many arguments!\n");
1419 if(!connect_tincd(true)) {
1423 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1425 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1426 fprintf(stderr, "Could not retry outgoing connections.\n");
1433 static int cmd_connect(int argc, char *argv[]) {
1435 fprintf(stderr, "Invalid number of arguments.\n");
1439 if(!check_id(argv[1])) {
1440 fprintf(stderr, "Invalid name for node.\n");
1444 if(!connect_tincd(true)) {
1448 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1450 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1451 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1458 static int cmd_disconnect(int argc, char *argv[]) {
1460 fprintf(stderr, "Invalid number of arguments.\n");
1464 if(!check_id(argv[1])) {
1465 fprintf(stderr, "Invalid name for node.\n");
1469 if(!connect_tincd(true)) {
1473 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1475 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1476 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1483 static int cmd_top(int argc, char *argv[]) {
1487 fprintf(stderr, "Too many arguments!\n");
1493 if(!connect_tincd(true)) {
1500 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1505 static int cmd_pcap(int argc, char *argv[]) {
1507 fprintf(stderr, "Too many arguments!\n");
1511 if(!connect_tincd(true)) {
1515 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1520 static void sigint_handler(int sig) {
1523 if(write(2, "\n", 1) < 0) {
1524 // nothing we can do
1527 shutdown(fd, SHUT_RDWR);
1531 static int cmd_log(int argc, char *argv[]) {
1533 fprintf(stderr, "Too many arguments!\n");
1537 if(!connect_tincd(true)) {
1542 signal(SIGINT, sigint_handler);
1545 bool use_color = use_ansi_escapes(stdout);
1546 log_control(fd, stdout, argc > 1 ? atoi(argv[1]) : DEBUG_UNSET, use_color);
1549 signal(SIGINT, SIG_DFL);
1557 static int cmd_pid(int argc, char *argv[]) {
1561 fprintf(stderr, "Too many arguments!\n");
1565 if(!connect_tincd(true) || !pid) {
1569 printf("%d\n", pid);
1573 size_t rstrip(char *value) {
1574 size_t len = strlen(value);
1576 while(len && strchr("\t\r\n ", value[len - 1])) {
1583 char *get_my_name(bool verbose) {
1584 FILE *f = fopen(tinc_conf, "r");
1588 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1597 while(fgets(buf, sizeof(buf), f)) {
1598 size_t len = strcspn(buf, "\t =");
1600 value += strspn(value, "\t ");
1604 value += strspn(value, "\t ");
1607 if(!rstrip(value)) {
1613 if(strcasecmp(buf, "Name")) {
1619 return replace_name(value);
1626 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1632 static ecdsa_t *get_pubkey(FILE *f) ATTR_MALLOC ATTR_DEALLOCATOR(ecdsa_free);
1633 static ecdsa_t *get_pubkey(FILE *f) {
1637 while(fgets(buf, sizeof(buf), f)) {
1638 size_t len = strcspn(buf, "\t =");
1640 value += strspn(value, "\t ");
1644 value += strspn(value, "\t ");
1647 if(!rstrip(value)) {
1653 if(strcasecmp(buf, "Ed25519PublicKey")) {
1658 return ecdsa_set_base64_public_key(value);
1665 const var_t variables[] = {
1666 /* Server configuration */
1667 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1668 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1669 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1670 {"BindToInterface", VAR_SERVER},
1671 {"Broadcast", VAR_SERVER | VAR_SAFE},
1672 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1673 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1674 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1675 {"Device", VAR_SERVER},
1676 {"DeviceStandby", VAR_SERVER},
1677 {"DeviceType", VAR_SERVER},
1678 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1679 {"Ed25519PrivateKeyFile", VAR_SERVER},
1680 {"ExperimentalProtocol", VAR_SERVER | VAR_OBSOLETE},
1681 {"Forwarding", VAR_SERVER},
1682 {"FWMark", VAR_SERVER},
1683 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1684 {"Hostnames", VAR_SERVER},
1685 {"IffOneQueue", VAR_SERVER},
1686 {"Interface", VAR_SERVER},
1687 {"InvitationExpire", VAR_SERVER},
1688 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1689 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1690 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1691 {"LogLevel", VAR_SERVER},
1692 {"MACExpire", VAR_SERVER | VAR_SAFE},
1693 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1694 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1695 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1696 {"Mode", VAR_SERVER | VAR_SAFE},
1697 {"Name", VAR_SERVER},
1698 {"PingInterval", VAR_SERVER | VAR_SAFE},
1699 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1700 {"PriorityInheritance", VAR_SERVER},
1701 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1702 {"PrivateKeyFile", VAR_SERVER},
1703 {"ProcessPriority", VAR_SERVER},
1704 {"Proxy", VAR_SERVER},
1705 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1706 {"Sandbox", VAR_SERVER},
1707 {"ScriptsExtension", VAR_SERVER},
1708 {"ScriptsInterpreter", VAR_SERVER},
1709 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1710 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1711 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1712 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1713 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1714 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1715 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1716 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1717 {"UDPRcvBuf", VAR_SERVER},
1718 {"UDPSndBuf", VAR_SERVER},
1719 {"UPnP", VAR_SERVER},
1720 {"UPnPDiscoverWait", VAR_SERVER},
1721 {"UPnPRefreshPeriod", VAR_SERVER},
1722 {"VDEGroup", VAR_SERVER},
1723 {"VDEPort", VAR_SERVER},
1724 /* Host configuration */
1725 {"Address", VAR_HOST | VAR_MULTIPLE},
1726 {"Cipher", VAR_SERVER | VAR_HOST},
1727 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1728 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1729 {"Digest", VAR_SERVER | VAR_HOST},
1730 {"Ed25519PublicKey", VAR_HOST},
1731 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1732 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1733 {"MACLength", VAR_SERVER | VAR_HOST},
1734 {"PMTU", VAR_SERVER | VAR_HOST},
1735 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1737 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1738 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1739 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1740 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1741 {"Weight", VAR_HOST | VAR_SAFE},
1745 // Request actual port from tincd
1746 static bool read_actual_port(void) {
1747 pidfile_t *pidfile = read_pidfile();
1750 printf("%s\n", pidfile->port);
1754 fprintf(stderr, "Could not get port from the pidfile.\n");
1759 static int cmd_config(int argc, char *argv[]) {
1761 fprintf(stderr, "Invalid number of arguments.\n");
1765 if(strcasecmp(argv[0], "config")) {
1769 typedef enum { GET, DEL, SET, ADD } action_t;
1770 action_t action = GET;
1772 if(!strcasecmp(argv[1], "get")) {
1774 } else if(!strcasecmp(argv[1], "add")) {
1775 argv++, argc--, action = ADD;
1776 } else if(!strcasecmp(argv[1], "del")) {
1777 argv++, argc--, action = DEL;
1778 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1779 argv++, argc--, action = SET;
1783 fprintf(stderr, "Invalid number of arguments.\n");
1787 // Concatenate the rest of the command line
1788 strncpy(line, argv[1], sizeof(line) - 1);
1790 for(int i = 2; i < argc; i++) {
1791 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1792 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1795 // Liberal parsing into node name, variable name and value.
1801 len = strcspn(line, "\t =");
1803 value += strspn(value, "\t ");
1807 value += strspn(value, "\t ");
1811 variable = strchr(line, '.');
1821 fprintf(stderr, "No variable given.\n");
1825 if((action == SET || action == ADD) && !*value) {
1826 fprintf(stderr, "No value for variable given.\n");
1830 if(action == GET && *value) {
1834 // If port is requested, try reading it from the pidfile and fall back to configs if that fails
1835 if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
1839 /* Some simple checks. */
1841 bool warnonremove = false;
1843 for(int i = 0; variables[i].name; i++) {
1844 if(strcasecmp(variables[i].name, variable)) {
1849 variable = (char *)variables[i].name;
1851 if(!strcasecmp(variable, "Subnet") && *value) {
1854 if(!str2net(&s, value)) {
1855 fprintf(stderr, "Malformed subnet definition %s\n", value);
1859 if(!subnetcheck(s)) {
1860 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1865 /* Discourage use of obsolete variables. */
1867 if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
1869 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1871 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1876 /* Don't put server variables in host config files */
1878 if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
1880 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1882 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1887 /* Should this go into our own host config file? */
1889 if(!node && !(variables[i].type & VAR_SERVER)) {
1890 node = get_my_name(true);
1897 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1898 Turn on warnings when it seems variables might be removed unintentionally. */
1900 if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
1901 warnonremove = true;
1903 } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
1904 warnonremove = true;
1910 if(node && !check_id(node)) {
1911 fprintf(stderr, "Invalid name for node.\n");
1921 if(force || action == GET || action == DEL) {
1922 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1924 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1926 if(node && node != line) {
1934 // Open the right configuration file.
1935 char filename[PATH_MAX];
1938 size_t wrote = (size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1945 if(wrote >= sizeof(filename)) {
1946 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, node);
1951 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1954 FILE *f = fopen(filename, "r");
1957 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1961 char tmpfile[PATH_MAX];
1965 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1966 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1970 tf = fopen(tmpfile, "w");
1973 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1979 // Copy the file, making modifications on the fly, unless we are just getting a value.
1983 bool removed = false;
1986 while(fgets(buf1, sizeof(buf1), f)) {
1987 buf1[sizeof(buf1) - 1] = 0;
1988 strncpy(buf2, buf1, sizeof(buf2));
1990 // Parse line in a simple way
1993 size_t len = strcspn(buf2, "\t =");
1994 bvalue = buf2 + len;
1995 bvalue += strspn(bvalue, "\t ");
1997 if(*bvalue == '=') {
1999 bvalue += strspn(bvalue, "\t ");
2006 if(!strcasecmp(buf2, variable)) {
2009 printf("%s\n", bvalue);
2010 } else if(action == DEL) {
2011 if(!*value || !strcasecmp(bvalue, value)) {
2015 } else if(action == SET) {
2016 // Warn if "set" was used for variables that can occur multiple times
2017 if(warnonremove && strcasecmp(bvalue, value)) {
2018 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2021 // Already set? Delete the rest...
2026 // Otherwise, replace.
2027 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2028 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2034 } else if(action == ADD) {
2035 // Check if we've already seen this variable with the same value
2036 if(!strcasecmp(bvalue, value)) {
2043 // Copy original line...
2044 if(fputs(buf1, tf) < 0) {
2045 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2049 // Add newline if it is missing...
2050 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2051 if(fputc('\n', tf) < 0) {
2052 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2059 // Make sure we read everything...
2060 if(ferror(f) || !feof(f)) {
2061 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2066 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2070 // Add new variable if necessary.
2071 if((action == ADD && !found) || (action == SET && !set)) {
2072 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2073 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2082 fprintf(stderr, "No matching configuration variables found.\n");
2087 // Make sure we wrote everything...
2089 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2093 // Could we find what we had to remove?
2094 if((action == GET || action == DEL) && !removed) {
2096 fprintf(stderr, "No configuration variables deleted.\n");
2100 // Replace the configuration file with the new one
2103 if(remove(filename)) {
2104 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2110 if(rename(tmpfile, filename)) {
2111 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2115 // Silently try notifying a running tincd of changes.
2116 if(connect_tincd(false)) {
2117 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2123 static bool try_bind(int port) {
2124 struct addrinfo *ai = NULL, *aip;
2125 struct addrinfo hint = {
2126 .ai_flags = AI_PASSIVE,
2127 .ai_family = AF_UNSPEC,
2128 .ai_socktype = SOCK_STREAM,
2129 .ai_protocol = IPPROTO_TCP,
2132 bool success = true;
2134 snprintf(portstr, sizeof(portstr), "%d", port);
2136 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2140 for(aip = ai; aip; aip = aip->ai_next) {
2141 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2148 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2161 int check_port(const char *name) {
2166 fprintf(stderr, "Warning: could not bind to port 655. ");
2168 for(int i = 0; i < 100; i++) {
2169 uint16_t port = 0x1000 + prng(0x8000);
2171 if(try_bind(port)) {
2172 char filename[PATH_MAX];
2173 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2174 FILE *f = fopen(filename, "a");
2177 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2178 fprintf(stderr, "Please change tinc's Port manually.\n");
2182 fprintf(f, "Port = %d\n", port);
2184 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2189 fprintf(stderr, "Please change tinc's Port manually.\n");
2193 static int cmd_init(int argc, char *argv[]) {
2194 if(!access(tinc_conf, F_OK)) {
2195 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2200 fprintf(stderr, "Too many arguments!\n");
2202 } else if(argc < 2) {
2205 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2207 if(!fgets(buf, sizeof(buf), stdin)) {
2208 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2212 size_t len = rstrip(buf);
2215 fprintf(stderr, "No name given!\n");
2221 fprintf(stderr, "No Name given!\n");
2225 name = strdup(argv[1]);
2228 fprintf(stderr, "No Name given!\n");
2233 if(!check_id(name)) {
2234 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2238 if(!makedirs(DIR_HOSTS | DIR_CONFBASE | DIR_CONFDIR | DIR_CACHE)) {
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#---------------------------------------------------------------#\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 sandbox_set_level(SANDBOX_NORMAL);
3356 int result = run_command(argc, argv);