#include "xalloc.h"
#include "protocol.h"
#include "control_common.h"
+#include "ecdsagen.h"
#include "rsagen.h"
#include "utils.h"
#include "tincctl.h"
/* If nonzero, print the version on standard output and exit. */
static bool show_version = false;
-/* If nonzero, it will attempt to kill a running tincd and exit. */
-static int kill_tincd = 0;
-
-/* If nonzero, generate public/private keypair for this host/net. */
-static int generate_keys = 0;
-
static char *name = NULL;
static char *identname = NULL; /* program name for syslog */
-static char *controlcookiename = NULL; /* cookie file location */
+static char *pidfilename = NULL; /* pid file location */
static char controlcookie[1024];
char *netname = NULL;
char *confbase = NULL;
-static char *host = NULL;
#ifdef HAVE_MINGW
static struct WSAData wsa_state;
static struct option const long_options[] = {
{"config", required_argument, NULL, 'c'},
- {"host", required_argument, NULL, 'h'},
{"net", required_argument, NULL, 'n'},
{"help", no_argument, NULL, 1},
{"version", no_argument, NULL, 2},
- {"controlcookie", required_argument, NULL, 5},
+ {"pidfile", required_argument, NULL, 5},
{NULL, 0, NULL, 0}
};
else {
printf("Usage: %s [options] command\n\n", program_name);
printf("Valid options are:\n"
- " -c, --config=DIR Read configuration options from DIR.\n"
- " -n, --net=NETNAME Connect to net NETNAME.\n"
- " --controlcookie=FILENAME Read control socket from FILENAME.\n"
- " --help Display this help and exit.\n"
- " --version Output version information and exit.\n"
+ " -c, --config=DIR Read configuration options from DIR.\n"
+ " -n, --net=NETNAME Connect to net NETNAME.\n"
+ " --pidfile=FILENAME Read control cookie from FILENAME.\n"
+ " --help Display this help and exit.\n"
+ " --version Output version information and exit.\n"
"\n"
"Valid commands are:\n"
" start Start tincd.\n"
" stop Stop tincd.\n"
" restart Restart tincd.\n"
- " reload Reload configuration of running tincd.\n"
+ " reload Partially reload configuration of running tincd.\n"
" pid Show PID of currently running tincd.\n"
- " generate-keys [bits] Generate a new public/private keypair.\n"
+ " generate-keys [bits] Generate new RSA and ECDSA public/private keypairs.\n"
+ " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
+ " generate-ecdsa-keys Generate a new ECDSA public/private keypair.\n"
" dump Dump a list of one of the following things:\n"
" nodes - all known nodes in the VPN\n"
" edges - all known connections in the VPN\n"
" purge Purge unreachable nodes\n"
" debug N Set debug level\n"
" retry Retry all outgoing connections\n"
- " reload Partial reload of configuration\n"
" disconnect NODE Close meta connection with NODE\n"
#ifdef HAVE_CURSES
" top Show real-time statistics\n"
int r;
int option_index = 0;
- while((r = getopt_long(argc, argv, "c:n:h:", long_options, &option_index)) != EOF) {
+ while((r = getopt_long(argc, argv, "c:n:", long_options, &option_index)) != EOF) {
switch (r) {
case 0: /* long option */
break;
confbase = xstrdup(optarg);
break;
- case 'h': /* alternative host to connect to */
- host = xstrdup(optarg);
- break;
-
case 'n': /* net name given */
netname = xstrdup(optarg);
break;
break;
case 5: /* open control socket here */
- controlcookiename = xstrdup(optarg);
+ pidfilename = xstrdup(optarg);
break;
case '?':
char *directory;
char buf[PATH_MAX];
char buf2[PATH_MAX];
- size_t len;
/* Check stdin and stdout */
if(isatty(0) && isatty(1)) {
return NULL;
}
- len = strlen(buf);
+ size_t len = strlen(buf);
if(len)
buf[--len] = 0;
return r;
}
+/*
+ Generate a public/private ECDSA keypair, and ask for a file to store
+ them in.
+*/
+static bool ecdsa_keygen() {
+ ecdsa_t key;
+ FILE *f;
+ char *filename;
+
+ fprintf(stderr, "Generating ECDSA keypair:\n");
+
+ if(!ecdsa_generate(&key)) {
+ fprintf(stderr, "Error during key generation!\n");
+ return false;
+ } else
+ fprintf(stderr, "Done.\n");
+
+ xasprintf(&filename, "%s/ecdsa_key.priv", confbase);
+ f = ask_and_open(filename, "private ECDSA key", "a");
+
+ if(!f)
+ return false;
+
+#ifdef HAVE_FCHMOD
+ /* Make it unreadable for others. */
+ fchmod(fileno(f), 0600);
+#endif
+
+ if(ftell(f))
+ fprintf(stderr, "Appending key to existing contents.\nMake sure only one key is stored in the file.\n");
+
+ ecdsa_write_pem_private_key(&key, f);
+
+ fclose(f);
+ free(filename);
+
+ if(name)
+ xasprintf(&filename, "%s/hosts/%s", confbase, name);
+ else
+ xasprintf(&filename, "%s/ecdsa_key.pub", confbase);
+
+ f = ask_and_open(filename, "public ECDSA key", "a");
+
+ if(!f)
+ return false;
+
+ if(ftell(f))
+ fprintf(stderr, "Appending key to existing contents.\nMake sure only one key is stored in the file.\n");
+
+ char *pubkey = ecdsa_get_base64_public_key(&key);
+ fprintf(f, "ECDSAPublicKey = %s\n", pubkey);
+ free(pubkey);
+
+ fclose(f);
+ free(filename);
+
+ return true;
+}
+
/*
Generate a public/private RSA keypair, and ask for a file to store
them in.
*/
-static bool keygen(int bits) {
+static bool rsa_keygen(int bits) {
rsa_t key;
FILE *f;
char *filename;
xasprintf(&confbase, "%s", installdir);
}
}
- if(!controlcookiename)
- xasprintf(&controlcookiename, "%s/cookie", confbase);
+ if(!pidfilename)
+ xasprintf(&pidfilename, "%s/pid", confbase);
RegCloseKey(key);
if(*installdir)
return;
}
#endif
- if(!controlcookiename)
- xasprintf(&controlcookiename, "%s/run/%s.cookie", LOCALSTATEDIR, identname);
+ if(!pidfilename)
+ xasprintf(&pidfilename, "%s/run/%s.pid", LOCALSTATEDIR, identname);
if(netname) {
if(!confbase)
bool sendline(int fd, char *format, ...) {
static char buffer[4096];
char *p = buffer;
- size_t blen = 0;
+ int blen = 0;
va_list ap;
va_start(ap, format);
blen = vsnprintf(buffer, sizeof buffer, format, ap);
va_end(ap);
- if(blen < 0 || blen >= sizeof buffer)
+ if(blen < 1 || blen >= sizeof buffer)
return false;
buffer[blen] = '\n';
int result = send(fd, p, blen, 0);
if(result == -1 && errno == EINTR)
continue;
- else if(result <= 0);
+ else if(result <= 0)
return false;
p += result;
blen -= result;
}
}
-int main(int argc, char *argv[], char *envp[]) {
+#ifdef HAVE_MINGW
+static bool remove_service(void) {
+ SC_HANDLE manager = NULL;
+ SC_HANDLE service = NULL;
+ SERVICE_STATUS status = {0};
+
+ manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if(!manager) {
+ fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
+ return false;
+ }
+
+ service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
+
+ if(!service) {
+ fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
+ return false;
+ }
+
+ if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
+ fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
+ else
+ fprintf(stderr, "%s service stopped\n", identname);
+
+ if(!DeleteService(service)) {
+ fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
+ return false;
+ }
+
+ fprintf(stderr, "%s service removed\n", identname);
+
+ return true;
+}
+#endif
+
+int main(int argc, char *argv[]) {
int fd;
int result;
+ char host[128];
char port[128];
int pid;
make_names();
if(show_version) {
- printf("%s version %s (built %s %s, protocol %d)\n", PACKAGE,
- VERSION, __DATE__, __TIME__, PROT_CURRENT);
+ printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
+ VERSION, __DATE__, __TIME__, PROT_MAJOR, PROT_MINOR);
printf("Copyright (C) 1998-2009 Ivo Timmermans, Guus Sliepen and others.\n"
"See the AUTHORS file for a complete list.\n\n"
"tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
// First handle commands that don't involve connecting to a running tinc daemon.
+ if(!strcasecmp(argv[optind], "generate-rsa-keys")) {
+ return !rsa_keygen(optind > argc ? atoi(argv[optind + 1]) : 2048);
+ }
+
+ if(!strcasecmp(argv[optind], "generate-ecdsa-keys")) {
+ return !ecdsa_keygen();
+ }
+
if(!strcasecmp(argv[optind], "generate-keys")) {
- return !keygen(optind > argc ? atoi(argv[optind + 1]) : 2048);
+ return !(rsa_keygen(optind > argc ? atoi(argv[optind + 1]) : 2048) && ecdsa_keygen());
}
if(!strcasecmp(argv[optind], "start")) {
- argv[optind] = NULL;
- execve(SBINDIR "/tincd", argv, envp);
- fprintf(stderr, "Could not start tincd: %s", strerror(errno));
+ int i, j;
+ char *c;
+ char *slash = strrchr(argv[0], '/');
+#ifdef HAVE_MINGW
+ if ((c = strrchr(argv[0], '\\')) > slash)
+ slash = c;
+#endif
+ if (slash++) {
+ c = xmalloc((slash - argv[0]) + sizeof("tincd"));
+ sprintf(c, "%.*stincd", (int)(slash - argv[0]), argv[0]);
+ }
+ else
+ c = "tincd";
+ argv[0] = c;
+ for(i = j = 1; argv[i]; ++i)
+ if (i != optind && strcmp(argv[i], "--") != 0)
+ argv[j++] = argv[i];
+ argv[j] = NULL;
+ execvp(c, argv);
+ fprintf(stderr, "Could not start %s: %s\n", c, strerror(errno));
return 1;
}
* ancestors are writable only by trusted users, which we don't verify.
*/
- FILE *f = fopen(controlcookiename, "r");
+ FILE *f = fopen(pidfilename, "r");
if(!f) {
- fprintf(stderr, "Could not open control socket cookie file %s: %s\n", controlcookiename, strerror(errno));
+ fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
return 1;
}
- if(fscanf(f, "%1024s %128s %d", controlcookie, port, &pid) != 3) {
- fprintf(stderr, "Could not parse control socket cookie file %s\n", controlcookiename);
+ if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
+ fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
return 1;
}
struct addrinfo *res = NULL;
if(getaddrinfo(host, port, &hints, &res) || !res) {
- fprintf(stderr, "Cannot resolve %s port %s: %s", host ?: "localhost", port, strerror(errno));
+ fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, strerror(errno));
return 1;
}
#endif
if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
- fprintf(stderr, "Cannot connect to %s port %s: %s\n", host ?: "localhost", port, sockstrerror(sockerrno));
+ fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
return 1;
}
}
if(!strcasecmp(argv[optind], "stop")) {
+#ifndef HAVE_MINGW
sendline(fd, "%d %d", CONTROL, REQ_STOP);
if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_STOP || result) {
fprintf(stderr, "Could not stop tinc daemon\n");
return 1;
}
+#else
+ if(!remove_service())
+ return 1;
+#endif
return 0;
}