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"
46 #define MSG_NOSIGNAL 0
49 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 bool netnamegiven = false;
75 char *scriptinterpreter = NULL;
76 char *scriptextension = "";
82 static struct option const long_options[] = {
83 {"batch", no_argument, NULL, 'b'},
84 {"config", required_argument, NULL, 'c'},
85 {"net", required_argument, NULL, 'n'},
86 {"help", no_argument, NULL, 1},
87 {"version", no_argument, NULL, 2},
88 {"pidfile", required_argument, NULL, 3},
89 {"force", no_argument, NULL, 4},
93 static void version(void) {
94 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
95 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
96 printf("Copyright (C) 1998-2018 Ivo Timmermans, Guus Sliepen and others.\n"
97 "See the AUTHORS file for a complete list.\n\n"
98 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
99 "and you are welcome to redistribute it under certain conditions;\n"
100 "see the file COPYING for details.\n");
103 static void usage(bool status) {
105 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
107 printf("Usage: %s [options] command\n\n", program_name);
108 printf("Valid options are:\n"
109 " -b, --batch Don't ask for anything (non-interactive mode).\n"
110 " -c, --config=DIR Read configuration options from DIR.\n"
111 " -n, --net=NETNAME Connect to net NETNAME.\n"
112 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
113 " --force Force some commands to work despite warnings.\n"
114 " --help Display this help and exit.\n"
115 " --version Output version information and exit.\n"
117 "Valid commands are:\n"
118 " init [name] Create initial configuration files.\n"
119 " get VARIABLE Print current value of VARIABLE\n"
120 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
121 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
122 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
123 " start [tincd options] Start tincd.\n"
124 " stop Stop tincd.\n"
125 " restart [tincd options] Restart tincd.\n"
126 " reload Partially reload configuration of running tincd.\n"
127 " pid Show PID of currently running tincd.\n"
128 #ifdef DISABLE_LEGACY
129 " generate-keys Generate a new Ed25519 public/private key pair.\n"
131 " generate-keys [bits] Generate new RSA and Ed25519 public/private key pairs.\n"
132 " generate-rsa-keys [bits] Generate a new RSA public/private key pair.\n"
134 " generate-ed25519-keys Generate a new Ed25519 public/private key pair.\n"
135 " dump Dump a list of one of the following things:\n"
136 " [reachable] nodes - all known nodes in the VPN\n"
137 " edges - all known connections in the VPN\n"
138 " subnets - all known subnets in the VPN\n"
139 " connections - all meta connections with ourself\n"
140 " [di]graph - graph of the VPN in dotty format\n"
141 " invitations - outstanding invitations\n"
142 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
143 " purge Purge unreachable nodes\n"
144 " debug N Set debug level\n"
145 " retry Retry all outgoing connections\n"
146 " disconnect NODE Close meta connection with NODE\n"
148 " top Show real-time statistics\n"
150 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
151 " log [level] Dump log output [up to the specified level]\n"
152 " export Export host configuration of local node to standard output\n"
153 " export-all Export all host configuration files to standard output\n"
154 " import Import host configuration file(s) from standard input\n"
155 " exchange Same as export followed by import\n"
156 " exchange-all Same as export-all followed by import\n"
157 " invite NODE [...] Generate an invitation for NODE\n"
158 " join INVITATION Join a VPN using an INVITATION\n"
159 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
160 " fsck Check the configuration files for problems.\n"
161 " sign [FILE] Generate a signed version of a file.\n"
162 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
164 printf("Report bugs to tinc@tinc-vpn.org.\n");
168 static bool parse_options(int argc, char **argv) {
170 int option_index = 0;
172 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
174 case 0: /* long option */
181 case 'c': /* config file */
182 confbase = xstrdup(optarg);
183 confbasegiven = true;
186 case 'n': /* net name given */
187 netname = xstrdup(optarg);
190 case 1: /* show help */
194 case 2: /* show version */
198 case 3: /* open control socket here */
199 pidfilename = xstrdup(optarg);
206 case '?': /* wrong options */
215 if(!netname && (netname = getenv("NETNAME"))) {
216 netname = xstrdup(netname);
219 /* netname "." is special: a "top-level name" */
221 if(netname && (!*netname || !strcmp(netname, "."))) {
226 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
227 fprintf(stderr, "Invalid character in netname!\n");
234 /* Open a file with the desired permissions, minus the umask.
235 Also, if we want to create an executable file, we call fchmod()
236 to set the executable bits. */
238 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
239 mode_t mask = umask(0);
241 umask(~perms & 0777);
242 FILE *f = fopen(filename, mode);
245 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
251 if((perms & 0444) && f) {
252 fchmod(fileno(f), perms);
260 static void disable_old_keys(const char *filename, const char *what) {
261 char tmpfile[PATH_MAX] = "";
263 bool disabled = false;
267 FILE *r = fopen(filename, "r");
274 int result = snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
276 if(result < sizeof(tmpfile)) {
277 struct stat st = {.st_mode = 0600};
278 fstat(fileno(r), &st);
279 w = fopenmask(tmpfile, "w", st.st_mode);
282 while(fgets(buf, sizeof(buf), r)) {
283 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
284 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
290 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
297 if(block || ed25519pubkey) {
301 if(fputs(buf, w) < 0) {
307 if(block && !strncmp(buf, "-----END ", 9)) {
317 if(ferror(r) || fclose(r) < 0) {
323 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
333 // We cannot atomically replace files on Windows.
334 char bakfile[PATH_MAX] = "";
335 snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
337 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
338 rename(bakfile, filename);
341 if(rename(tmpfile, filename)) {
343 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
348 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
355 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
357 char directory[PATH_MAX] = ".";
363 /* Check stdin and stdout */
365 /* Ask for a file and/or directory name. */
366 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
368 if(fgets(buf, sizeof(buf), stdin) == NULL) {
369 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
373 size_t len = strlen(buf);
386 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
389 if(filename[0] != '/') {
391 /* The directory is a relative path or a filename. */
392 getcwd(directory, sizeof(directory));
394 if((size_t)snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename) >= sizeof(buf2)) {
395 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", directory, filename);
407 disable_old_keys(filename, what);
409 /* Open it first to keep the inode busy */
411 r = fopenmask(filename, mode, perms);
414 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
422 Generate a public/private Ed25519 key pair, and ask for a file to store
425 static bool ed25519_keygen(bool ask) {
428 char fname[PATH_MAX];
430 fprintf(stderr, "Generating Ed25519 key pair:\n");
432 if(!(key = ecdsa_generate())) {
433 fprintf(stderr, "Error during key generation!\n");
436 fprintf(stderr, "Done.\n");
439 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
440 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
446 if(!ecdsa_write_pem_private_key(key, f)) {
447 fprintf(stderr, "Error writing private key!\n");
454 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
456 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
459 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
465 char *pubkey = ecdsa_get_base64_public_key(key);
466 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
484 #ifndef DISABLE_LEGACY
486 Generate a public/private RSA key pair, and ask for a file to store
489 static bool rsa_keygen(int bits, bool ask) {
492 char fname[PATH_MAX];
494 // Make sure the key size is a multiple of 8 bits.
497 // Make sure that a valid key size is used.
498 if(bits < 1024 || bits > 8192) {
499 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
501 } else if(bits < 2048) {
502 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
505 fprintf(stderr, "Generating %d bits keys:\n", bits);
507 if(!(key = rsa_generate(bits, 0x10001))) {
508 fprintf(stderr, "Error during key generation!\n");
511 fprintf(stderr, "Done.\n");
514 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
515 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
521 if(!rsa_write_pem_private_key(key, f)) {
522 fprintf(stderr, "Error writing private key!\n");
529 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
531 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
534 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
540 if(!rsa_write_pem_public_key(key, f)) {
541 fprintf(stderr, "Error writing public key!\n");
564 bool recvline(int fd, char *line, size_t len) {
565 char *newline = NULL;
571 while(!(newline = memchr(buffer, '\n', blen))) {
572 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
574 if(result == -1 && sockerrno == EINTR) {
576 } else if(result <= 0) {
583 if((size_t)(newline - buffer) >= len) {
587 len = newline - buffer;
589 memcpy(line, buffer, len);
591 memmove(buffer, newline + 1, blen - len - 1);
597 static bool recvdata(int fd, char *data, size_t len) {
599 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
601 if(result == -1 && sockerrno == EINTR) {
603 } else if(result <= 0) {
610 memcpy(data, buffer, len);
611 memmove(buffer, buffer + len, blen - len);
617 bool sendline(int fd, char *format, ...) {
618 static char buffer[4096];
623 va_start(ap, format);
624 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
625 buffer[sizeof(buffer) - 1] = 0;
628 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
636 int result = send(fd, p, blen, MSG_NOSIGNAL);
638 if(result == -1 && sockerrno == EINTR) {
640 } else if(result <= 0) {
651 static void pcap(int fd, FILE *out, uint32_t snaplen) {
652 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
660 uint32_t tz_accuracy;
667 snaplen ? snaplen : sizeof(data),
680 fwrite(&header, sizeof(header), 1, out);
685 while(recvline(fd, line, sizeof(line))) {
687 int n = sscanf(line, "%d %d %d", &code, &req, &len);
688 gettimeofday(&tv, NULL);
690 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || (size_t)len > sizeof(data)) {
694 if(!recvdata(fd, data, len)) {
698 packet.tv_sec = tv.tv_sec;
699 packet.tv_usec = tv.tv_usec;
701 packet.origlen = len;
702 fwrite(&packet, sizeof(packet), 1, out);
703 fwrite(data, len, 1, out);
708 static void logcontrol(int fd, FILE *out, int level) {
709 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
713 while(recvline(fd, line, sizeof(line))) {
715 int n = sscanf(line, "%d %d %d", &code, &req, &len);
717 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || (size_t)len > sizeof(data)) {
721 if(!recvdata(fd, data, len)) {
725 fwrite(data, len, 1, out);
731 static bool stop_tincd(void) {
732 if(!connect_tincd(true)) {
736 sendline(fd, "%d %d", CONTROL, REQ_STOP);
738 while(recvline(fd, line, sizeof(line))) {
739 // wait for tincd to close the connection...
750 static bool remove_service(void) {
751 SC_HANDLE manager = NULL;
752 SC_HANDLE service = NULL;
753 SERVICE_STATUS status = {0};
754 bool success = false;
756 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
759 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
763 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
766 if(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
767 success = stop_tincd();
769 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
775 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
776 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
778 fprintf(stderr, "%s service stopped\n", identname);
781 if(!DeleteService(service)) {
782 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
791 CloseServiceHandle(service);
795 CloseServiceHandle(manager);
799 fprintf(stderr, "%s service removed\n", identname);
806 bool connect_tincd(bool verbose) {
811 struct timeval tv = {0, 0};
813 if(select(fd + 1, &r, NULL, NULL, &tv)) {
814 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
822 FILE *f = fopen(pidfilename, "r");
826 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
835 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
837 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
848 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
849 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
850 /* clean up the stale socket and pid file */
852 unlink(unixsocketname);
856 struct sockaddr_un sa = {
857 .sun_family = AF_UNIX,
860 if(strlen(unixsocketname) >= sizeof(sa.sun_path)) {
861 fprintf(stderr, "UNIX socket filename %s is too long!", unixsocketname);
865 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
867 fd = socket(AF_UNIX, SOCK_STREAM, 0);
871 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
877 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
879 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
888 struct addrinfo hints = {
889 .ai_family = AF_UNSPEC,
890 .ai_socktype = SOCK_STREAM,
891 .ai_protocol = IPPROTO_TCP,
895 struct addrinfo *res = NULL;
897 if(getaddrinfo(host, port, &hints, &res) || !res) {
899 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
905 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
909 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
915 unsigned long arg = 0;
917 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
919 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
923 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
925 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
937 static const int one = 1;
938 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
941 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
946 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
948 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
956 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
958 fprintf(stderr, "Could not fully establish control socket connection\n");
970 static int cmd_start(int argc, char *argv[]) {
971 if(connect_tincd(false)) {
973 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
975 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
982 char *slash = strrchr(program_name, '/');
986 if((c = strrchr(program_name, '\\')) > slash) {
993 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
999 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
1004 Windows has no real concept of an "argv array". A command line is just one string.
1005 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
1006 it uses quotes to handle spaces in arguments.
1007 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
1008 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
1009 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
1011 xasprintf(&arg0, "\"%s\"", arg0);
1013 nargv[nargc++] = arg0;
1015 for(int i = 1; i < optind; i++) {
1016 nargv[nargc++] = orig_argv[i];
1019 for(int i = 1; i < argc; i++) {
1020 nargv[nargc++] = argv[i];
1024 int status = spawnvp(_P_WAIT, c, nargv);
1027 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
1033 int pfd[2] = {-1, -1};
1035 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
1036 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
1044 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1052 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1053 setenv("TINC_UMBILICAL", buf, true);
1054 exit(execvp(c, nargv));
1061 int status = -1, result;
1063 signal(SIGINT, SIG_IGN);
1066 // Pass all log messages from the umbilical to stderr.
1067 // A nul-byte right before closure means tincd started successfully.
1068 bool failure = true;
1072 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1073 failure = buf[len - 1];
1088 // Make sure the child process is really gone.
1089 result = waitpid(pid, &status, 0);
1092 signal(SIGINT, SIG_DFL);
1095 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1096 fprintf(stderr, "Error starting %s\n", c);
1104 static int cmd_stop(int argc, char *argv[]) {
1108 fprintf(stderr, "Too many arguments!\n");
1113 return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
1118 if(kill(pid, SIGTERM)) {
1119 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1123 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1124 waitpid(pid, NULL, 0);
1135 static int cmd_restart(int argc, char *argv[]) {
1137 return cmd_start(argc, argv);
1140 static int cmd_reload(int argc, char *argv[]) {
1144 fprintf(stderr, "Too many arguments!\n");
1148 if(!connect_tincd(true)) {
1152 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1154 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1155 fprintf(stderr, "Could not reload configuration.\n");
1163 static int dump_invitations(void) {
1164 char dname[PATH_MAX];
1165 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1166 DIR *dir = opendir(dname);
1169 if(errno == ENOENT) {
1170 fprintf(stderr, "No outstanding invitations.\n");
1174 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1182 while((ent = readdir(dir))) {
1183 char buf[MAX_STRING_SIZE];
1185 if(b64decode(ent->d_name, buf, 24) != 18) {
1189 char fname[PATH_MAX];
1191 if((size_t)snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name) >= sizeof(fname)) {
1192 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", dname, ent->d_name);
1196 FILE *f = fopen(fname, "r");
1199 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1205 if(!fgets(buf, sizeof(buf), f)) {
1206 fprintf(stderr, "Invalid invitation file %s\n", fname);
1213 char *eol = buf + strlen(buf);
1215 while(strchr("\t \r\n", *--eol)) {
1219 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1220 fprintf(stderr, "Invalid invitation file %s\n", fname);
1225 printf("%s %s\n", ent->d_name, buf + 7);
1231 fprintf(stderr, "No outstanding invitations.\n");
1237 static int cmd_dump(int argc, char *argv[]) {
1238 bool only_reachable = false;
1240 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1241 if(strcasecmp(argv[2], "nodes")) {
1242 fprintf(stderr, "`reachable' only supported for nodes.\n");
1247 only_reachable = true;
1253 fprintf(stderr, "Invalid number of arguments.\n");
1258 if(!strcasecmp(argv[1], "invitations")) {
1259 return dump_invitations();
1262 if(!connect_tincd(true)) {
1268 if(!strcasecmp(argv[1], "nodes")) {
1269 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1270 } else if(!strcasecmp(argv[1], "edges")) {
1271 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1272 } else if(!strcasecmp(argv[1], "subnets")) {
1273 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1274 } else if(!strcasecmp(argv[1], "connections")) {
1275 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1276 } else if(!strcasecmp(argv[1], "graph")) {
1277 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1278 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1280 } else if(!strcasecmp(argv[1], "digraph")) {
1281 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1282 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1285 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1291 printf("graph {\n");
1292 } else if(do_graph == 2) {
1293 printf("digraph {\n");
1296 while(recvline(fd, line, sizeof(line))) {
1297 char node1[4096], node2[4096];
1298 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1301 if(do_graph && req == REQ_DUMP_NODES) {
1323 char local_host[4096];
1324 char local_port[4096];
1327 int cipher, digest, maclength, compression, distance, socket, weight;
1328 short int pmtu, minmtu, maxmtu;
1329 unsigned int options, status_int;
1330 node_status_t status;
1331 long int last_state_change;
1333 uint64_t in_packets, in_bytes, out_packets, out_bytes;
1336 case REQ_DUMP_NODES: {
1337 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);
1340 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1344 memcpy(&status, &status_int, sizeof(status));
1347 const char *color = "black";
1349 if(!strcmp(host, "MYSELF")) {
1351 } else if(!status.reachable) {
1353 } else if(strcmp(via, node)) {
1355 } else if(!status.validkey) {
1357 } else if(minmtu > 0) {
1361 printf(" \"%s\" [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1363 if(only_reachable && !status.reachable) {
1367 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,
1368 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);
1370 if(udp_ping_rtt != -1) {
1371 printf(" rtt %d.%03d", udp_ping_rtt / 1000, udp_ping_rtt % 1000);
1379 case REQ_DUMP_EDGES: {
1380 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);
1383 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1388 float w = 1 + 65536.0 / weight;
1390 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1391 printf(" \"%s\" -- \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1392 } else if(do_graph == 2) {
1393 printf(" \"%s\" -> \"%s\" [w = %f, weight = %f];\n", node1, node2, w, w);
1396 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);
1401 case REQ_DUMP_SUBNETS: {
1402 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1405 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1409 printf("%s owner %s\n", strip_weight(subnet), node);
1413 case REQ_DUMP_CONNECTIONS: {
1414 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1417 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1421 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1426 fprintf(stderr, "Unable to parse dump from tincd.\n");
1431 fprintf(stderr, "Error receiving dump.\n");
1435 static int cmd_purge(int argc, char *argv[]) {
1439 fprintf(stderr, "Too many arguments!\n");
1443 if(!connect_tincd(true)) {
1447 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1449 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1450 fprintf(stderr, "Could not purge old information.\n");
1457 static int cmd_debug(int argc, char *argv[]) {
1459 fprintf(stderr, "Invalid number of arguments.\n");
1463 if(!connect_tincd(true)) {
1467 int debuglevel = atoi(argv[1]);
1470 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1472 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1473 fprintf(stderr, "Could not set debug level.\n");
1477 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1481 static int cmd_retry(int argc, char *argv[]) {
1485 fprintf(stderr, "Too many arguments!\n");
1489 if(!connect_tincd(true)) {
1493 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1495 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1496 fprintf(stderr, "Could not retry outgoing connections.\n");
1503 static int cmd_connect(int argc, char *argv[]) {
1505 fprintf(stderr, "Invalid number of arguments.\n");
1509 if(!check_id(argv[1])) {
1510 fprintf(stderr, "Invalid name for node.\n");
1514 if(!connect_tincd(true)) {
1518 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1520 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1521 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1528 static int cmd_disconnect(int argc, char *argv[]) {
1530 fprintf(stderr, "Invalid number of arguments.\n");
1534 if(!check_id(argv[1])) {
1535 fprintf(stderr, "Invalid name for node.\n");
1539 if(!connect_tincd(true)) {
1543 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1545 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1546 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1553 static int cmd_top(int argc, char *argv[]) {
1557 fprintf(stderr, "Too many arguments!\n");
1563 if(!connect_tincd(true)) {
1570 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1575 static int cmd_pcap(int argc, char *argv[]) {
1577 fprintf(stderr, "Too many arguments!\n");
1581 if(!connect_tincd(true)) {
1585 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1590 static void sigint_handler(int sig) {
1593 fprintf(stderr, "\n");
1594 shutdown(fd, SHUT_RDWR);
1598 static int cmd_log(int argc, char *argv[]) {
1600 fprintf(stderr, "Too many arguments!\n");
1604 if(!connect_tincd(true)) {
1609 signal(SIGINT, sigint_handler);
1612 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1615 signal(SIGINT, SIG_DFL);
1623 static int cmd_pid(int argc, char *argv[]) {
1627 fprintf(stderr, "Too many arguments!\n");
1631 if(!connect_tincd(true) || !pid) {
1635 printf("%d\n", pid);
1639 int rstrip(char *value) {
1640 int len = strlen(value);
1642 while(len && strchr("\t\r\n ", value[len - 1])) {
1649 char *get_my_name(bool verbose) {
1650 FILE *f = fopen(tinc_conf, "r");
1654 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1663 while(fgets(buf, sizeof(buf), f)) {
1664 int len = strcspn(buf, "\t =");
1666 value += strspn(value, "\t ");
1670 value += strspn(value, "\t ");
1673 if(!rstrip(value)) {
1679 if(strcasecmp(buf, "Name")) {
1685 return replace_name(value);
1692 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1698 ecdsa_t *get_pubkey(FILE *f) {
1702 while(fgets(buf, sizeof(buf), f)) {
1703 int len = strcspn(buf, "\t =");
1705 value += strspn(value, "\t ");
1709 value += strspn(value, "\t ");
1712 if(!rstrip(value)) {
1718 if(strcasecmp(buf, "Ed25519PublicKey")) {
1723 return ecdsa_set_base64_public_key(value);
1730 const var_t variables[] = {
1731 /* Server configuration */
1732 {"AddressFamily", VAR_SERVER | VAR_SAFE},
1733 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1734 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1735 {"BindToInterface", VAR_SERVER},
1736 {"Broadcast", VAR_SERVER | VAR_SAFE},
1737 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1738 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1739 {"DecrementTTL", VAR_SERVER | VAR_SAFE},
1740 {"Device", VAR_SERVER},
1741 {"DeviceStandby", VAR_SERVER},
1742 {"DeviceType", VAR_SERVER},
1743 {"DirectOnly", VAR_SERVER | VAR_SAFE},
1744 {"Ed25519PrivateKeyFile", VAR_SERVER},
1745 {"ExperimentalProtocol", VAR_SERVER},
1746 {"Forwarding", VAR_SERVER},
1747 {"FWMark", VAR_SERVER},
1748 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1749 {"Hostnames", VAR_SERVER},
1750 {"IffOneQueue", VAR_SERVER},
1751 {"Interface", VAR_SERVER},
1752 {"InvitationExpire", VAR_SERVER},
1753 {"KeyExpire", VAR_SERVER | VAR_SAFE},
1754 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1755 {"LocalDiscovery", VAR_SERVER | VAR_SAFE},
1756 {"LogLevel", VAR_SERVER},
1757 {"MACExpire", VAR_SERVER | VAR_SAFE},
1758 {"MaxConnectionBurst", VAR_SERVER | VAR_SAFE},
1759 {"MaxOutputBufferSize", VAR_SERVER | VAR_SAFE},
1760 {"MaxTimeout", VAR_SERVER | VAR_SAFE},
1761 {"Mode", VAR_SERVER | VAR_SAFE},
1762 {"Name", VAR_SERVER},
1763 {"PingInterval", VAR_SERVER | VAR_SAFE},
1764 {"PingTimeout", VAR_SERVER | VAR_SAFE},
1765 {"PriorityInheritance", VAR_SERVER},
1766 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1767 {"PrivateKeyFile", VAR_SERVER},
1768 {"ProcessPriority", VAR_SERVER},
1769 {"Proxy", VAR_SERVER},
1770 {"ReplayWindow", VAR_SERVER | VAR_SAFE},
1771 {"ScriptsExtension", VAR_SERVER},
1772 {"ScriptsInterpreter", VAR_SERVER},
1773 {"StrictSubnets", VAR_SERVER | VAR_SAFE},
1774 {"TunnelServer", VAR_SERVER | VAR_SAFE},
1775 {"UDPDiscovery", VAR_SERVER | VAR_SAFE},
1776 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER | VAR_SAFE},
1777 {"UDPDiscoveryInterval", VAR_SERVER | VAR_SAFE},
1778 {"UDPDiscoveryTimeout", VAR_SERVER | VAR_SAFE},
1779 {"MTUInfoInterval", VAR_SERVER | VAR_SAFE},
1780 {"UDPInfoInterval", VAR_SERVER | VAR_SAFE},
1781 {"UDPRcvBuf", VAR_SERVER},
1782 {"UDPSndBuf", VAR_SERVER},
1783 {"UPnP", VAR_SERVER},
1784 {"UPnPDiscoverWait", VAR_SERVER},
1785 {"UPnPRefreshPeriod", VAR_SERVER},
1786 {"VDEGroup", VAR_SERVER},
1787 {"VDEPort", VAR_SERVER},
1788 /* Host configuration */
1789 {"Address", VAR_HOST | VAR_MULTIPLE},
1790 {"Cipher", VAR_SERVER | VAR_HOST},
1791 {"ClampMSS", VAR_SERVER | VAR_HOST | VAR_SAFE},
1792 {"Compression", VAR_SERVER | VAR_HOST | VAR_SAFE},
1793 {"Digest", VAR_SERVER | VAR_HOST},
1794 {"Ed25519PublicKey", VAR_HOST},
1795 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1796 {"IndirectData", VAR_SERVER | VAR_HOST | VAR_SAFE},
1797 {"MACLength", VAR_SERVER | VAR_HOST},
1798 {"PMTU", VAR_SERVER | VAR_HOST},
1799 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1801 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1802 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1803 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1804 {"TCPOnly", VAR_SERVER | VAR_HOST | VAR_SAFE},
1805 {"Weight", VAR_HOST | VAR_SAFE},
1809 static int cmd_config(int argc, char *argv[]) {
1811 fprintf(stderr, "Invalid number of arguments.\n");
1815 if(strcasecmp(argv[0], "config")) {
1821 if(!strcasecmp(argv[1], "get")) {
1823 } else if(!strcasecmp(argv[1], "add")) {
1824 argv++, argc--, action = 1;
1825 } else if(!strcasecmp(argv[1], "del")) {
1826 argv++, argc--, action = -1;
1827 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1828 argv++, argc--, action = 0;
1832 fprintf(stderr, "Invalid number of arguments.\n");
1836 // Concatenate the rest of the command line
1837 strncpy(line, argv[1], sizeof(line) - 1);
1839 for(int i = 2; i < argc; i++) {
1840 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1841 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1844 // Liberal parsing into node name, variable name and value.
1850 len = strcspn(line, "\t =");
1852 value += strspn(value, "\t ");
1856 value += strspn(value, "\t ");
1860 variable = strchr(line, '.');
1870 fprintf(stderr, "No variable given.\n");
1874 if(action >= 0 && !*value) {
1875 fprintf(stderr, "No value for variable given.\n");
1879 if(action < -1 && *value) {
1883 /* Some simple checks. */
1885 bool warnonremove = false;
1887 for(int i = 0; variables[i].name; i++) {
1888 if(strcasecmp(variables[i].name, variable)) {
1893 variable = (char *)variables[i].name;
1895 if(!strcasecmp(variable, "Subnet")) {
1898 if(!str2net(&s, value)) {
1899 fprintf(stderr, "Malformed subnet definition %s\n", value);
1902 if(!subnetcheck(s)) {
1903 fprintf(stderr, "Network address and prefix length do not match: %s\n", value);
1908 /* Discourage use of obsolete variables. */
1910 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1912 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1914 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1919 /* Don't put server variables in host config files */
1921 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1923 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1925 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1930 /* Should this go into our own host config file? */
1932 if(!node && !(variables[i].type & VAR_SERVER)) {
1933 node = get_my_name(true);
1940 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1941 Turn on warnings when it seems variables might be removed unintentionally. */
1943 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1944 warnonremove = true;
1946 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1947 warnonremove = true;
1953 if(node && !check_id(node)) {
1954 fprintf(stderr, "Invalid name for node.\n");
1959 if(force || action < 0) {
1960 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1962 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1967 // Open the right configuration file.
1968 char filename[PATH_MAX];
1971 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1973 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1976 FILE *f = fopen(filename, "r");
1979 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1983 char tmpfile[PATH_MAX];
1987 if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
1988 fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
1992 tf = fopen(tmpfile, "w");
1995 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
2001 // Copy the file, making modifications on the fly, unless we are just getting a value.
2005 bool removed = false;
2008 while(fgets(buf1, sizeof(buf1), f)) {
2009 buf1[sizeof(buf1) - 1] = 0;
2010 strncpy(buf2, buf1, sizeof(buf2));
2012 // Parse line in a simple way
2016 len = strcspn(buf2, "\t =");
2017 bvalue = buf2 + len;
2018 bvalue += strspn(bvalue, "\t ");
2020 if(*bvalue == '=') {
2022 bvalue += strspn(bvalue, "\t ");
2029 if(!strcasecmp(buf2, variable)) {
2033 printf("%s\n", bvalue);
2035 } else if(action == -1) {
2036 if(!*value || !strcasecmp(bvalue, value)) {
2042 } else if(action == 0) {
2043 // Warn if "set" was used for variables that can occur multiple times
2044 if(warnonremove && strcasecmp(bvalue, value)) {
2045 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
2048 // Already set? Delete the rest...
2053 // Otherwise, replace.
2054 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2055 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2062 } else if(action > 0) {
2063 // Check if we've already seen this variable with the same value
2064 if(!strcasecmp(bvalue, value)) {
2071 // Copy original line...
2072 if(fputs(buf1, tf) < 0) {
2073 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2077 // Add newline if it is missing...
2078 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
2079 if(fputc('\n', tf) < 0) {
2080 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2087 // Make sure we read everything...
2088 if(ferror(f) || !feof(f)) {
2089 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2094 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2098 // Add new variable if necessary.
2099 if((action > 0 && !found) || (action == 0 && !set)) {
2100 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2101 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2110 fprintf(stderr, "No matching configuration variables found.\n");
2115 // Make sure we wrote everything...
2117 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2121 // Could we find what we had to remove?
2122 if(action < 0 && !removed) {
2124 fprintf(stderr, "No configuration variables deleted.\n");
2128 // Replace the configuration file with the new one
2131 if(remove(filename)) {
2132 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2138 if(rename(tmpfile, filename)) {
2139 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2143 // Silently try notifying a running tincd of changes.
2144 if(connect_tincd(false)) {
2145 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2151 static bool try_bind(int port) {
2152 struct addrinfo *ai = NULL, *aip;
2153 struct addrinfo hint = {
2154 .ai_flags = AI_PASSIVE,
2155 .ai_family = AF_UNSPEC,
2156 .ai_socktype = SOCK_STREAM,
2157 .ai_protocol = IPPROTO_TCP,
2160 bool success = true;
2162 snprintf(portstr, sizeof(portstr), "%d", port);
2164 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2168 for(aip = ai; aip; aip = aip->ai_next) {
2169 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2176 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2189 int check_port(const char *name) {
2194 fprintf(stderr, "Warning: could not bind to port 655. ");
2196 for(int i = 0; i < 100; i++) {
2197 int port = 0x1000 + (rand() & 0x7fff);
2199 if(try_bind(port)) {
2200 char filename[PATH_MAX];
2201 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2202 FILE *f = fopen(filename, "a");
2205 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2206 fprintf(stderr, "Please change tinc's Port manually.\n");
2210 fprintf(f, "Port = %d\n", port);
2212 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2217 fprintf(stderr, "Please change tinc's Port manually.\n");
2221 static int cmd_init(int argc, char *argv[]) {
2222 if(!access(tinc_conf, F_OK)) {
2223 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2228 fprintf(stderr, "Too many arguments!\n");
2230 } else if(argc < 2) {
2233 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2235 if(!fgets(buf, sizeof(buf), stdin)) {
2236 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2240 int len = rstrip(buf);
2243 fprintf(stderr, "No name given!\n");
2249 fprintf(stderr, "No Name given!\n");
2253 name = strdup(argv[1]);
2256 fprintf(stderr, "No Name given!\n");
2261 if(!check_id(name)) {
2262 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2266 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2267 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2271 if(mkdir(confbase, 0777) && errno != EEXIST) {
2272 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2276 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2277 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2281 FILE *f = fopen(tinc_conf, "w");
2284 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2288 fprintf(f, "Name = %s\n", name);
2291 #ifndef DISABLE_LEGACY
2293 if(!rsa_keygen(2048, false)) {
2299 if(!ed25519_keygen(false)) {
2306 char filename[PATH_MAX];
2307 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2309 if(access(filename, F_OK)) {
2310 FILE *f = fopenmask(filename, "w", 0777);
2313 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2317 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");
2327 static int cmd_generate_keys(int argc, char *argv[]) {
2328 #ifdef DISABLE_LEGACY
2336 fprintf(stderr, "Too many arguments!\n");
2341 name = get_my_name(false);
2344 #ifndef DISABLE_LEGACY
2346 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2352 if(!ed25519_keygen(true)) {
2359 #ifndef DISABLE_LEGACY
2360 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2362 fprintf(stderr, "Too many arguments!\n");
2367 name = get_my_name(false);
2370 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2374 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2378 fprintf(stderr, "Too many arguments!\n");
2383 name = get_my_name(false);
2386 return !ed25519_keygen(true);
2389 static int cmd_help(int argc, char *argv[]) {
2397 static int cmd_version(int argc, char *argv[]) {
2401 fprintf(stderr, "Too many arguments!\n");
2409 static int cmd_info(int argc, char *argv[]) {
2411 fprintf(stderr, "Invalid number of arguments.\n");
2415 if(!connect_tincd(true)) {
2419 return info(fd, argv[1]);
2422 static const char *conffiles[] = {
2433 static int cmd_edit(int argc, char *argv[]) {
2435 fprintf(stderr, "Invalid number of arguments.\n");
2439 char filename[PATH_MAX] = "";
2441 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2442 for(int i = 0; conffiles[i]; i++) {
2443 if(!strcmp(argv[1], conffiles[i])) {
2444 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2453 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2454 char *dash = strchr(argv[1], '-');
2459 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2460 fprintf(stderr, "Invalid configuration filename.\n");
2468 const char *editor = getenv("VISUAL");
2471 editor = getenv("EDITOR");
2478 xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
2480 xasprintf(&command, "edit \"%s\"", filename);
2482 int result = system(command);
2489 // Silently try notifying a running tincd of changes.
2490 if(connect_tincd(false)) {
2491 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2497 static int export(const char *name, FILE *out) {
2498 char filename[PATH_MAX];
2499 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2500 FILE *in = fopen(filename, "r");
2503 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2507 fprintf(out, "Name = %s\n", name);
2510 while(fgets(buf, sizeof(buf), in)) {
2511 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2517 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2526 static int cmd_export(int argc, char *argv[]) {
2530 fprintf(stderr, "Too many arguments!\n");
2534 char *name = get_my_name(true);
2540 int result = export(name, stdout);
2550 static int cmd_export_all(int argc, char *argv[]) {
2554 fprintf(stderr, "Too many arguments!\n");
2558 DIR *dir = opendir(hosts_dir);
2561 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2569 while((ent = readdir(dir))) {
2570 if(!check_id(ent->d_name)) {
2577 printf("#---------------------------------------------------------------#\n");
2580 result |= export(ent->d_name, stdout);
2592 static int cmd_import(int argc, char *argv[]) {
2596 fprintf(stderr, "Too many arguments!\n");
2605 char filename[PATH_MAX] = "";
2607 bool firstline = true;
2609 while(fgets(buf, sizeof(buf), in)) {
2610 if(sscanf(buf, "Name = %4095s", name) == 1) {
2613 if(!check_id(name)) {
2614 fprintf(stderr, "Invalid Name in input!\n");
2622 if((size_t)snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name) >= sizeof(filename)) {
2623 fprintf(stderr, "Filename too long: %s" SLASH "%s\n", hosts_dir, name);
2627 if(!force && !access(filename, F_OK)) {
2628 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2633 out = fopen(filename, "w");
2636 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2642 } else if(firstline) {
2643 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2648 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2653 if(fputs(buf, out) < 0) {
2654 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2665 fprintf(stderr, "Imported %d host configuration files.\n", count);
2668 fprintf(stderr, "No host configuration files imported.\n");
2673 static int cmd_exchange(int argc, char *argv[]) {
2674 return cmd_export(argc, argv) ? 1 : cmd_import(argc, argv);
2677 static int cmd_exchange_all(int argc, char *argv[]) {
2678 return cmd_export_all(argc, argv) ? 1 : cmd_import(argc, argv);
2681 static int switch_network(char *name) {
2682 if(strcmp(name, ".")) {
2683 if(!check_netname(name, false)) {
2684 fprintf(stderr, "Invalid character in netname!\n");
2688 if(!check_netname(name, true)) {
2689 fprintf(stderr, "Warning: unsafe character in netname!\n");
2699 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2706 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2707 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2708 xasprintf(&prompt, "%s> ", identname);
2713 static int cmd_network(int argc, char *argv[]) {
2715 fprintf(stderr, "Too many arguments!\n");
2720 return switch_network(argv[1]);
2723 DIR *dir = opendir(confdir);
2726 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2732 while((ent = readdir(dir))) {
2733 if(*ent->d_name == '.') {
2737 if(!strcmp(ent->d_name, "tinc.conf")) {
2742 char fname[PATH_MAX];
2743 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2745 if(!access(fname, R_OK)) {
2746 printf("%s\n", ent->d_name);
2755 static int cmd_fsck(int argc, char *argv[]) {
2759 fprintf(stderr, "Too many arguments!\n");
2763 return fsck(orig_argv[0]);
2766 static void *readfile(FILE *in, size_t *len) {
2768 size_t bufsize = 4096;
2769 char *buf = xmalloc(bufsize);
2772 size_t read = fread(buf + count, 1, bufsize - count, in);
2780 if(count >= bufsize) {
2782 buf = xrealloc(buf, bufsize);
2793 static int cmd_sign(int argc, char *argv[]) {
2795 fprintf(stderr, "Too many arguments!\n");
2800 name = get_my_name(true);
2807 char fname[PATH_MAX];
2808 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2809 FILE *fp = fopen(fname, "r");
2812 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2816 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2819 fprintf(stderr, "Could not read private key from %s\n", fname);
2829 in = fopen(argv[1], "rb");
2832 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2841 char *data = readfile(in, &len);
2848 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2853 // Ensure we sign our name and current time as well
2854 long t = time(NULL);
2856 xasprintf(&trailer, " %s %ld", name, t);
2857 int trailer_len = strlen(trailer);
2859 data = xrealloc(data, len + trailer_len);
2860 memcpy(data + len, trailer, trailer_len);
2865 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2866 fprintf(stderr, "Error generating signature\n");
2872 b64encode(sig, sig, 64);
2875 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2876 fwrite(data, len, 1, stdout);
2882 static int cmd_verify(int argc, char *argv[]) {
2884 fprintf(stderr, "Not enough arguments!\n");
2889 fprintf(stderr, "Too many arguments!\n");
2893 char *node = argv[1];
2895 if(!strcmp(node, ".")) {
2897 name = get_my_name(true);
2905 } else if(!strcmp(node, "*")) {
2908 if(!check_id(node)) {
2909 fprintf(stderr, "Invalid node name\n");
2917 in = fopen(argv[2], "rb");
2920 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2928 char *data = readfile(in, &len);
2935 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2939 char *newline = memchr(data, '\n', len);
2941 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2942 fprintf(stderr, "Invalid input\n");
2948 size_t skip = newline - data;
2950 char signer[MAX_STRING_SIZE] = "";
2951 char sig[MAX_STRING_SIZE] = "";
2954 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2955 fprintf(stderr, "Invalid input\n");
2960 if(node && strcmp(node, signer)) {
2961 fprintf(stderr, "Signature is not made by %s\n", node);
2971 xasprintf(&trailer, " %s %ld", signer, t);
2972 int trailer_len = strlen(trailer);
2974 data = xrealloc(data, len + trailer_len);
2975 memcpy(data + len, trailer, trailer_len);
2978 newline = data + skip;
2980 char fname[PATH_MAX];
2981 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2982 FILE *fp = fopen(fname, "r");
2985 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2990 ecdsa_t *key = get_pubkey(fp);
2994 key = ecdsa_read_pem_public_key(fp);
2998 fprintf(stderr, "Could not read public key from %s\n", fname);
3006 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
3007 fprintf(stderr, "Invalid signature\n");
3015 fwrite(newline, len - (newline - data), 1, stdout);
3021 static const struct {
3022 const char *command;
3023 int (*function)(int argc, char *argv[]);
3026 {"start", cmd_start, false},
3027 {"stop", cmd_stop, false},
3028 {"restart", cmd_restart, false},
3029 {"reload", cmd_reload, false},
3030 {"dump", cmd_dump, false},
3031 {"list", cmd_dump, false},
3032 {"purge", cmd_purge, false},
3033 {"debug", cmd_debug, false},
3034 {"retry", cmd_retry, false},
3035 {"connect", cmd_connect, false},
3036 {"disconnect", cmd_disconnect, false},
3037 {"top", cmd_top, false},
3038 {"pcap", cmd_pcap, false},
3039 {"log", cmd_log, false},
3040 {"pid", cmd_pid, false},
3041 {"config", cmd_config, true},
3042 {"add", cmd_config, false},
3043 {"del", cmd_config, false},
3044 {"get", cmd_config, false},
3045 {"set", cmd_config, false},
3046 {"init", cmd_init, false},
3047 {"generate-keys", cmd_generate_keys, false},
3048 #ifndef DISABLE_LEGACY
3049 {"generate-rsa-keys", cmd_generate_rsa_keys, false},
3051 {"generate-ed25519-keys", cmd_generate_ed25519_keys, false},
3052 {"help", cmd_help, false},
3053 {"version", cmd_version, false},
3054 {"info", cmd_info, false},
3055 {"edit", cmd_edit, false},
3056 {"export", cmd_export, false},
3057 {"export-all", cmd_export_all, false},
3058 {"import", cmd_import, false},
3059 {"exchange", cmd_exchange, false},
3060 {"exchange-all", cmd_exchange_all, false},
3061 {"invite", cmd_invite, false},
3062 {"join", cmd_join, false},
3063 {"network", cmd_network, false},
3064 {"fsck", cmd_fsck, false},
3065 {"sign", cmd_sign, false},
3066 {"verify", cmd_verify, false},
3067 {NULL, NULL, false},
3070 #ifdef HAVE_READLINE
3071 static char *complete_command(const char *text, int state) {
3080 while(commands[i].command) {
3081 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
3082 return xstrdup(commands[i].command);
3091 static char *complete_dump(const char *text, int state) {
3092 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
3102 if(!strncasecmp(matches[i], text, strlen(text))) {
3103 return xstrdup(matches[i]);
3112 static char *complete_config(const char *text, int state) {
3121 while(variables[i].name) {
3122 char *dot = strchr(text, '.');
3125 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3127 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3131 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3132 return xstrdup(variables[i].name);
3142 static char *complete_info(const char *text, int state) {
3148 if(!connect_tincd(false)) {
3152 // Check the list of nodes
3153 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3154 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3157 while(recvline(fd, line, sizeof(line))) {
3159 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3172 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3176 if(!strncmp(item, text, strlen(text))) {
3177 return xstrdup(strip_weight(item));
3184 static char *complete_nothing(const char *text, int state) {
3190 static char **completion(const char *text, int start, int end) {
3192 char **matches = NULL;
3195 matches = rl_completion_matches(text, complete_command);
3196 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3197 matches = rl_completion_matches(text, complete_dump);
3198 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3199 matches = rl_completion_matches(text, complete_config);
3200 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3201 matches = rl_completion_matches(text, complete_config);
3202 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3203 matches = rl_completion_matches(text, complete_config);
3204 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3205 matches = rl_completion_matches(text, complete_config);
3206 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3207 matches = rl_completion_matches(text, complete_info);
3214 static int cmd_shell(int argc, char *argv[]) {
3215 xasprintf(&prompt, "%s> ", identname);
3219 int maxargs = argc + 16;
3220 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3222 for(int i = 0; i < argc; i++) {
3226 #ifdef HAVE_READLINE
3227 rl_readline_name = "tinc";
3228 rl_completion_entry_function = complete_nothing;
3229 rl_attempted_completion_function = completion;
3230 rl_filename_completion_desired = 0;
3235 #ifdef HAVE_READLINE
3240 rl_basic_word_break_characters = "\t\n ";
3241 line = readline(prompt);
3242 copy = line ? xstrdup(line) : NULL;
3244 line = fgets(buf, sizeof(buf), stdin);
3250 fputs(prompt, stdout);
3253 line = fgets(buf, sizeof(buf), stdin);
3260 /* Ignore comments */
3269 char *p = line + strspn(line, " \t\n");
3270 char *next = strtok(p, " \t\n");
3273 if(nargc >= maxargs) {
3275 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3280 next = strtok(NULL, " \t\n");
3287 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3288 #ifdef HAVE_READLINE
3297 for(int i = 0; commands[i].command; i++) {
3298 if(!strcasecmp(nargv[argc], commands[i].command)) {
3299 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3305 #ifdef HAVE_READLINE
3314 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3319 #ifdef HAVE_READLINE
3332 int main(int argc, char *argv[]) {
3333 program_name = argv[0];
3336 tty = isatty(0) && isatty(1);
3338 if(!parse_options(argc, argv)) {
3343 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3344 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3357 static struct WSAData wsa_state;
3359 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3360 fprintf(stderr, "System call `%s' failed: %s\n", "WSAStartup", winerror(GetLastError()));
3369 if(optind >= argc) {
3370 return cmd_shell(argc, argv);
3373 for(int i = 0; commands[i].command; i++) {
3374 if(!strcasecmp(argv[optind], commands[i].command)) {
3375 return commands[i].function(argc - optind, argv + optind);
3379 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);