2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2017 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"
45 #define MSG_NOSIGNAL 0
48 static char **orig_argv;
51 /* If nonzero, display usage information and exit. */
52 static bool show_help = false;
54 /* If nonzero, print the version on standard output and exit. */
55 static bool show_version = false;
57 static char *name = NULL;
58 static char controlcookie[1025];
59 char *tinc_conf = NULL;
60 char *hosts_dir = NULL;
63 // Horrible global variables...
72 bool confbasegiven = false;
73 bool netnamegiven = false;
74 char *scriptinterpreter = NULL;
75 char *scriptextension = "";
81 static struct option const long_options[] = {
82 {"batch", no_argument, NULL, 'b'},
83 {"config", required_argument, NULL, 'c'},
84 {"net", required_argument, NULL, 'n'},
85 {"help", no_argument, NULL, 1},
86 {"version", no_argument, NULL, 2},
87 {"pidfile", required_argument, NULL, 3},
88 {"force", no_argument, NULL, 4},
92 static void version(void) {
93 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
94 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
95 printf("Copyright (C) 1998-2017 Ivo Timmermans, Guus Sliepen and others.\n"
96 "See the AUTHORS file for a complete list.\n\n"
97 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
98 "and you are welcome to redistribute it under certain conditions;\n"
99 "see the file COPYING for details.\n");
102 static void usage(bool status) {
104 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
106 printf("Usage: %s [options] command\n\n", program_name);
107 printf("Valid options are:\n"
108 " -b, --batch Don't ask for anything (non-interactive mode).\n"
109 " -c, --config=DIR Read configuration options from DIR.\n"
110 " -n, --net=NETNAME Connect to net NETNAME.\n"
111 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
112 " --force Force some commands to work despite warnings.\n"
113 " --help Display this help and exit.\n"
114 " --version Output version information and exit.\n"
116 "Valid commands are:\n"
117 " init [name] Create initial configuration files.\n"
118 " get VARIABLE Print current value of VARIABLE\n"
119 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
120 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
121 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
122 " start [tincd options] Start tincd.\n"
123 " stop Stop tincd.\n"
124 " restart [tincd options] Restart tincd.\n"
125 " reload Partially reload configuration of running tincd.\n"
126 " pid Show PID of currently running tincd.\n"
127 #ifdef DISABLE_LEGACY
128 " generate-keys Generate a new Ed25519 public/private keypair.\n"
130 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
131 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
133 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
134 " dump Dump a list of one of the following things:\n"
135 " [reachable] nodes - all known nodes in the VPN\n"
136 " edges - all known connections in the VPN\n"
137 " subnets - all known subnets in the VPN\n"
138 " connections - all meta connections with ourself\n"
139 " [di]graph - graph of the VPN in dotty format\n"
140 " invitations - outstanding invitations\n"
141 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
142 " purge Purge unreachable nodes\n"
143 " debug N Set debug level\n"
144 " retry Retry all outgoing connections\n"
145 " disconnect NODE Close meta connection with NODE\n"
147 " top Show real-time statistics\n"
149 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
150 " log [level] Dump log output [up to the specified level]\n"
151 " export Export host configuration of local node to standard output\n"
152 " export-all Export all host configuration files to standard output\n"
153 " import Import host configuration file(s) from standard input\n"
154 " exchange Same as export followed by import\n"
155 " exchange-all Same as export-all followed by import\n"
156 " invite NODE [...] Generate an invitation for NODE\n"
157 " join INVITATION Join a VPN using an INVITATION\n"
158 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
159 " fsck Check the configuration files for problems.\n"
160 " sign [FILE] Generate a signed version of a file.\n"
161 " verify NODE [FILE] Verify that a file was signed by the given NODE.\n"
163 printf("Report bugs to tinc@tinc-vpn.org.\n");
167 static bool parse_options(int argc, char **argv) {
169 int option_index = 0;
171 while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
173 case 0: /* long option */
180 case 'c': /* config file */
181 confbase = xstrdup(optarg);
182 confbasegiven = true;
185 case 'n': /* net name given */
186 netname = xstrdup(optarg);
189 case 1: /* show help */
193 case 2: /* show version */
197 case 3: /* open control socket here */
198 pidfilename = xstrdup(optarg);
205 case '?': /* wrong options */
214 if(!netname && (netname = getenv("NETNAME"))) {
215 netname = xstrdup(netname);
218 /* netname "." is special: a "top-level name" */
220 if(netname && (!*netname || !strcmp(netname, "."))) {
225 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
226 fprintf(stderr, "Invalid character in netname!\n");
233 /* Open a file with the desired permissions, minus the umask.
234 Also, if we want to create an executable file, we call fchmod()
235 to set the executable bits. */
237 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
238 mode_t mask = umask(0);
241 FILE *f = fopen(filename, mode);
244 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
250 if((perms & 0444) && f) {
251 fchmod(fileno(f), perms);
259 static void disable_old_keys(const char *filename, const char *what) {
260 char tmpfile[PATH_MAX] = "";
262 bool disabled = false;
267 r = fopen(filename, "r");
273 snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
275 struct stat st = {.st_mode = 0600};
276 fstat(fileno(r), &st);
277 w = fopenmask(tmpfile, "w", st.st_mode);
279 while(fgets(buf, sizeof(buf), r)) {
280 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
281 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
287 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
294 if(block || ed25519pubkey) {
298 if(fputs(buf, w) < 0) {
304 if(block && !strncmp(buf, "-----END ", 9)) {
314 if(ferror(r) || fclose(r) < 0) {
320 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
330 // We cannot atomically replace files on Windows.
331 char bakfile[PATH_MAX] = "";
332 snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
334 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
335 rename(bakfile, filename);
338 if(rename(tmpfile, filename)) {
340 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
345 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
352 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
354 char directory[PATH_MAX] = ".";
358 /* Check stdin and stdout */
360 /* Ask for a file and/or directory name. */
361 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
363 if(fgets(buf, sizeof(buf), stdin) == NULL) {
364 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
368 size_t len = strlen(buf);
381 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
384 if(filename[0] != '/') {
386 /* The directory is a relative path or a filename. */
387 getcwd(directory, sizeof(directory));
388 snprintf(buf2, sizeof(buf2), "%s" SLASH "%s", directory, filename);
392 disable_old_keys(filename, what);
394 /* Open it first to keep the inode busy */
396 r = fopenmask(filename, mode, perms);
399 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
407 Generate a public/private Ed25519 keypair, and ask for a file to store
410 static bool ed25519_keygen(bool ask) {
413 char fname[PATH_MAX];
415 fprintf(stderr, "Generating Ed25519 keypair:\n");
417 if(!(key = ecdsa_generate())) {
418 fprintf(stderr, "Error during key generation!\n");
421 fprintf(stderr, "Done.\n");
424 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
425 f = ask_and_open(fname, "private Ed25519 key", "a", ask, 0600);
431 if(!ecdsa_write_pem_private_key(key, f)) {
432 fprintf(stderr, "Error writing private key!\n");
439 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
441 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.pub", confbase);
444 f = ask_and_open(fname, "public Ed25519 key", "a", ask, 0666);
450 char *pubkey = ecdsa_get_base64_public_key(key);
451 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
469 #ifndef DISABLE_LEGACY
471 Generate a public/private RSA keypair, and ask for a file to store
474 static bool rsa_keygen(int bits, bool ask) {
477 char fname[PATH_MAX];
479 // Make sure the key size is a multiple of 8 bits.
482 // Make sure that a valid key size is used.
483 if(bits < 1024 || bits > 8192) {
484 fprintf(stderr, "Invalid key size %d specified! It should be between 1024 and 8192 bits.\n", bits);
486 } else if(bits < 2048) {
487 fprintf(stderr, "WARNING: generating a weak %d bits RSA key! 2048 or more bits are recommended.\n", bits);
490 fprintf(stderr, "Generating %d bits keys:\n", bits);
492 if(!(key = rsa_generate(bits, 0x10001))) {
493 fprintf(stderr, "Error during key generation!\n");
496 fprintf(stderr, "Done.\n");
499 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.priv", confbase);
500 f = ask_and_open(fname, "private RSA key", "a", ask, 0600);
506 if(!rsa_write_pem_private_key(key, f)) {
507 fprintf(stderr, "Error writing private key!\n");
514 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, name);
516 snprintf(fname, sizeof(fname), "%s" SLASH "rsa_key.pub", confbase);
519 f = ask_and_open(fname, "public RSA key", "a", ask, 0666);
525 if(!rsa_write_pem_public_key(key, f)) {
526 fprintf(stderr, "Error writing public key!\n");
549 bool recvline(int fd, char *line, size_t len) {
550 char *newline = NULL;
556 while(!(newline = memchr(buffer, '\n', blen))) {
557 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
559 if(result == -1 && sockerrno == EINTR) {
561 } else if(result <= 0) {
568 if(newline - buffer >= len) {
572 len = newline - buffer;
574 memcpy(line, buffer, len);
576 memmove(buffer, newline + 1, blen - len - 1);
582 bool recvdata(int fd, char *data, size_t len) {
588 int result = recv(fd, buffer + blen, sizeof(buffer) - blen, 0);
590 if(result == -1 && sockerrno == EINTR) {
592 } else if(result <= 0) {
599 memcpy(data, buffer, len);
600 memmove(buffer, buffer + len, blen - len);
606 bool sendline(int fd, char *format, ...) {
607 static char buffer[4096];
612 va_start(ap, format);
613 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
614 buffer[sizeof(buffer) - 1] = 0;
617 if(blen < 1 || blen >= sizeof(buffer)) {
625 int result = send(fd, p, blen, MSG_NOSIGNAL);
627 if(result == -1 && sockerrno == EINTR) {
629 } else if(result <= 0) {
640 static void pcap(int fd, FILE *out, int snaplen) {
641 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
649 uint32_t tz_accuracy;
656 snaplen ? : sizeof(data),
669 fwrite(&header, sizeof(header), 1, out);
674 while(recvline(fd, line, sizeof(line))) {
676 int n = sscanf(line, "%d %d %d", &code, &req, &len);
677 gettimeofday(&tv, NULL);
679 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof(data)) {
683 if(!recvdata(fd, data, len)) {
687 packet.tv_sec = tv.tv_sec;
688 packet.tv_usec = tv.tv_usec;
690 packet.origlen = len;
691 fwrite(&packet, sizeof(packet), 1, out);
692 fwrite(data, len, 1, out);
697 static void logcontrol(int fd, FILE *out, int level) {
698 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
702 while(recvline(fd, line, sizeof(line))) {
704 int n = sscanf(line, "%d %d %d", &code, &req, &len);
706 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof(data)) {
710 if(!recvdata(fd, data, len)) {
714 fwrite(data, len, 1, out);
721 static bool remove_service(void) {
722 SC_HANDLE manager = NULL;
723 SC_HANDLE service = NULL;
724 SERVICE_STATUS status = {0};
726 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
729 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
733 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
736 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
740 if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
741 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
743 fprintf(stderr, "%s service stopped\n", identname);
746 if(!DeleteService(service)) {
747 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
751 fprintf(stderr, "%s service removed\n", identname);
757 bool connect_tincd(bool verbose) {
762 struct timeval tv = {0, 0};
764 if(select(fd + 1, &r, NULL, NULL, &tv)) {
765 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
773 FILE *f = fopen(pidfilename, "r");
777 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
786 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
788 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
799 if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
800 fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
801 /* clean up the stale socket and pid file */
803 unlink(unixsocketname);
807 struct sockaddr_un sa;
809 sa.sun_family = AF_UNIX;
811 strncpy(sa.sun_path, unixsocketname, sizeof(sa.sun_path));
813 fd = socket(AF_UNIX, SOCK_STREAM, 0);
817 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
823 if(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
825 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
834 struct addrinfo hints = {
835 .ai_family = AF_UNSPEC,
836 .ai_socktype = SOCK_STREAM,
837 .ai_protocol = IPPROTO_TCP,
841 struct addrinfo *res = NULL;
843 if(getaddrinfo(host, port, &hints, &res) || !res) {
845 fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
851 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
855 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
862 unsigned long arg = 0;
864 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
866 fprintf(stderr, "System call `%s' failed: %s\n", "ioctlsocket", sockstrerror(sockerrno));
872 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
874 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
886 static const int one = 1;
887 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
893 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %4095s %d", &code, data, &version) != 3 || code != 0) {
895 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
903 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
905 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
907 fprintf(stderr, "Could not fully establish control socket connection\n");
919 static int cmd_start(int argc, char *argv[]) {
920 if(connect_tincd(false)) {
922 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
924 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
931 char *slash = strrchr(program_name, '/');
935 if((c = strrchr(program_name, '\\')) > slash) {
942 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
948 char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
953 Windows has no real concept of an "argv array". A command line is just one string.
954 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
955 it uses quotes to handle spaces in arguments.
956 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
957 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
958 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
960 xasprintf(&arg0, "\"%s\"", arg0);
962 nargv[nargc++] = arg0;
964 for(int i = 1; i < optind; i++) {
965 nargv[nargc++] = orig_argv[i];
968 for(int i = 1; i < argc; i++) {
969 nargv[nargc++] = argv[i];
973 int status = spawnvp(_P_WAIT, c, nargv);
976 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
982 int pfd[2] = {-1, -1};
984 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pfd)) {
985 fprintf(stderr, "Could not create umbilical socket: %s\n", strerror(errno));
993 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
1001 snprintf(buf, sizeof(buf), "%d", pfd[1]);
1002 setenv("TINC_UMBILICAL", buf, true);
1003 exit(execvp(c, nargv));
1010 int status = -1, result;
1012 signal(SIGINT, SIG_IGN);
1015 // Pass all log messages from the umbilical to stderr.
1016 // A nul-byte right before closure means tincd started successfully.
1017 bool failure = true;
1021 while((len = read(pfd[0], buf, sizeof(buf))) > 0) {
1022 failure = buf[len - 1];
1037 // Make sure the child process is really gone.
1038 result = waitpid(pid, &status, 0);
1041 signal(SIGINT, SIG_DFL);
1044 if(failure || result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
1045 fprintf(stderr, "Error starting %s\n", c);
1053 static int cmd_stop(int argc, char *argv[]) {
1055 fprintf(stderr, "Too many arguments!\n");
1061 if(!connect_tincd(true)) {
1063 if(kill(pid, SIGTERM)) {
1064 fprintf(stderr, "Could not send TERM signal to process with PID %d: %s\n", pid, strerror(errno));
1068 fprintf(stderr, "Sent TERM signal to process with PID %d.\n", pid);
1069 waitpid(pid, NULL, 0);
1076 sendline(fd, "%d %d", CONTROL, REQ_STOP);
1078 while(recvline(fd, line, sizeof(line))) {
1079 // Wait for tincd to close the connection...
1084 if(!remove_service()) {
1096 static int cmd_restart(int argc, char *argv[]) {
1098 return cmd_start(argc, argv);
1101 static int cmd_reload(int argc, char *argv[]) {
1103 fprintf(stderr, "Too many arguments!\n");
1107 if(!connect_tincd(true)) {
1111 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1113 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
1114 fprintf(stderr, "Could not reload configuration.\n");
1122 static int dump_invitations(void) {
1123 char dname[PATH_MAX];
1124 snprintf(dname, sizeof(dname), "%s" SLASH "invitations", confbase);
1125 DIR *dir = opendir(dname);
1128 if(errno == ENOENT) {
1129 fprintf(stderr, "No outstanding invitations.\n");
1133 fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
1141 while((ent = readdir(dir))) {
1142 char buf[MAX_STRING_SIZE];
1144 if(b64decode(ent->d_name, buf, 24) != 18) {
1148 char fname[PATH_MAX];
1149 snprintf(fname, sizeof(fname), "%s" SLASH "%s", dname, ent->d_name);
1150 FILE *f = fopen(fname, "r");
1153 fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
1159 if(!fgets(buf, sizeof(buf), f)) {
1160 fprintf(stderr, "Invalid invitation file %s\n", fname);
1167 char *eol = buf + strlen(buf);
1169 while(strchr("\t \r\n", *--eol)) {
1173 if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
1174 fprintf(stderr, "Invalid invitation file %s\n", fname);
1179 printf("%s %s\n", ent->d_name, buf + 7);
1185 fprintf(stderr, "No outstanding invitations.\n");
1191 static int cmd_dump(int argc, char *argv[]) {
1192 bool only_reachable = false;
1194 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
1195 if(strcasecmp(argv[2], "nodes")) {
1196 fprintf(stderr, "`reachable' only supported for nodes.\n");
1201 only_reachable = true;
1207 fprintf(stderr, "Invalid number of arguments.\n");
1212 if(!strcasecmp(argv[1], "invitations")) {
1213 return dump_invitations();
1216 if(!connect_tincd(true)) {
1222 if(!strcasecmp(argv[1], "nodes")) {
1223 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1224 } else if(!strcasecmp(argv[1], "edges")) {
1225 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1226 } else if(!strcasecmp(argv[1], "subnets")) {
1227 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
1228 } else if(!strcasecmp(argv[1], "connections")) {
1229 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
1230 } else if(!strcasecmp(argv[1], "graph")) {
1231 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1232 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1234 } else if(!strcasecmp(argv[1], "digraph")) {
1235 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
1236 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
1239 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
1245 printf("graph {\n");
1246 } else if(do_graph == 2) {
1247 printf("digraph {\n");
1250 while(recvline(fd, line, sizeof(line))) {
1251 char node1[4096], node2[4096];
1252 int n = sscanf(line, "%d %d %4095s %4095s", &code, &req, node1, node2);
1255 if(do_graph && req == REQ_DUMP_NODES) {
1277 char local_host[4096];
1278 char local_port[4096];
1281 int cipher, digest, maclength, compression, distance, socket, weight;
1282 short int pmtu, minmtu, maxmtu;
1283 unsigned int options, status_int;
1284 node_status_t status;
1285 long int last_state_change;
1288 case REQ_DUMP_NODES: {
1289 int n = sscanf(line, "%*d %*d %4095s %4095s %4095s port %4095s %d %d %d %d %x %x %4095s %4095s %d %hd %hd %hd %ld", node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
1292 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1296 memcpy(&status, &status_int, sizeof(status));
1299 const char *color = "black";
1301 if(!strcmp(host, "MYSELF")) {
1303 } else if(!status.reachable) {
1305 } else if(strcmp(via, node)) {
1307 } else if(!status.validkey) {
1309 } else if(minmtu > 0) {
1313 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1315 if(only_reachable && !status.reachable) {
1319 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)\n",
1320 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1325 case REQ_DUMP_EDGES: {
1326 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);
1329 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1334 float w = 1 + 65536.0 / weight;
1336 if(do_graph == 1 && strcmp(node1, node2) > 0) {
1337 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1338 } else if(do_graph == 2) {
1339 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1342 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);
1347 case REQ_DUMP_SUBNETS: {
1348 int n = sscanf(line, "%*d %*d %4095s %4095s", subnet, node);
1351 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1355 printf("%s owner %s\n", strip_weight(subnet), node);
1359 case REQ_DUMP_CONNECTIONS: {
1360 int n = sscanf(line, "%*d %*d %4095s %4095s port %4095s %x %d %x", node, host, port, &options, &socket, &status_int);
1363 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1367 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1372 fprintf(stderr, "Unable to parse dump from tincd.\n");
1377 fprintf(stderr, "Error receiving dump.\n");
1381 static int cmd_purge(int argc, char *argv[]) {
1383 fprintf(stderr, "Too many arguments!\n");
1387 if(!connect_tincd(true)) {
1391 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1393 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1394 fprintf(stderr, "Could not purge old information.\n");
1401 static int cmd_debug(int argc, char *argv[]) {
1403 fprintf(stderr, "Invalid number of arguments.\n");
1407 if(!connect_tincd(true)) {
1411 int debuglevel = atoi(argv[1]);
1414 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1416 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1417 fprintf(stderr, "Could not set debug level.\n");
1421 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1425 static int cmd_retry(int argc, char *argv[]) {
1427 fprintf(stderr, "Too many arguments!\n");
1431 if(!connect_tincd(true)) {
1435 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1437 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1438 fprintf(stderr, "Could not retry outgoing connections.\n");
1445 static int cmd_connect(int argc, char *argv[]) {
1447 fprintf(stderr, "Invalid number of arguments.\n");
1451 if(!check_id(argv[1])) {
1452 fprintf(stderr, "Invalid name for node.\n");
1456 if(!connect_tincd(true)) {
1460 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1462 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1463 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1470 static int cmd_disconnect(int argc, char *argv[]) {
1472 fprintf(stderr, "Invalid number of arguments.\n");
1476 if(!check_id(argv[1])) {
1477 fprintf(stderr, "Invalid name for node.\n");
1481 if(!connect_tincd(true)) {
1485 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1487 if(!recvline(fd, line, sizeof(line)) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1488 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1495 static int cmd_top(int argc, char *argv[]) {
1497 fprintf(stderr, "Too many arguments!\n");
1503 if(!connect_tincd(true)) {
1510 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1515 static int cmd_pcap(int argc, char *argv[]) {
1517 fprintf(stderr, "Too many arguments!\n");
1521 if(!connect_tincd(true)) {
1525 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1530 static void sigint_handler(int sig) {
1531 fprintf(stderr, "\n");
1532 shutdown(fd, SHUT_RDWR);
1536 static int cmd_log(int argc, char *argv[]) {
1538 fprintf(stderr, "Too many arguments!\n");
1542 if(!connect_tincd(true)) {
1547 signal(SIGINT, sigint_handler);
1550 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1553 signal(SIGINT, SIG_DFL);
1561 static int cmd_pid(int argc, char *argv[]) {
1563 fprintf(stderr, "Too many arguments!\n");
1567 if(!connect_tincd(true) || !pid) {
1571 printf("%d\n", pid);
1575 int rstrip(char *value) {
1576 int len = strlen(value);
1578 while(len && strchr("\t\r\n ", value[len - 1])) {
1585 char *get_my_name(bool verbose) {
1586 FILE *f = fopen(tinc_conf, "r");
1590 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1599 while(fgets(buf, sizeof(buf), f)) {
1600 int len = strcspn(buf, "\t =");
1602 value += strspn(value, "\t ");
1606 value += strspn(value, "\t ");
1609 if(!rstrip(value)) {
1615 if(strcasecmp(buf, "Name")) {
1621 return replace_name(value);
1628 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1634 ecdsa_t *get_pubkey(FILE *f) {
1638 while(fgets(buf, sizeof(buf), f)) {
1639 int len = strcspn(buf, "\t =");
1641 value += strspn(value, "\t ");
1645 value += strspn(value, "\t ");
1648 if(!rstrip(value)) {
1654 if(strcasecmp(buf, "Ed25519PublicKey")) {
1659 return ecdsa_set_base64_public_key(value);
1666 const var_t variables[] = {
1667 /* Server configuration */
1668 {"AddressFamily", VAR_SERVER},
1669 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1670 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1671 {"BindToInterface", VAR_SERVER},
1672 {"Broadcast", VAR_SERVER | VAR_SAFE},
1673 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1674 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1675 {"DecrementTTL", VAR_SERVER},
1676 {"Device", VAR_SERVER},
1677 {"DeviceStandby", VAR_SERVER},
1678 {"DeviceType", VAR_SERVER},
1679 {"DirectOnly", VAR_SERVER},
1680 {"Ed25519PrivateKeyFile", VAR_SERVER},
1681 {"ExperimentalProtocol", VAR_SERVER},
1682 {"Forwarding", 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},
1689 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1690 {"LocalDiscovery", VAR_SERVER},
1691 {"LogLevel", VAR_SERVER},
1692 {"MACExpire", VAR_SERVER},
1693 {"MaxConnectionBurst", VAR_SERVER},
1694 {"MaxOutputBufferSize", VAR_SERVER},
1695 {"MaxTimeout", VAR_SERVER},
1696 {"Mode", VAR_SERVER | VAR_SAFE},
1697 {"Name", VAR_SERVER},
1698 {"PingInterval", VAR_SERVER},
1699 {"PingTimeout", VAR_SERVER},
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},
1706 {"ScriptsExtension", VAR_SERVER},
1707 {"ScriptsInterpreter", VAR_SERVER},
1708 {"StrictSubnets", VAR_SERVER},
1709 {"TunnelServer", VAR_SERVER},
1710 {"UDPDiscovery", VAR_SERVER},
1711 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1712 {"UDPDiscoveryInterval", VAR_SERVER},
1713 {"UDPDiscoveryTimeout", VAR_SERVER},
1714 {"MTUInfoInterval", VAR_SERVER},
1715 {"UDPInfoInterval", VAR_SERVER},
1716 {"UDPRcvBuf", VAR_SERVER},
1717 {"UDPSndBuf", VAR_SERVER},
1718 {"UPnP", VAR_SERVER},
1719 {"UPnPDiscoverWait", VAR_SERVER},
1720 {"UPnPRefreshPeriod", VAR_SERVER},
1721 {"VDEGroup", VAR_SERVER},
1722 {"VDEPort", VAR_SERVER},
1723 /* Host configuration */
1724 {"Address", VAR_HOST | VAR_MULTIPLE},
1725 {"Cipher", VAR_SERVER | VAR_HOST},
1726 {"ClampMSS", VAR_SERVER | VAR_HOST},
1727 {"Compression", VAR_SERVER | VAR_HOST},
1728 {"Digest", VAR_SERVER | VAR_HOST},
1729 {"Ed25519PublicKey", VAR_HOST},
1730 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1731 {"IndirectData", VAR_SERVER | VAR_HOST},
1732 {"MACLength", VAR_SERVER | VAR_HOST},
1733 {"PMTU", VAR_SERVER | VAR_HOST},
1734 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1736 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1737 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1738 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1739 {"TCPOnly", VAR_SERVER | VAR_HOST},
1740 {"Weight", VAR_HOST | VAR_SAFE},
1744 static int cmd_config(int argc, char *argv[]) {
1746 fprintf(stderr, "Invalid number of arguments.\n");
1750 if(strcasecmp(argv[0], "config")) {
1756 if(!strcasecmp(argv[1], "get")) {
1758 } else if(!strcasecmp(argv[1], "add")) {
1759 argv++, argc--, action = 1;
1760 } else if(!strcasecmp(argv[1], "del")) {
1761 argv++, argc--, action = -1;
1762 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1763 argv++, argc--, action = 0;
1767 fprintf(stderr, "Invalid number of arguments.\n");
1771 // Concatenate the rest of the command line
1772 strncpy(line, argv[1], sizeof(line) - 1);
1774 for(int i = 2; i < argc; i++) {
1775 strncat(line, " ", sizeof(line) - 1 - strlen(line));
1776 strncat(line, argv[i], sizeof(line) - 1 - strlen(line));
1779 // Liberal parsing into node name, variable name and value.
1785 len = strcspn(line, "\t =");
1787 value += strspn(value, "\t ");
1791 value += strspn(value, "\t ");
1795 variable = strchr(line, '.');
1805 fprintf(stderr, "No variable given.\n");
1809 if(action >= 0 && !*value) {
1810 fprintf(stderr, "No value for variable given.\n");
1814 if(action < -1 && *value) {
1818 /* Some simple checks. */
1820 bool warnonremove = false;
1822 for(int i = 0; variables[i].name; i++) {
1823 if(strcasecmp(variables[i].name, variable)) {
1828 variable = (char *)variables[i].name;
1830 /* Discourage use of obsolete variables. */
1832 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1834 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1836 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1841 /* Don't put server variables in host config files */
1843 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1845 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1847 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1852 /* Should this go into our own host config file? */
1854 if(!node && !(variables[i].type & VAR_SERVER)) {
1855 node = get_my_name(true);
1862 /* Change "add" into "set" for variables that do not allow multiple occurrences.
1863 Turn on warnings when it seems variables might be removed unintentionally. */
1865 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1866 warnonremove = true;
1868 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1869 warnonremove = true;
1875 if(node && !check_id(node)) {
1876 fprintf(stderr, "Invalid name for node.\n");
1881 if(force || action < 0) {
1882 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1884 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1889 // Open the right configuration file.
1890 char filename[PATH_MAX];
1893 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, node);
1895 snprintf(filename, sizeof(filename), "%s", tinc_conf);
1898 FILE *f = fopen(filename, "r");
1901 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1905 char tmpfile[PATH_MAX];
1909 snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename);
1910 tf = fopen(tmpfile, "w");
1913 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1919 // Copy the file, making modifications on the fly, unless we are just getting a value.
1923 bool removed = false;
1926 while(fgets(buf1, sizeof(buf1), f)) {
1927 buf1[sizeof(buf1) - 1] = 0;
1928 strncpy(buf2, buf1, sizeof(buf2));
1930 // Parse line in a simple way
1934 len = strcspn(buf2, "\t =");
1935 bvalue = buf2 + len;
1936 bvalue += strspn(bvalue, "\t ");
1938 if(*bvalue == '=') {
1940 bvalue += strspn(bvalue, "\t ");
1947 if(!strcasecmp(buf2, variable)) {
1951 printf("%s\n", bvalue);
1953 } else if(action == -1) {
1954 if(!*value || !strcasecmp(bvalue, value)) {
1960 } else if(action == 0) {
1961 // Warn if "set" was used for variables that can occur multiple times
1962 if(warnonremove && strcasecmp(bvalue, value)) {
1963 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1966 // Already set? Delete the rest...
1971 // Otherwise, replace.
1972 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1973 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1980 } else if(action > 0) {
1981 // Check if we've already seen this variable with the same value
1982 if(!strcasecmp(bvalue, value)) {
1989 // Copy original line...
1990 if(fputs(buf1, tf) < 0) {
1991 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1995 // Add newline if it is missing...
1996 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1997 if(fputc('\n', tf) < 0) {
1998 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2005 // Make sure we read everything...
2006 if(ferror(f) || !feof(f)) {
2007 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
2012 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
2016 // Add new variable if necessary.
2017 if((action > 0 && !found) || (action == 0 && !set)) {
2018 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
2019 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
2028 fprintf(stderr, "No matching configuration variables found.\n");
2033 // Make sure we wrote everything...
2035 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
2039 // Could we find what we had to remove?
2040 if(action < 0 && !removed) {
2042 fprintf(stderr, "No configuration variables deleted.\n");
2046 // Replace the configuration file with the new one
2049 if(remove(filename)) {
2050 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
2056 if(rename(tmpfile, filename)) {
2057 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
2061 // Silently try notifying a running tincd of changes.
2062 if(connect_tincd(false)) {
2063 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2069 static bool try_bind(int port) {
2070 struct addrinfo *ai = NULL, *aip;
2071 struct addrinfo hint = {
2072 .ai_flags = AI_PASSIVE,
2073 .ai_family = AF_UNSPEC,
2074 .ai_socktype = SOCK_STREAM,
2075 .ai_protocol = IPPROTO_TCP,
2078 bool success = true;
2080 snprintf(portstr, sizeof(portstr), "%d", port);
2082 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
2086 for(aip = ai; aip; aip = aip->ai_next) {
2087 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
2094 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
2107 int check_port(char *name) {
2112 fprintf(stderr, "Warning: could not bind to port 655. ");
2114 for(int i = 0; i < 100; i++) {
2115 int port = 0x1000 + (rand() & 0x7fff);
2117 if(try_bind(port)) {
2118 char filename[PATH_MAX];
2119 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
2120 FILE *f = fopen(filename, "a");
2123 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
2124 fprintf(stderr, "Please change tinc's Port manually.\n");
2128 fprintf(f, "Port = %d\n", port);
2130 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
2135 fprintf(stderr, "Please change tinc's Port manually.\n");
2139 static int cmd_init(int argc, char *argv[]) {
2140 if(!access(tinc_conf, F_OK)) {
2141 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
2146 fprintf(stderr, "Too many arguments!\n");
2148 } else if(argc < 2) {
2151 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
2153 if(!fgets(buf, sizeof(buf), stdin)) {
2154 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
2158 int len = rstrip(buf);
2161 fprintf(stderr, "No name given!\n");
2167 fprintf(stderr, "No Name given!\n");
2171 name = strdup(argv[1]);
2174 fprintf(stderr, "No Name given!\n");
2179 if(!check_id(name)) {
2180 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
2184 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
2185 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
2189 if(mkdir(confbase, 0777) && errno != EEXIST) {
2190 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
2194 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
2195 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
2199 FILE *f = fopen(tinc_conf, "w");
2202 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
2206 fprintf(f, "Name = %s\n", name);
2209 #ifndef DISABLE_LEGACY
2211 if(!rsa_keygen(2048, false)) {
2217 if(!ed25519_keygen(false)) {
2224 char filename[PATH_MAX];
2225 snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
2227 if(access(filename, F_OK)) {
2228 FILE *f = fopenmask(filename, "w", 0777);
2231 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
2235 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");
2245 static int cmd_generate_keys(int argc, char *argv[]) {
2246 #ifdef DISABLE_LEGACY
2253 fprintf(stderr, "Too many arguments!\n");
2258 name = get_my_name(false);
2261 #ifndef DISABLE_LEGACY
2263 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)) {
2269 if(!ed25519_keygen(true)) {
2276 #ifndef DISABLE_LEGACY
2277 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
2279 fprintf(stderr, "Too many arguments!\n");
2284 name = get_my_name(false);
2287 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
2291 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
2293 fprintf(stderr, "Too many arguments!\n");
2298 name = get_my_name(false);
2301 return !ed25519_keygen(true);
2304 static int cmd_help(int argc, char *argv[]) {
2309 static int cmd_version(int argc, char *argv[]) {
2311 fprintf(stderr, "Too many arguments!\n");
2319 static int cmd_info(int argc, char *argv[]) {
2321 fprintf(stderr, "Invalid number of arguments.\n");
2325 if(!connect_tincd(true)) {
2329 return info(fd, argv[1]);
2332 static const char *conffiles[] = {
2343 static int cmd_edit(int argc, char *argv[]) {
2345 fprintf(stderr, "Invalid number of arguments.\n");
2349 char filename[PATH_MAX] = "";
2351 if(strncmp(argv[1], "hosts" SLASH, 6)) {
2352 for(int i = 0; conffiles[i]; i++) {
2353 if(!strcmp(argv[1], conffiles[i])) {
2354 snprintf(filename, sizeof(filename), "%s" SLASH "%s", confbase, argv[1]);
2363 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, argv[1]);
2364 char *dash = strchr(argv[1], '-');
2369 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
2370 fprintf(stderr, "Invalid configuration filename.\n");
2378 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ? : getenv("EDITOR") ? : "vi", filename);
2380 xasprintf(&command, "edit \"%s\"", filename);
2382 int result = system(command);
2389 // Silently try notifying a running tincd of changes.
2390 if(connect_tincd(false)) {
2391 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
2397 static int export(const char *name, FILE *out) {
2398 char filename[PATH_MAX];
2399 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2400 FILE *in = fopen(filename, "r");
2403 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
2407 fprintf(out, "Name = %s\n", name);
2410 while(fgets(buf, sizeof(buf), in)) {
2411 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4)) {
2417 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2426 static int cmd_export(int argc, char *argv[]) {
2428 fprintf(stderr, "Too many arguments!\n");
2432 char *name = get_my_name(true);
2438 int result = export(name, stdout);
2448 static int cmd_export_all(int argc, char *argv[]) {
2450 fprintf(stderr, "Too many arguments!\n");
2454 DIR *dir = opendir(hosts_dir);
2457 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2465 while((ent = readdir(dir))) {
2466 if(!check_id(ent->d_name)) {
2473 printf("#---------------------------------------------------------------#\n");
2476 result |= export(ent->d_name, stdout);
2488 static int cmd_import(int argc, char *argv[]) {
2490 fprintf(stderr, "Too many arguments!\n");
2499 char filename[PATH_MAX] = "";
2501 bool firstline = true;
2503 while(fgets(buf, sizeof(buf), in)) {
2504 if(sscanf(buf, "Name = %4095s", name) == 1) {
2507 if(!check_id(name)) {
2508 fprintf(stderr, "Invalid Name in input!\n");
2516 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
2518 if(!force && !access(filename, F_OK)) {
2519 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2524 out = fopen(filename, "w");
2527 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2533 } else if(firstline) {
2534 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2539 if(!strcmp(buf, "#---------------------------------------------------------------#\n")) {
2544 if(fputs(buf, out) < 0) {
2545 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2556 fprintf(stderr, "Imported %d host configuration files.\n", count);
2559 fprintf(stderr, "No host configuration files imported.\n");
2564 static int cmd_exchange(int argc, char *argv[]) {
2565 return cmd_export(argc, argv) ? : cmd_import(argc, argv);
2568 static int cmd_exchange_all(int argc, char *argv[]) {
2569 return cmd_export_all(argc, argv) ? : cmd_import(argc, argv);
2572 static int switch_network(char *name) {
2573 if(strcmp(name, ".")) {
2574 if(!check_netname(name, false)) {
2575 fprintf(stderr, "Invalid character in netname!\n");
2579 if(!check_netname(name, true)) {
2580 fprintf(stderr, "Warning: unsafe character in netname!\n");
2590 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2597 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2598 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2599 xasprintf(&prompt, "%s> ", identname);
2604 static int cmd_network(int argc, char *argv[]) {
2606 fprintf(stderr, "Too many arguments!\n");
2611 return switch_network(argv[1]);
2614 DIR *dir = opendir(confdir);
2617 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2623 while((ent = readdir(dir))) {
2624 if(*ent->d_name == '.') {
2628 if(!strcmp(ent->d_name, "tinc.conf")) {
2633 char fname[PATH_MAX];
2634 snprintf(fname, sizeof(fname), "%s/%s/tinc.conf", confdir, ent->d_name);
2636 if(!access(fname, R_OK)) {
2637 printf("%s\n", ent->d_name);
2646 static int cmd_fsck(int argc, char *argv[]) {
2648 fprintf(stderr, "Too many arguments!\n");
2652 return fsck(orig_argv[0]);
2655 static void *readfile(FILE *in, size_t *len) {
2657 size_t alloced = 4096;
2658 char *buf = xmalloc(alloced);
2661 size_t read = fread(buf + count, 1, alloced - count, in);
2669 if(count >= alloced) {
2671 buf = xrealloc(buf, alloced);
2682 static int cmd_sign(int argc, char *argv[]) {
2684 fprintf(stderr, "Too many arguments!\n");
2689 name = get_my_name(true);
2696 char fname[PATH_MAX];
2697 snprintf(fname, sizeof(fname), "%s" SLASH "ed25519_key.priv", confbase);
2698 FILE *fp = fopen(fname, "r");
2701 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2705 ecdsa_t *key = ecdsa_read_pem_private_key(fp);
2708 fprintf(stderr, "Could not read private key from %s\n", fname);
2718 in = fopen(argv[1], "rb");
2721 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
2730 char *data = readfile(in, &len);
2737 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2742 // Ensure we sign our name and current time as well
2743 long t = time(NULL);
2745 xasprintf(&trailer, " %s %ld", name, t);
2746 int trailer_len = strlen(trailer);
2748 data = xrealloc(data, len + trailer_len);
2749 memcpy(data + len, trailer, trailer_len);
2754 if(!ecdsa_sign(key, data, len + trailer_len, sig)) {
2755 fprintf(stderr, "Error generating signature\n");
2761 b64encode(sig, sig, 64);
2764 fprintf(stdout, "Signature = %s %ld %s\n", name, t, sig);
2765 fwrite(data, len, 1, stdout);
2771 static int cmd_verify(int argc, char *argv[]) {
2773 fprintf(stderr, "Not enough arguments!\n");
2778 fprintf(stderr, "Too many arguments!\n");
2782 char *node = argv[1];
2784 if(!strcmp(node, ".")) {
2786 name = get_my_name(true);
2794 } else if(!strcmp(node, "*")) {
2797 if(!check_id(node)) {
2798 fprintf(stderr, "Invalid node name\n");
2806 in = fopen(argv[2], "rb");
2809 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
2817 char *data = readfile(in, &len);
2824 fprintf(stderr, "Error reading %s: %s\n", argv[1], strerror(errno));
2828 char *newline = memchr(data, '\n', len);
2830 if(!newline || (newline - data > MAX_STRING_SIZE - 1)) {
2831 fprintf(stderr, "Invalid input\n");
2837 size_t skip = newline - data;
2839 char signer[MAX_STRING_SIZE] = "";
2840 char sig[MAX_STRING_SIZE] = "";
2843 if(sscanf(data, "Signature = %s %ld %s", signer, &t, sig) != 3 || strlen(sig) != 86 || !t || !check_id(signer)) {
2844 fprintf(stderr, "Invalid input\n");
2849 if(node && strcmp(node, signer)) {
2850 fprintf(stderr, "Signature is not made by %s\n", node);
2860 xasprintf(&trailer, " %s %ld", signer, t);
2861 int trailer_len = strlen(trailer);
2863 data = xrealloc(data, len + trailer_len);
2864 memcpy(data + len, trailer, trailer_len);
2867 newline = data + skip;
2869 char fname[PATH_MAX];
2870 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, node);
2871 FILE *fp = fopen(fname, "r");
2874 fprintf(stderr, "Could not open %s: %s\n", fname, strerror(errno));
2879 ecdsa_t *key = get_pubkey(fp);
2883 key = ecdsa_read_pem_public_key(fp);
2887 fprintf(stderr, "Could not read public key from %s\n", fname);
2895 if(b64decode(sig, sig, 86) != 64 || !ecdsa_verify(key, newline, len + trailer_len - (newline - data), sig)) {
2896 fprintf(stderr, "Invalid signature\n");
2904 fwrite(newline, len - (newline - data), 1, stdout);
2910 static const struct {
2911 const char *command;
2912 int (*function)(int argc, char *argv[]);
2915 {"start", cmd_start},
2917 {"restart", cmd_restart},
2918 {"reload", cmd_reload},
2921 {"purge", cmd_purge},
2922 {"debug", cmd_debug},
2923 {"retry", cmd_retry},
2924 {"connect", cmd_connect},
2925 {"disconnect", cmd_disconnect},
2930 {"config", cmd_config, true},
2931 {"add", cmd_config},
2932 {"del", cmd_config},
2933 {"get", cmd_config},
2934 {"set", cmd_config},
2936 {"generate-keys", cmd_generate_keys},
2937 #ifndef DISABLE_LEGACY
2938 {"generate-rsa-keys", cmd_generate_rsa_keys},
2940 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2942 {"version", cmd_version},
2945 {"export", cmd_export},
2946 {"export-all", cmd_export_all},
2947 {"import", cmd_import},
2948 {"exchange", cmd_exchange},
2949 {"exchange-all", cmd_exchange_all},
2950 {"invite", cmd_invite},
2952 {"network", cmd_network},
2955 {"verify", cmd_verify},
2959 #ifdef HAVE_READLINE
2960 static char *complete_command(const char *text, int state) {
2969 while(commands[i].command) {
2970 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text))) {
2971 return xstrdup(commands[i].command);
2980 static char *complete_dump(const char *text, int state) {
2981 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2991 if(!strncasecmp(matches[i], text, strlen(text))) {
2992 return xstrdup(matches[i]);
3001 static char *complete_config(const char *text, int state) {
3010 while(variables[i].name) {
3011 char *dot = strchr(text, '.');
3014 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
3016 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
3020 if(!strncasecmp(variables[i].name, text, strlen(text))) {
3021 return xstrdup(variables[i].name);
3031 static char *complete_info(const char *text, int state) {
3037 if(!connect_tincd(false)) {
3041 // Check the list of nodes
3042 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
3043 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
3046 while(recvline(fd, line, sizeof(line))) {
3048 int n = sscanf(line, "%d %d %4095s", &code, &req, item);
3061 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
3065 if(!strncmp(item, text, strlen(text))) {
3066 return xstrdup(strip_weight(item));
3073 static char *complete_nothing(const char *text, int state) {
3077 static char **completion(const char *text, int start, int end) {
3078 char **matches = NULL;
3081 matches = rl_completion_matches(text, complete_command);
3082 } else if(!strncasecmp(rl_line_buffer, "dump ", 5)) {
3083 matches = rl_completion_matches(text, complete_dump);
3084 } else if(!strncasecmp(rl_line_buffer, "add ", 4)) {
3085 matches = rl_completion_matches(text, complete_config);
3086 } else if(!strncasecmp(rl_line_buffer, "del ", 4)) {
3087 matches = rl_completion_matches(text, complete_config);
3088 } else if(!strncasecmp(rl_line_buffer, "get ", 4)) {
3089 matches = rl_completion_matches(text, complete_config);
3090 } else if(!strncasecmp(rl_line_buffer, "set ", 4)) {
3091 matches = rl_completion_matches(text, complete_config);
3092 } else if(!strncasecmp(rl_line_buffer, "info ", 5)) {
3093 matches = rl_completion_matches(text, complete_info);
3100 static int cmd_shell(int argc, char *argv[]) {
3101 xasprintf(&prompt, "%s> ", identname);
3105 int maxargs = argc + 16;
3106 char **nargv = xmalloc(maxargs * sizeof(*nargv));
3108 for(int i = 0; i < argc; i++) {
3112 #ifdef HAVE_READLINE
3113 rl_readline_name = "tinc";
3114 rl_completion_entry_function = complete_nothing;
3115 rl_attempted_completion_function = completion;
3116 rl_filename_completion_desired = 0;
3121 #ifdef HAVE_READLINE
3126 rl_basic_word_break_characters = "\t\n ";
3127 line = readline(prompt);
3130 copy = xstrdup(line);
3133 line = fgets(buf, sizeof(buf), stdin);
3139 fputs(prompt, stdout);
3142 line = fgets(buf, sizeof(buf), stdin);
3149 /* Ignore comments */
3158 char *p = line + strspn(line, " \t\n");
3159 char *next = strtok(p, " \t\n");
3162 if(nargc >= maxargs) {
3164 nargv = xrealloc(nargv, maxargs * sizeof(*nargv));
3169 next = strtok(NULL, " \t\n");
3176 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit")) {
3183 for(int i = 0; commands[i].command; i++) {
3184 if(!strcasecmp(nargv[argc], commands[i].command)) {
3185 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
3191 #ifdef HAVE_READLINE
3200 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
3215 int main(int argc, char *argv[]) {
3216 program_name = argv[0];
3219 tty = isatty(0) && isatty(1);
3221 if(!parse_options(argc, argv)) {
3226 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
3227 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
3240 static struct WSAData wsa_state;
3242 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
3243 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
3252 if(optind >= argc) {
3253 return cmd_shell(argc, argv);
3256 for(int i = 0; commands[i].command; i++) {
3257 if(!strcasecmp(argv[optind], commands[i].command)) {
3258 return commands[i].function(argc - optind, argv + optind);
3262 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);