From 01f47c46af514a9d7f39c143e4558a8426a0d3eb Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Fri, 18 May 2007 16:52:34 +0000 Subject: [PATCH] Start of control socket implementation. --- src/Makefile.am | 11 +- src/control.c | 116 +++++++++++++++++ src/control.h | 28 ++++ src/net_setup.c | 16 +-- src/tincctl.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++ src/tincd.c | 3 + 6 files changed, 499 insertions(+), 12 deletions(-) create mode 100644 src/control.c create mode 100644 src/control.h create mode 100644 src/tincctl.c diff --git a/src/Makefile.am b/src/Makefile.am index ed204ae4..41b25980 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,21 +1,23 @@ ## Produce this file with automake to get Makefile.in # $Id: Makefile.am,v 1.4.4.33 2003/08/02 15:13:08 guus Exp $ -sbin_PROGRAMS = tincd +sbin_PROGRAMS = tincd tincctl EXTRA_DIST = linux/device.c bsd/device.c solaris/device.c cygwin/device.c mingw/device.c mingw/common.h raw_socket/device.c uml_socket/device.c -tincd_SOURCES = conf.c connection.c edge.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \ +tincd_SOURCES = conf.c connection.c control.c edge.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \ net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \ protocol_key.c protocol_subnet.c route.c subnet.c tincd.c +tincctl_SOURCES = tincctl.c + nodist_tincd_SOURCES = device.c DEFAULT_INCLUDES = INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib -noinst_HEADERS = conf.h connection.h device.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \ +noinst_HEADERS = conf.h connection.h control.h device.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \ protocol.h route.h subnet.h LIBS = @LIBS@ @LIBINTL@ @@ -23,6 +25,9 @@ LIBS = @LIBS@ @LIBINTL@ tincd_LDADD = \ $(top_builddir)/lib/libvpn.a +tincctl_LDADD = \ + $(top_builddir)/lib/libvpn.a + localedir = $(datadir)/locale AM_CFLAGS = @CFLAGS@ -DCONFDIR=\"$(sysconfdir)\" -DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" diff --git a/src/control.c b/src/control.c new file mode 100644 index 00000000..b72ea9ee --- /dev/null +++ b/src/control.c @@ -0,0 +1,116 @@ +/* + control.c -- Control socket handling. + Copyright (C) 2007 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include + +#include "system.h" +#include "conf.h" +#include "control.h" +#include "logger.h" +#include "xalloc.h" + +static int control_socket = -1; +static struct event control_event; +static splay_tree_t *control_socket_tree; +extern char *controlfilename; + +static void handle_control_data(int fd, short events, void *event) { + char buf[MAXBUFSIZE]; + size_t inlen; + + inlen = read(fd, buf, sizeof buf); + + if(inlen <= 0) { + logger(LOG_DEBUG, _("Closing control socket")); + event_del(event); + splay_delete(control_socket_tree, event); + close(fd); + } +} + +static void handle_new_control_socket(int fd, short events, void *data) { + int newfd; + struct event *ev; + + newfd = accept(fd, NULL, NULL); + + if(newfd < 0) { + logger(LOG_ERR, _("Accepting a new connection failed: %s"), strerror(errno)); + event_del(&control_event); + return; + } + + ev = xmalloc(sizeof *ev); + event_set(ev, newfd, EV_READ | EV_PERSIST, handle_control_data, ev); + event_add(ev, NULL); + splay_insert(control_socket_tree, ev); + + logger(LOG_DEBUG, _("Control socket connection accepted")); +} + +static int control_compare(const struct event *a, const struct event *b) { + return a < b ? -1 : a > b ? 1 : 0; +} + +void init_control() { + struct sockaddr_un addr; + + control_socket_tree = splay_alloc_tree((splay_compare_t)control_compare, (splay_action_t)free); + + if(strlen(controlfilename) >= sizeof addr.sun_path) { + logger(LOG_ERR, _("Control socket filename too long!")); + return; + } + + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, controlfilename, sizeof addr.sun_path - 1); + + control_socket = socket(PF_UNIX, SOCK_STREAM, 0); + + if(control_socket < 0) { + logger(LOG_ERR, _("Creating UNIX socket failed: %s"), strerror(errno)); + return; + } + + unlink(controlfilename); + if(bind(control_socket, (struct sockaddr *)&addr, sizeof addr) < 0) { + logger(LOG_ERR, _("Can't bind to %s: %s\n"), controlfilename, strerror(errno)); + close(control_socket); + return; + } + + if(listen(control_socket, 3) < 0) { + logger(LOG_ERR, _("Can't listen on %s: %s\n"), controlfilename, strerror(errno)); + close(control_socket); + return; + } + + event_set(&control_event, control_socket, EV_READ | EV_PERSIST, handle_new_control_socket, NULL); + event_add(&control_event, NULL); +} + +void exit_control() { + if(control_socket >= 0) { + event_del(&control_event); + close(control_socket); + } +} diff --git a/src/control.h b/src/control.h new file mode 100644 index 00000000..94b82aa1 --- /dev/null +++ b/src/control.h @@ -0,0 +1,28 @@ +/* + control.h -- header for control.c. + Copyright (C) 2007 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#ifndef __TINC_CONTROL_H__ +#define __TINC_CONTROL_H__ + +extern void init_control(); +extern void exit_control(); + +#endif diff --git a/src/net_setup.c b/src/net_setup.c index c5f776af..4407cd4f 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -31,6 +31,7 @@ #include "splay_tree.h" #include "conf.h" #include "connection.h" +#include "control.h" #include "device.h" #include "graph.h" #include "logger.h" @@ -525,10 +526,8 @@ bool setup_myself(void) { EV_READ|EV_PERSIST, handle_new_meta_connection, NULL); if(event_add(&listen_socket[listen_sockets].ev_tcp, NULL) < 0) { - logger(LOG_WARNING, _("event_add failed: %s"), strerror(errno)); - close(listen_socket[listen_sockets].tcp); - close(listen_socket[listen_sockets].udp); - continue; + logger(LOG_EMERG, _("event_add failed: %s"), strerror(errno)); + abort(); } event_set(&listen_socket[listen_sockets].ev_udp, @@ -536,11 +535,8 @@ bool setup_myself(void) { EV_READ|EV_PERSIST, handle_incoming_vpn_data, NULL); if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) { - logger(LOG_WARNING, _("event_add failed: %s"), strerror(errno)); - close(listen_socket[listen_sockets].tcp); - close(listen_socket[listen_sockets].udp); - event_del(&listen_socket[listen_sockets].ev_tcp); - continue; + logger(LOG_EMERG, _("event_add failed: %s"), strerror(errno)); + abort(); } ifdebug(CONNECTIONS) { @@ -581,6 +577,7 @@ bool setup_network_connections(void) { init_nodes(); init_edges(); init_requests(); + init_control(); if(get_config_int(lookup_config(config_tree, "PingInterval"), &pinginterval)) { if(pinginterval < 1) { @@ -649,6 +646,7 @@ void close_network_connections(void) { asprintf(&envp[3], "NAME=%s", myself->name); envp[4] = NULL; + exit_control(); exit_requests(); exit_edges(); exit_subnets(); diff --git a/src/tincctl.c b/src/tincctl.c new file mode 100644 index 00000000..91d141dd --- /dev/null +++ b/src/tincctl.c @@ -0,0 +1,337 @@ +/* + tincctl.c -- Controlling a running tincd + Copyright (C) 2007 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ + +#include "system.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "conf.h" +#include "protocol.h" +#include "xalloc.h" + +/* The name this program was run with. */ +char *program_name = NULL; + +/* If nonzero, display usage information and exit. */ +bool show_help = false; + +/* If nonzero, print the version on standard output and exit. */ +bool show_version = false; + +/* If nonzero, it will attempt to kill a running tincd and exit. */ +int kill_tincd = 0; + +/* If nonzero, generate public/private keypair for this host/net. */ +int generate_keys = 0; + +char *identname = NULL; /* program name for syslog */ +char *pidfilename = NULL; /* pid file location */ +char *controlfilename = NULL; /* pid file location */ +char *confbase = NULL; +char *netname = NULL; + +static int status; + +static struct option const long_options[] = { + {"config", required_argument, NULL, 'c'}, + {"net", required_argument, NULL, 'n'}, + {"help", no_argument, NULL, 1}, + {"version", no_argument, NULL, 2}, + {"pidfile", required_argument, NULL, 5}, + {NULL, 0, NULL, 0} +}; + +static void usage(bool status) { + if(status) + fprintf(stderr, _("Try `%s --help\' for more information.\n"), + program_name); + 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" + " --pidfile=FILENAME Write PID to FILENAME.\n" + " --help Display this help and exit.\n" + " --version Output version information and exit.\n" + "Valid commands are:\n" + " start Start tincd.\n" + " stop Stop tincd.\n" + " restart Restart tincd.\n" + " reload Reload configuration of running tincd.\n" + " genkey [bits] Generate a new 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" + " subnets - all known subnets in the VPN\n" + " connections - all meta connections with ourself\n" + " graph - graph of the VPN in dotty format\n" + "\n")); + printf(_("Report bugs to tinc@tinc-vpn.org.\n")); + } +} + +static bool parse_options(int argc, char **argv) { + int r; + int option_index = 0; + + while((r = getopt_long(argc, argv, "c:n:", long_options, &option_index)) != EOF) { + switch (r) { + case 0: /* long option */ + break; + + case 'c': /* config file */ + confbase = xstrdup(optarg); + break; + + case 'n': /* net name given */ + netname = xstrdup(optarg); + break; + + case 1: /* show help */ + show_help = true; + break; + + case 2: /* show version */ + show_version = true; + break; + + case 5: /* write PID to a file */ + pidfilename = xstrdup(optarg); + break; + + case '?': + usage(true); + return false; + + default: + break; + } + } + + return true; +} + +/* This function prettyprints the key generation process */ + +static void indicator(int a, int b, void *p) { + switch (a) { + case 0: + fprintf(stderr, "."); + break; + + case 1: + fprintf(stderr, "+"); + break; + + case 2: + fprintf(stderr, "-"); + break; + + case 3: + switch (b) { + case 0: + fprintf(stderr, " p\n"); + break; + + case 1: + fprintf(stderr, " q\n"); + break; + + default: + fprintf(stderr, "?"); + } + break; + + default: + fprintf(stderr, "?"); + } +} + +/* + Generate a public/private RSA keypair, and ask for a file to store + them in. +*/ +static bool keygen(int bits) { + RSA *rsa_key; + FILE *f; + char *name = NULL; + char *filename; + + fprintf(stderr, _("Generating %d bits keys:\n"), bits); + rsa_key = RSA_generate_key(bits, 0x10001, indicator, NULL); + + if(!rsa_key) { + fprintf(stderr, _("Error during key generation!\n")); + return false; + } else + fprintf(stderr, _("Done.\n")); + + asprintf(&filename, "%s/rsa_key.priv", confbase); + f = ask_and_open(filename, _("private RSA 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")); + + PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL); + fclose(f); + free(filename); + + if(name) + asprintf(&filename, "%s/hosts/%s", confbase, name); + else + asprintf(&filename, "%s/rsa_key.pub", confbase); + + f = ask_and_open(filename, _("public RSA 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")); + + PEM_write_RSAPublicKey(f, rsa_key); + fclose(f); + free(filename); + + return true; +} + +/* + Set all files and paths according to netname +*/ +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); + + asprintf(&controlfilename, LOCALSTATEDIR "/run/%s.control", identname); + + if(netname) { + if(!confbase) + asprintf(&confbase, CONFDIR "/tinc/%s", netname); + else + fprintf(stderr, _("Both netname and configuration directory given, using the latter...\n")); + } else { + if(!confbase) + asprintf(&confbase, CONFDIR "/tinc"); + } +} + +int main(int argc, char **argv) { + int fd; + struct sockaddr_un addr; + program_name = argv[0]; + + 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-2007 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(strlen(controlfilename) >= sizeof addr.sun_path) { + fprintf(stderr, _("Control socket filename too long!\n")); + return 1; + } + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if(fd < 0) { + fprintf(stderr, _("Cannot create UNIX socket: %s\n"), strerror(errno)); + return 1; + } + + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, controlfilename, sizeof addr.sun_path - 1); + + if(connect(fd, (struct sockaddr *)&addr, sizeof addr) < 0) { + fprintf(stderr, _("Cannot connect to %s: %s\n"), controlfilename, strerror(errno)); + return 1; + } + + printf("Connected to %s.\n", controlfilename); + + close(fd); + + return 0; +} diff --git a/src/tincd.c b/src/tincd.c index 54ee232d..bde1b9bc 100644 --- a/src/tincd.c +++ b/src/tincd.c @@ -78,6 +78,7 @@ bool use_logfile = false; char *identname = NULL; /* program name for syslog */ char *pidfilename = NULL; /* pid file location */ +char *controlfilename = NULL; /* pid file location */ char *logfilename = NULL; /* log file location */ char **g_argv; /* a copy of the cmdline arguments */ @@ -378,6 +379,8 @@ static void make_names(void) if(!pidfilename) asprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname); + asprintf(&controlfilename, LOCALSTATEDIR "/run/%s.control", identname); + if(!logfilename) asprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname); -- 2.39.5