#include "system.h"
+#include <getopt.h>
+
#include "cfg/cfg.h"
#include "fd/event.h"
#include "fd/fd.h"
#include "tnl/tnl.h"
#include "vnd/vnd.h"
-static bool vnd_recv(struct vnd *vnd, char *buf, int len) {
- static int p = 0;
- char b[4];
- logger(LOG_DEBUG, _("Read packet of %d bytes from vnd %p"), len, vnd);
- memcpy(b, buf + 16, 4);
- memcpy(buf + 16, buf + 20, 4);
- memcpy(buf + 20, b, 4);
- vnd->send(vnd, buf, len);
- return true;
+static bool show_help = false;
+static bool show_version = false;
+static int kill_tincd = 0;
+static bool bypass_security = false;
+static bool do_mlock = false;
+static bool use_logfile = false;
+static bool do_detach = true;
+static int debug_level = 1;
+
+static char *confbase = NULL;
+static char *identname = NULL;
+static char *pidfilename = NULL;
+static char *logfilename = NULL;
+static char *cfgfilename = NULL;
+
+int tinc_argc;
+char **tinc_argv;
+cfg_tree_t tinc_cfg;
+
+static struct option const long_options[] = {
+ {"config", required_argument, NULL, 'c'},
+ {"kill", optional_argument, NULL, 'k'},
+ {"net", required_argument, NULL, 'n'},
+ {"help", no_argument, NULL, 1},
+ {"version", no_argument, NULL, 2},
+ {"no-detach", no_argument, NULL, 'D'},
+ {"debug", optional_argument, NULL, 'd'},
+ {"bypass-security", no_argument, NULL, 3},
+ {"mlock", no_argument, NULL, 'L'},
+ {"logfile", optional_argument, NULL, 4},
+ {"pidfile", required_argument, NULL, 5},
+ {NULL, 0, NULL, 0}
+};
+
+#ifdef HAVE_MINGW
+static struct WSAData wsa_state;
+#endif
+
+static void usage(bool status) {
+ if(status)
+ fprintf(stderr, _("Try `%s --help\' for more information.\n"), tinc_argv[0]);
+ else {
+ printf(_("Usage: %s [option]...\n\n"), tinc_argv[0]);
+ printf(_(" -c, --config=DIR Read configuration options from DIR.\n"
+ " -D, --no-detach Don't fork and detach.\n"
+ " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n"
+ " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n"
+ " -n, --net=NETNAME Connect to net NETNAME.\n"
+ " -L, --mlock Lock tinc into main memory.\n"
+ " --logfile[=FILENAME] Write log entries to a logfile.\n"
+ " --pidfile=FILENAME Write PID to FILENAME.\n"
+ " --help Display this help and exit.\n"
+ " --version Output version information and exit.\n\n"));
+ printf(_("Report bugs to tinc@tinc-vpn.org.\n"));
+ }
}
-static bool vnd_stop(event_t *event) {
- static int i = 0;
+static bool parse_options(int argc, char **argv) {
+ int result;
+ int option_index = 0;
+
+ while((result = getopt_long(argc, argv, "c:DLd::k::n:", long_options, &option_index)) != EOF) {
+ switch (result) {
+ case 0:
+ break;
+
+ case 'c': /* --config */
+ confbase = xstrdup(optarg);
+ break;
+
+ case 'D': /* --no-detach */
+ do_detach = false;
+ break;
+
+ case 'L': /* --mlock */
+ do_mlock = true;
+ break;
+
+ case 'd': /* --debug */
+ if(optarg)
+ debug_level = atoi(optarg);
+ else
+ debug_level++;
+ break;
+
+ case 'k': /* --kill */
+#ifndef HAVE_MINGW
+ if(optarg) {
+ if(!strcasecmp(optarg, "HUP"))
+ kill_tincd = SIGHUP;
+ else if(!strcasecmp(optarg, "TERM"))
+ kill_tincd = SIGTERM;
+ else if(!strcasecmp(optarg, "KILL"))
+ kill_tincd = SIGKILL;
+ else if(!strcasecmp(optarg, "USR1"))
+ kill_tincd = SIGUSR1;
+ else if(!strcasecmp(optarg, "USR2"))
+ kill_tincd = SIGUSR2;
+ else if(!strcasecmp(optarg, "WINCH"))
+ kill_tincd = SIGWINCH;
+ else if(!strcasecmp(optarg, "INT"))
+ kill_tincd = SIGINT;
+ else if(!strcasecmp(optarg, "ALRM"))
+ kill_tincd = SIGALRM;
+ else {
+ kill_tincd = atoi(optarg);
- logger(LOG_DEBUG, "i = %d", i++);
+ if(!kill_tincd) {
+ fprintf(stderr, _("Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n"),
+ optarg);
+ usage(true);
+ return false;
+ }
+ }
+ } else
+ kill_tincd = SIGTERM;
+#else
+ kill_tincd = 1;
+#endif
+ break;
- if(i > 5) {
- fd_stop();
- return false;
+ case 'n': /* --net */
+ netname = xstrdup(optarg);
+ break;
+
+ case 1: /* --help */
+ show_help = true;
+ break;
+
+ case 2: /* --version */
+ show_version = true;
+ break;
+
+ case 3: /* --bypass-security */
+ bypass_security = true;
+ break;
+
+ case 4: /* --logfile */
+ use_logfile = true;
+ if(optarg)
+ logfilename = xstrdup(optarg);
+ break;
+
+ case 5: /* --pidfile */
+ pidfilename = xstrdup(optarg);
+ break;
+
+ case '?':
+ usage(true);
+ return false;
+
+ default:
+ break;
+ }
}
- event_update(event, event->interval);
return true;
}
-int test(int argc, char **argv) {
- vnd_t *vnd;
- event_t *stop;
- tnl_listen_t *listener;
-
- //vnd_init();
- if(fd_init() && tnl_init()) {
- vnd = vnd_new();
- vnd_set(vnd, "/dev/tun", "test", VND_MODE_TUN, vnd_recv);
-
- stop = event_new();
- event_set(stop, (struct timeval){5, 0}, vnd_stop, NULL);
- event_add(stop);
-
- clear(new(listener));
- listener->type = SOCK_STREAM;
- listener->protocol = IPPROTO_TCP;
- sa(&listener->local.address)->sa_family = AF_INET;
-
- if(tnl_listen(listener) && vnd_open(vnd)) {
- fd_run();
- vnd_close(vnd);
- listener->close(listener);
+static void make_names(void)
+{
+#ifdef HAVE_MINGW
+ HKEY key;
+ char installdir[1024] = "";
+ long len = sizeof(installdir);
+#endif
+
+ if(netname)
+ asprintf(&identname, "tinc.%s", netname);
+ else
+ identname = xstrdup("tinc");
+
+#ifdef HAVE_MINGW
+ if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) {
+ if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) {
+ if(!logfilename)
+ asprintf(&logfilename, "%s/log/%s.log", identname);
+ if(!confbase) {
+ if(netname)
+ asprintf(&confbase, "%s/%s", installdir, netname);
+ else
+ asprintf(&confbase, "%s", installdir);
+ }
}
+ RegCloseKey(key);
+ if(*installdir)
+ return;
+ }
+#endif
+
+ if(!pidfilename)
+ asprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
- vnd_free(vnd);
+ if(!logfilename)
+ asprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname);
- tnl_exit();
- fd_exit();
+ if(!confbase) {
+ if(netname)
+ asprintf(&confbase, CONFDIR "/tinc/%s", netname);
+ else
+ asprintf(&confbase, CONFDIR "/tinc");
}
- //vnd_exit();
-}
-avl_tree_t *tinc_cfg = NULL;
-char *tinc_netname = NULL;
+ asprintf(&cfgfilename, "%s/tinc.conf", confbase);
+}
int main(int argc, char **argv) {
- tnl_listen_t *listener;
+ tinc_argc = argc;
+ tinc_argv = argv;
- logger_init("tinc", LOGGER_MODE_STDERR);
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ if(!parse_options(argc, argv))
+ return 1;
+ make_names();
+
+ if(show_version) {
+ printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE,
+ VERSION, __DATE__, __TIME__, PROT_CURRENT);
+ printf(_("Copyright (C) 1998-2004 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"
+ "and you are welcome to redistribute it under certain conditions;\n"
+ "see the file COPYING for details.\n"));
+
+ return 0;
+ }
+
+ if(show_help) {
+ usage(false);
+ return 0;
+ }
+
+ if(kill_tincd)
+ return !kill_other(kill_tincd);
+
+ openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
+
+ /* Lock all pages into memory if requested */
+
+ if(do_mlock)
+#ifdef HAVE_MLOCKALL
+ if(mlockall(MCL_CURRENT | MCL_FUTURE)) {
+ logger(LOG_ERR, _("System call `%s' failed: %s"), "mlockall",
+ strerror(errno));
+#else
+ {
+ logger(LOG_ERR, _("mlockall() not supported on this platform!"));
+#endif
+ return -1;
+ }
+
tinc_cfg = cfg_tree_new();
+
+ asprintf(cfgfilename, "%s/tinc.conf", confbase);
- if(!cfg_read_file(tinc_cfg, "tinc.conf"))
+ if(!cfg_read_file(tinc_cfg, cfgfilename))
return 1;
- if(fd_init() && tnl_init()) {
- clear(new(listener));
- listener->type = SOCK_STREAM;
- listener->protocol = IPPROTO_TCP;
- sa(&listener->local.address)->sa_family = AF_INET;
- ((struct sockaddr_in *) &listener->local.address)->sin_port = htons(655);
- if(tnl_listen(listener)) {
- fd_run();
- listener->close(listener);
- }
- tnl_exit() && fd_exit();
+#ifdef HAVE_MINGW
+ if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
+ logger(LOG_ERR, _("System call `%s' failed: %s"), "WSAStartup", winerror(GetLastError()));
+ return 1;
}
+#endif
+ if(do_detach && !detach())
+ return 1;
+
+ if(!fd_init() || !tnl_init() || !rt_init())
+ return 1;
+
+ fd_run();
+
+ rt_exit();
+ tnl_exit();
+ fd_exit();
+end:
+ logger(LOG_NOTICE, _("Terminating"));
+
+#ifndef HAVE_MINGW
+ remove_pid(pidfilename);
+#endif
+
+ logger_exit();
+
return 0;
}
-
return tnl_recv(tnl);
}
+static bool tnl_authenticate(tnl_t *tnl) {
+ gnutls_x509_crt cert;
+ const gnutls_datum *certs;
+ int ncerts = 0, result;
+ char buf[1024], *name, *p;
+ int len;
+
+ certs = gnutls_certificate_get_peers(tnl->session, &ncerts);
+
+ if (!certs || !ncerts) {
+ logger(LOG_ERR, _("tnl: no certificates from %s"), tnl->remote.hostname);
+ return false;
+ }
+
+ len = sizeof buf;
+ gnutls_x509_crt_init(&cert);
+ result = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER) ?: gnutls_x509_crt_get_dn(cert, buf, &len);
+
+ if(result) {
+ logger(LOG_ERR, _("tnl: error importing certificate from %s: %s"), tnl->remote.hostname, gnutls_strerror(errno));
+ gnutls_x509_crt_deinit(cert);
+ return false;
+ }
+
+ name = strstr(buf, "CN=");
+ if(!name) {
+ logger(LOG_ERR, _("tnl: no name in certificate from %s"), tnl->remote.hostname);
+ gnutls_x509_crt_deinit(cert);
+ return false;
+ }
+
+ name += 3;
+ for(p = name; *p && *p != ','; p++);
+ *p = '\0';
+
+ if(tnl->remote.id && strcmp(tnl->remote.id, name)) {
+ logger(LOG_ERR, _("tnl: peer %s is %s instead of %s"), tnl->remote.hostname, name, tnl->remote.id);
+ return false;
+ }
+
+ replace(tnl->remote.id, name);
+
+ result = gnutls_certificate_verify_peers(tnl->session);
+
+ if(result < 0) {
+ logger(LOG_ERR, "tnl: error verifying certificate from %s (%s): %s\n", tnl->remote.id, tnl->remote.hostname, gnutls_strerror(result));
+ return false;
+ }
+
+ if(result) {
+ logger(LOG_ERR, "tnl: certificate from %s (%s) not good, verification result %x", tnl->remote.id, tnl->remote.hostname, result);
+ return false;
+ }
+}
+
+
+
static bool tnl_handshake_handler(fd_t *fd) {
+ char id[1024];
tnl_t *tnl = fd->data;
int result;
logger(LOG_DEBUG, _("tnl: handshake finished"));
- result = gnutls_certificate_verify_peers(tnl->session);
- if(result < 0) {
- logger(LOG_ERR, "tnl: certificate error: %s\n", gnutls_strerror(result));
- tnl->close(tnl);
- return false;
- }
-
- if(result) {
- logger(LOG_ERR, "tnl: certificate not good, verification result %x", result);
- tnl->close(tnl);
+ if(!tnl_authenticate(tnl))
return false;
- }
tnl->status == TNL_STATUS_UP;
- tnl->fd.handler = tnl_recv_handler;
+ tnl->fd.read = tnl_recv_handler;
tnl->accept(tnl);
return true;
}
sa_unmap(&ss);
- new(tnl);
+ clear(new(tnl));
tnl->local = listener->local;
tnl->remote.address = ss;
len = sizeof tnl->local.address;
tnl->close = tnl_close;
tnl->fd.fd = sock;
- tnl->fd.mode = FD_MODE_READ;
- tnl->fd.handler = tnl_handshake_handler;
+ tnl->fd.read = tnl_handshake_handler;
tnl->fd.data = tnl;
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
return false;
}
- fd_del(&tnl->fd);
-
fcntl(tnl->fd.fd, F_SETFL, fcntl(tnl->fd.fd, F_GETFL) | O_NONBLOCK);
tnl->status = TNL_STATUS_HANDSHAKE;
gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)fd->fd);
gnutls_handshake(tnl->session);
- tnl->fd.mode = FD_MODE_READ;
- tnl->fd.handler = tnl_handshake_handler;
- fd_add(&tnl->fd);
+ tnl->fd.write = NULL;
+ tnl->fd.read = tnl_handshake_handler;
+ fd_mod(&tnl->fd);
logger(LOG_DEBUG, _("tnl: connected"));
tnl->status = TNL_STATUS_CONNECTING;
tnl->fd.fd = sock;
- tnl->fd.mode = FD_MODE_WRITE;
- tnl->fd.handler = tnl_connect_handler;
+ tnl->fd.write = tnl_connect_handler;
tnl->fd.data = tnl;
tnl->send_packet = tnl_send_packet;
tnl_add(tnl);
-
fd_add(&tnl->fd);
return true;
}
listener->fd.fd = sock;
- listener->fd.mode = FD_MODE_READ;
- listener->fd.handler = tnl_accept_handler;
+ listener->fd.read = tnl_accept_handler;
listener->fd.data = listener;
listener->close = tnl_listen_close;