Use the TCP socket infrastructure for control sockets.
authorGuus Sliepen <guus@sliepen.eu.org>
Sat, 7 Nov 2009 22:43:25 +0000 (23:43 +0100)
committerGuus Sliepen <guus@sliepen.eu.org>
Sat, 7 Nov 2009 22:43:25 +0000 (23:43 +0100)
The control socket code was completely different from how meta connections are
handled, resulting in lots of extra code to handle requests.  Also, not every
operating system has UNIX sockets, so we have to resort to another type of
sockets or pipes for those anyway.  To reduce code duplication and make control
sockets work the same on all platforms, we now just connect to the TCP port
where tincd is already listening on.

To authenticate, the program that wants to control a running tinc daemon must
send the contents of a cookie file. The cookie is a random 256 bits number that
is regenerated every time tincd starts. The cookie file should only be readable
by the same user that can start a tincd.

Instead of the binary-ish protocol previously used, we now use an ASCII
protocol similar to that of the meta connections, but this can still change.

18 files changed:
src/connection.c
src/connection.h
src/control.c
src/control.h
src/control_common.h
src/edge.c
src/edge.h
src/graph.c
src/graph.h
src/node.c
src/node.h
src/protocol.c
src/protocol.h
src/protocol_auth.c
src/subnet.c
src/subnet.h
src/tincctl.c
src/tincd.c

index 2372890..519cf5b 100644 (file)
@@ -24,6 +24,7 @@
 #include "splay_tree.h"
 #include "cipher.h"
 #include "conf.h"
+#include "control_common.h"
 #include "list.h"
 #include "logger.h"
 #include "net.h"                               /* Don't ask. */
@@ -91,20 +92,19 @@ void connection_del(connection_t *c) {
        splay_delete(connection_tree, c);
 }
 
-int dump_connections(struct evbuffer *out) {
+bool dump_connections(connection_t *cdump) {
        splay_node_t *node;
        connection_t *c;
 
        for(node = connection_tree->head; node; node = node->next) {
                c = node->data;
-               if(evbuffer_add_printf(out,
-                                  " %s at %s options %x socket %d status %04x\n",
-                                  c->name, c->hostname, c->options, c->socket,
-                                  bitfield_to_int(&c->status, sizeof c->status)) == -1)
-                       return errno;
+               send_request(cdump, "%d %d %s at %s options %x socket %d status %04x",
+                               CONTROL, REQ_DUMP_CONNECTIONS,
+                               c->name, c->hostname, c->options, c->socket,
+                               bitfield_to_int(&c->status, sizeof c->status));
        }
 
-       return 0;
+       return send_request(cdump, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
 }
 
 bool read_connection_config(connection_t *c) {
index 9476996..6d29531 100644 (file)
@@ -40,7 +40,8 @@ typedef struct connection_status_t {
                int encryptout:1;                       /* 1 if we can encrypt outgoing traffic */
                int decryptin:1;                        /* 1 if we have to decrypt incoming traffic */
                int mst:1;                              /* 1 if this connection is part of a minimum spanning tree */
-               int unused:23;
+               int control:1;
+               int unused:22;
 } connection_status_t;
 
 #include "edge.h"
@@ -97,7 +98,7 @@ extern connection_t *new_connection(void) __attribute__ ((__malloc__));
 extern void free_connection(connection_t *);
 extern void connection_add(connection_t *);
 extern void connection_del(connection_t *);
-extern int dump_connections(struct evbuffer *);
+extern bool dump_connections(struct connection_t *);
 extern bool read_connection_config(connection_t *);
 
 #endif                                                 /* __TINC_CONNECTION_H__ */
index b62e143..2207815 100644 (file)
 */
 
 #include "system.h"
+#include "crypto.h"
 #include "conf.h"
 #include "control.h"
 #include "control_common.h"
 #include "graph.h"
 #include "logger.h"
+#include "protocol.h"
 #include "utils.h"
 #include "xalloc.h"
 
 static int control_socket = -1;
 static struct event control_event;
 static splay_tree_t *control_socket_tree;
-extern char *controlsocketname;
+char controlcookie[65];
+extern char *controlcookiename;
 
-static void handle_control_data(struct bufferevent *event, void *data) {
-       tinc_ctl_request_t req;
-       tinc_ctl_request_t res;
-       struct evbuffer *res_data = NULL;
-       void *req_data;
-
-       if(EVBUFFER_LENGTH(event->input) < sizeof req)
-               return;
-
-       /* Copy the structure to ensure alignment */
-       memcpy(&req, EVBUFFER_DATA(event->input), sizeof req);
-
-       if(EVBUFFER_LENGTH(event->input) < req.length)
-               return;
-       req_data = EVBUFFER_DATA(event->input) + sizeof req;
+static bool control_return(connection_t *c, int type, int error) {
+       return send_request(c, "%d %d %d", CONTROL, type, error);
+}
 
-       if(req.length < sizeof req)
-               goto failure;
+static bool control_ok(connection_t *c, int type) {
+       return control_return(c, type, 0);
+}
 
-       memset(&res, 0, sizeof res);
-       res.type = req.type;
-       res.id = req.id;
+bool control_h(connection_t *c, char *request) {
+       int type;
 
-       res_data = evbuffer_new();
-       if(res_data == NULL) {
-               res.res_errno = ENOMEM;
-               goto respond;
+       if(!c->status.control || c->allow_request != CONTROL) {
+               logger(LOG_ERR, "Unauthorized control request from %s (%s)", c->name, c->hostname);
+               return false;
        }
 
-       if(req.type == REQ_STOP) {
-               logger(LOG_NOTICE, "Got '%s' command", "stop");
-               event_loopexit(NULL);
-               goto respond;
+       if(sscanf(request, "%*d %d", &type) != 1) {
+               logger(LOG_ERR, "Got bad %s from %s (%s)", "CONTROL", c->name, c->hostname);
+               return false;
        }
 
-       if(req.type == REQ_DUMP_NODES) {
-               logger(LOG_NOTICE, "Got '%s' command", "dump nodes");
-               res.res_errno = dump_nodes(res_data);
-               goto respond;
-       }
+       switch (type) {
+               case REQ_STOP:
+                       event_loopexit(NULL);
+                       return control_ok(c, REQ_STOP);
 
-       if(req.type == REQ_DUMP_EDGES) {
-               logger(LOG_NOTICE, "Got '%s' command", "dump edges");
-               res.res_errno = dump_edges(res_data);
-               goto respond;
-       }
+               case REQ_DUMP_NODES:
+                       return dump_nodes(c);
+                       
+               case REQ_DUMP_EDGES:
+                       return dump_edges(c);
 
-       if(req.type == REQ_DUMP_SUBNETS) {
-               logger(LOG_NOTICE, "Got '%s' command", "dump subnets");
-               res.res_errno = dump_subnets(res_data);
-               goto respond;
-       }
+               case REQ_DUMP_SUBNETS:
+                       return dump_subnets(c);
 
-       if(req.type == REQ_DUMP_CONNECTIONS) {
-               logger(LOG_NOTICE, "Got '%s' command", "dump connections");
-               res.res_errno = dump_connections(res_data);
-               goto respond;
-       }
-
-       if(req.type == REQ_DUMP_GRAPH) {
-               logger(LOG_NOTICE, "Got '%s' command", "dump graph");
-               res.res_errno = dump_graph(res_data);
-               goto respond;
-       }
+               case REQ_DUMP_CONNECTIONS:
+                       return dump_connections(c);
 
-       if(req.type == REQ_PURGE) {
-               logger(LOG_NOTICE, "Got '%s' command", "purge");
-               purge();
-               goto respond;
-       }
+               case REQ_PURGE:
+                       purge();
+                       return control_ok(c, REQ_PURGE);
 
-       if(req.type == REQ_SET_DEBUG) {
-               debug_t new_debug_level;
-
-               logger(LOG_NOTICE, "Got '%s' command", "debug");
-               if(req.length != sizeof req + sizeof debug_level)
-                       res.res_errno = EINVAL;
-               else {
-                       memcpy(&new_debug_level, req_data, sizeof new_debug_level);
-                       logger(LOG_NOTICE, "Changing debug level from %d to %d",
-                                  debug_level, new_debug_level);
-                       if(evbuffer_add_printf(res_data,
-                                                                  "Changing debug level from %d to %d\n",
-                                                                  debug_level, new_debug_level) == -1)
-                               res.res_errno = errno;
-                       debug_level = new_debug_level;
+               case REQ_SET_DEBUG: {
+                       int new_level;
+                       if(sscanf(request, "%*d %*d %d", &new_level) != 1)
+                               return false;
+                       send_request(c, "%d %d %d", CONTROL, REQ_SET_DEBUG, debug_level);
+                       if(new_level >= 0)
+                               debug_level = new_level;
+                       return true;
                }
-               goto respond;
-       }
-
-       if(req.type == REQ_RETRY) {
-               logger(LOG_NOTICE, "Got '%s' command", "retry");
-               retry();
-               goto respond;
-       }
-
-       if(req.type == REQ_RELOAD) {
-               logger(LOG_NOTICE, "Got '%s' command", "reload");
-               res.res_errno = reload_configuration();
-               goto respond;
-       }
-
-       logger(LOG_DEBUG, "Malformed control command received");
-       res.res_errno = EINVAL;
-
-respond:
-       res.length = (sizeof res)
-                                + ((res_data == NULL) ? 0 : EVBUFFER_LENGTH(res_data));
-       evbuffer_drain(event->input, req.length);
-       if(bufferevent_write(event, &res, sizeof res) == -1)
-               goto failure;
-       if(res_data != NULL) {
-               if(bufferevent_write_buffer(event, res_data) == -1)
-                       goto failure;
-               evbuffer_free(res_data);
-       }
-       return;
-
-failure:
-       logger(LOG_INFO, "Closing control socket on error");
-       evbuffer_free(res_data);
-       close(event->ev_read.ev_fd);
-       splay_delete(control_socket_tree, event);
-}
 
-static void handle_control_error(struct bufferevent *event, short what, void *data) {
-       if(what & EVBUFFER_EOF)
-               logger(LOG_DEBUG, "Control socket connection closed by peer");
-       else
-               logger(LOG_DEBUG, "Error while reading from control socket: %s", strerror(errno));
+               case REQ_RETRY:
+                       retry();
+                       return control_ok(c, REQ_RETRY);
 
-       close(event->ev_read.ev_fd);
-       splay_delete(control_socket_tree, event);
-}
-
-static void handle_new_control_socket(int fd, short events, void *data) {
-       int newfd;
-       struct bufferevent *ev;
-       tinc_ctl_greeting_t greeting;
-
-       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 = bufferevent_new(newfd, handle_control_data, NULL, handle_control_error, NULL);
-       if(!ev) {
-               logger(LOG_ERR, "Could not create bufferevent for new control connection: %s", strerror(errno));
-               close(newfd);
-               return;
-       }
+               case REQ_RELOAD:
+                       logger(LOG_NOTICE, "Got '%s' command", "reload");
+                       int result = reload_configuration();
+                       return control_return(c, REQ_RELOAD, result);
 
-       memset(&greeting, 0, sizeof greeting);
-       greeting.version = TINC_CTL_VERSION_CURRENT;
-       greeting.pid = getpid();
-       if(bufferevent_write(ev, &greeting, sizeof greeting) == -1) {
-               logger(LOG_ERR,
-                          "Cannot send greeting for new control connection: %s",
-                          strerror(errno));
-               bufferevent_free(ev);
-               close(newfd);
-               return;
+               default:
+                       return send_request(c, "%d %d", CONTROL, REQ_INVALID);
        }
-
-       bufferevent_enable(ev, EV_READ);
-       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;
 }
 
 bool init_control() {
-       int result;
+       randomize(controlcookie, sizeof controlcookie / 2);
+       bin2hex(controlcookie, controlcookie, sizeof controlcookie / 2);
+       controlcookie[sizeof controlcookie - 1] = 0;
 
-#ifdef HAVE_MINGW
-       struct sockaddr_in addr;
-       memset(&addr, 0, sizeof addr);
-       addr.sin_family = AF_INET;
-       addr.sin_addr.s_addr = htonl(0x7f000001);
-       addr.sin_port = htons(55555);
-       int option = 1;
-
-       control_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-       if(control_socket < 0) {
-               logger(LOG_ERR, "Creating control socket failed: %s", sockstrerror(sockerrno));
-               goto bail;
+       FILE *f = fopen(controlcookiename, "w");
+       if(!f) {
+               logger(LOG_ERR, "Cannot write control socket cookie file %s: %s", controlcookiename, strerror(errno));
+               return false;
        }
 
-       setsockopt(control_socket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option);
+#ifdef HAVE_FCHMOD
+       fchmod(f, 0600);
 #else
-       struct sockaddr_un addr;
-       char *lastslash;
-
-       if(strlen(controlsocketname) >= sizeof addr.sun_path) {
-               logger(LOG_ERR, "Control socket filename too long!");
-               goto bail;
-       }
-
-       memset(&addr, 0, sizeof addr);
-       addr.sun_family = AF_UNIX;
-       strncpy(addr.sun_path, controlsocketname, 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));
-               goto bail;
-       }
-
-       /*
-        * Restrict connections to our control socket by ensuring the parent
-        * directory can be traversed only by root. Note this is not totally
-        * race-free unless all ancestors are writable only by trusted users,
-        * which we don't verify.
-        */
-
-       struct stat statbuf;
-       lastslash = strrchr(controlsocketname, '/');
-       if(lastslash != NULL) {
-               *lastslash = 0; /* temporarily change controlsocketname to be dir */
-               if(mkdir(controlsocketname, 0700) < 0 && errno != EEXIST) {
-                       logger(LOG_ERR, "Unable to create control socket directory %s: %s", controlsocketname, strerror(errno));
-                       *lastslash = '/';
-                       goto bail;
-               }
-
-               result = stat(controlsocketname, &statbuf);
-               *lastslash = '/';
-       } else
-               result = stat(".", &statbuf);
-
-       if(result < 0) {
-               logger(LOG_ERR, "Examining control socket directory failed: %s", strerror(errno));
-               goto bail;
-       }
-
-       if(statbuf.st_uid != 0 || (statbuf.st_mode & S_IXOTH) != 0 || (statbuf.st_gid != 0 && (statbuf.st_mode & S_IXGRP)) != 0) {
-               logger(LOG_ERR, "Control socket directory ownership/permissions insecure.");
-               goto bail;
-       }
+       chmod(controlcookiename, 0600);
 #endif
 
-       result = bind(control_socket, (struct sockaddr *)&addr, sizeof addr);
-
-       if(result < 0 && sockinuse(sockerrno)) {
-#ifndef HAVE_MINGW
-               result = connect(control_socket, (struct sockaddr *)&addr, sizeof addr);
-               if(result < 0) {
-                       logger(LOG_WARNING, "Removing old control socket.");
-                       unlink(controlsocketname);
-                       result = bind(control_socket, (struct sockaddr *)&addr, sizeof addr);
-               } else
-#endif
-               {
-                       if(netname)
-                               logger(LOG_ERR, "Another tincd is already running for net `%s'.", netname);
-                       else
-                               logger(LOG_ERR, "Another tincd is already running.");
-                       goto bail;
-               }
-       }
+       fprintf(f, "%s %s %d\n", controlcookie, myport, getpid());
+       fclose(f);
 
-       if(result < 0) {
-               logger(LOG_ERR, "Can't bind to %s: %s", controlsocketname, strerror(errno));
-               goto bail;
-       }
-
-       if(listen(control_socket, 3) < 0) {
-               logger(LOG_ERR, "Can't listen on %s: %s", controlsocketname, strerror(errno));
-               goto bail;
-       }
-
-       control_socket_tree = splay_alloc_tree((splay_compare_t)control_compare, (splay_action_t)bufferevent_free);
-
-       event_set(&control_event, control_socket, EV_READ | EV_PERSIST, handle_new_control_socket, NULL);
-       event_add(&control_event, NULL);
        return true;
-
-bail:
-       if(control_socket != -1) {
-               closesocket(control_socket);
-               control_socket = -1;
-       }
-       return false;
 }
 
 void exit_control() {
-       event_del(&control_event);
-       closesocket(control_socket);
-       unlink(controlsocketname);
+       unlink(controlcookiename);
 }
index 91708fc..ce8145a 100644 (file)
@@ -22,5 +22,6 @@
 
 extern bool init_control();
 extern void exit_control();
+extern char controlcookie[];
 
 #endif
index 720c236..c89f9de 100644 (file)
 #ifndef __TINC_CONTROL_PROTOCOL_H__
 #define __TINC_CONTROL_PROTOCOL_H__
 
+#include "protocol.h"
+
 enum request_type {
-       REQ_STOP,
+       REQ_INVALID = -1,
+       REQ_STOP = 0,
        REQ_RELOAD,
        REQ_RESTART,
        REQ_DUMP_NODES,
@@ -36,18 +39,4 @@ enum request_type {
 
 #define TINC_CTL_VERSION_CURRENT 0
 
-/* This greeting is sent by the server on socket open. */
-typedef struct tinc_ctl_greeting_t {
-       int version;
-       pid_t pid;
-} tinc_ctl_greeting_t;
-
-/* A single request or response header. */
-typedef struct tinc_ctl_request_t {
-       size_t length; /* total length, including the header */
-       enum request_type type;
-       int id;
-       int res_errno; /* used only for responses */
-} tinc_ctl_request_t;
-
 #endif
index 80bdddb..f5aa099 100644 (file)
@@ -21,6 +21,7 @@
 #include "system.h"
 
 #include "splay_tree.h"
+#include "control_common.h"
 #include "edge.h"
 #include "logger.h"
 #include "netutl.h"
@@ -105,7 +106,7 @@ edge_t *lookup_edge(node_t *from, node_t *to) {
        return splay_search(from->edge_tree, &v);
 }
 
-int dump_edges(struct evbuffer *out) {
+bool dump_edges(connection_t *c) {
        splay_node_t *node, *node2;
        node_t *n;
        edge_t *e;
@@ -116,16 +117,13 @@ int dump_edges(struct evbuffer *out) {
                for(node2 = n->edge_tree->head; node2; node2 = node2->next) {
                        e = node2->data;
                        address = sockaddr2hostname(&e->address);
-                       if(evbuffer_add_printf(out,
-                                                                  " %s to %s at %s options %x weight %d\n",
-                                                                  e->from->name, e->to->name, address,
-                                                                  e->options, e->weight) == -1) {
-                               free(address);
-                               return errno;
-                       }
+                       send_request(c, "%d %d %s to %s at %s options %x weight %d",
+                                       CONTROL, REQ_DUMP_EDGES,
+                                       e->from->name, e->to->name, address,
+                                       e->options, e->weight);
                        free(address);
                }
        }
 
-       return 0;
+       return send_request(c, "%d %d", CONTROL, REQ_DUMP_EDGES);
 }
index cf62b71..ea45f49 100644 (file)
@@ -49,6 +49,6 @@ extern void free_edge_tree(splay_tree_t *);
 extern void edge_add(edge_t *);
 extern void edge_del(edge_t *);
 extern edge_t *lookup_edge(struct node_t *, struct node_t *);
-extern int dump_edges(struct evbuffer *);
+extern bool dump_edges(struct connection_t *);
 
 #endif                                                 /* __TINC_EDGE_H__ */
index 5a0aab0..06bf36d 100644 (file)
@@ -382,44 +382,8 @@ void check_reachability() {
        }
 }
 
-/* Dump nodes and edges to a graphviz file.
-          
-   The file can be converted to an image with
-   dot -Tpng graph_filename -o image_filename.png -Gconcentrate=true
-*/
-
-int dump_graph(struct evbuffer *out) {
-       splay_node_t *node;
-       node_t *n;
-       edge_t *e;
-
-       if(evbuffer_add_printf(out, "digraph {\n") == -1)
-               return errno;
-       
-       /* dump all nodes first */
-       for(node = node_tree->head; node; node = node->next) {
-               n = node->data;
-               if(evbuffer_add_printf(out, "   %s [label = \"%s\"];\n",
-                                                          n->name, n->name) == -1)
-                       return errno;
-       }
-
-       /* now dump all edges */
-       for(node = edge_weight_tree->head; node; node = node->next) {
-               e = node->data;
-               if(evbuffer_add_printf(out, "   %s -> %s;\n",
-                                                          e->from->name, e->to->name) == -1)
-                       return errno;
-       }
-
-       if(evbuffer_add_printf(out, "}\n") == -1)
-               return errno;
-
-       return 0;
-}
-
 void graph(void) {
-    subnet_cache_flush();
+       subnet_cache_flush();
        sssp_dijkstra();
        check_reachability();
        mst_kruskal();
index 4f9bb5d..c8d5fda 100644 (file)
@@ -24,6 +24,5 @@
 extern void graph(void);
 extern void mst_kruskal(void);
 extern void sssp_bfs(void);
-extern int dump_graph(struct evbuffer *);
 
 #endif /* __TINC_GRAPH_H__ */
index 75f01e3..e3eca44 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "system.h"
 
+#include "control_common.h"
 #include "splay_tree.h"
 #include "logger.h"
 #include "net.h"
@@ -154,19 +155,18 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
        }
 }
 
-int dump_nodes(struct evbuffer *out) {
+bool dump_nodes(connection_t *c) {
        splay_node_t *node;
        node_t *n;
 
        for(node = node_tree->head; node; node = node->next) {
                n = node->data;
-               if(evbuffer_add_printf(out, " %s at %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",
+               send_request(c, "%d %d %s at %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)", CONTROL, REQ_DUMP_NODES,
                           n->name, n->hostname, cipher_get_nid(&n->outcipher),
                           digest_get_nid(&n->outdigest), digest_length(&n->outdigest), n->outcompression,
                           n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-",
-                          n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu) == -1)
-                       return errno;
+                          n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu);
        }
 
-       return 0;
+       return send_request(c, "%d %d", CONTROL, REQ_DUMP_NODES);
 }
index 02e16c6..f5ebde3 100644 (file)
@@ -89,7 +89,7 @@ extern void node_add(node_t *);
 extern void node_del(node_t *);
 extern node_t *lookup_node(char *);
 extern node_t *lookup_node_udp(const sockaddr_t *);
-extern int dump_nodes(struct evbuffer *);
+extern bool dump_nodes(struct connection_t *);
 extern void update_node_udp(node_t *, const sockaddr_t *);
 
 #endif                                                 /* __TINC_NODE_H__ */
index ac4b767..02f2841 100644 (file)
@@ -38,7 +38,7 @@ static bool (*request_handlers[])(connection_t *, char *) = {
                ping_h, pong_h,
                add_subnet_h, del_subnet_h,
                add_edge_h, del_edge_h,
-               key_changed_h, req_key_h, ans_key_h, tcppacket_h,
+               key_changed_h, req_key_h, ans_key_h, tcppacket_h, control_h,
 };
 
 /* Request names */
@@ -48,7 +48,7 @@ static char (*request_name[]) = {
                "STATUS", "ERROR", "TERMREQ",
                "PING", "PONG",
                "ADD_SUBNET", "DEL_SUBNET",
-               "ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET",
+               "ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET", "CONTROL",
 };
 
 static splay_tree_t *past_request_tree;
index a5ac8ed..f290148 100644 (file)
@@ -44,6 +44,7 @@ typedef enum request_t {
        ADD_EDGE, DEL_EDGE,
        KEY_CHANGED, REQ_KEY, ANS_KEY,
        PACKET,
+       CONTROL,
        LAST                                            /* Guardian for the highest request number */
 } request_t;
 
@@ -119,5 +120,6 @@ extern bool key_changed_h(struct connection_t *, char *);
 extern bool req_key_h(struct connection_t *, char *);
 extern bool ans_key_h(struct connection_t *, char *);
 extern bool tcppacket_h(struct connection_t *, char *);
+extern bool control_h(struct connection_t *, char *);
 
 #endif                                                 /* __TINC_PROTOCOL_H__ */
index a38b9ad..8527230 100644 (file)
@@ -23,6 +23,8 @@
 #include "splay_tree.h"
 #include "conf.h"
 #include "connection.h"
+#include "control.h"
+#include "control_common.h"
 #include "crypto.h"
 #include "edge.h"
 #include "graph.h"
@@ -51,6 +53,15 @@ bool id_h(connection_t *c, char *request) {
                return false;
        }
 
+       /* Check if this is a control connection */
+
+       if(name[0] == '^' && !strcmp(name + 1, controlcookie)) {
+               c->status.control = true;
+               c->allow_request = CONTROL;
+               c->last_ping_time = time(NULL) + 3600;
+               return send_request(c, "%d %d %d", ACK, TINC_CTL_VERSION_CURRENT, getpid());
+       }
+
        /* Check if identity is a valid name */
 
        if(!check_id(name)) {
index 29ea96d..0669b8a 100644 (file)
@@ -21,6 +21,7 @@
 #include "system.h"
 
 #include "splay_tree.h"
+#include "control_common.h"
 #include "device.h"
 #include "logger.h"
 #include "net.h"
@@ -273,7 +274,7 @@ bool str2net(subnet_t *subnet, const char *subnetstr) {
 
 bool net2str(char *netstr, int len, const subnet_t *subnet) {
        if(!netstr || !subnet) {
-               logger(LOG_ERR, "net2str() was called with netstr=%p, subnet=%p!\n", netstr, subnet);
+               logger(LOG_ERR, "net2str() was called with netstr=%p, subnet=%p!", netstr, subnet);
                return false;
        }
 
@@ -527,7 +528,7 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
                free(envp[i]);
 }
 
-int dump_subnets(struct evbuffer *out) {
+bool dump_subnets(connection_t *c) {
        char netstr[MAXNETSTR];
        subnet_t *subnet;
        splay_node_t *node;
@@ -536,10 +537,10 @@ int dump_subnets(struct evbuffer *out) {
                subnet = node->data;
                if(!net2str(netstr, sizeof netstr, subnet))
                        continue;
-               if(evbuffer_add_printf(out, " %s owner %s\n",
-                                                          netstr, subnet->owner->name) == -1)
-                       return errno;
+               send_request(c, "%d %d %s owner %s",
+                               CONTROL, REQ_DUMP_SUBNETS,
+                               netstr, subnet->owner->name);
        }
 
-       return 0;
+       return send_request(c, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
 }
index c6aa93f..466eb20 100644 (file)
@@ -80,7 +80,7 @@ extern subnet_t *lookup_subnet(const struct node_t *, const subnet_t *);
 extern subnet_t *lookup_subnet_mac(const mac_t *);
 extern subnet_t *lookup_subnet_ipv4(const ipv4_t *);
 extern subnet_t *lookup_subnet_ipv6(const ipv6_t *);
-extern int dump_subnets(struct evbuffer *);
+extern bool dump_subnets(struct connection_t *);
 extern void subnet_cache_flush(void);
 
 #endif                                                 /* __TINC_SUBNET_H__ */
index 784c6a9..8a07dfa 100644 (file)
@@ -42,8 +42,10 @@ int kill_tincd = 0;
 /* If nonzero, generate public/private keypair for this host/net. */
 int generate_keys = 0;
 
+static char *name = NULL;
 static char *identname = NULL;                         /* program name for syslog */
-static char *controlsocketname = NULL;                 /* pid file location */
+static char *controlcookiename = NULL;                 /* cookie file location */
+static char controlcookie[1024];
 char *netname = NULL;
 char *confbase = NULL;
 
@@ -69,7 +71,7 @@ static void usage(bool status) {
                printf("Valid options are:\n"
                                "  -c, --config=DIR              Read configuration options from DIR.\n"
                                "  -n, --net=NETNAME             Connect to net NETNAME.\n"
-                               "      --controlsocket=FILENAME  Open control socket at FILENAME.\n"
+                               "      --controlcookie=FILENAME  Read control socket from FILENAME.\n"
                                "      --help                    Display this help and exit.\n"
                                "      --version                 Output version information and exit.\n"
                                "\n"
@@ -121,7 +123,7 @@ static bool parse_options(int argc, char **argv) {
                                break;
 
                        case 5:                                 /* open control socket here */
-                               controlsocketname = xstrdup(optarg);
+                               controlcookiename = xstrdup(optarg);
                                break;
 
                        case '?':
@@ -196,7 +198,6 @@ FILE *ask_and_open(const char *filename, const char *what, const char *mode) {
 static bool keygen(int bits) {
        rsa_t key;
        FILE *f;
-       char *name = NULL;
        char *filename;
 
        fprintf(stderr, "Generating %d bits keys:\n", bits);
@@ -272,14 +273,16 @@ static void make_names(void) {
                                        xasprintf(&confbase, "%s", installdir);
                        }
                }
+               if(!controlcookiename)
+                       xasprintf(&controlcookiename, "%s/cookie", confbase);
                RegCloseKey(key);
                if(*installdir)
                        return;
        }
 #endif
 
-       if(!controlsocketname)
-               xasprintf(&controlsocketname, "%s/run/%s.control/socket", LOCALSTATEDIR, identname);
+       if(!controlcookiename)
+               xasprintf(&controlcookiename, "%s/run/%s.control/socket", LOCALSTATEDIR, identname);
 
        if(netname) {
                if(!confbase)
@@ -292,123 +295,67 @@ static void make_names(void) {
        }
 }
 
-static int fullread(int fd, void *data, size_t datalen) {
-       int rv, len = 0;
+static bool recvline(int fd, char *line, size_t len) {
+       static char buffer[4096];
+       static size_t blen = 0;
+       char *newline = NULL;
 
-       while(len < datalen) {
-               rv = recv(fd, data + len, datalen - len, 0);
-               if(rv == -1 && errno == EINTR)
+       while(!(newline = memchr(buffer, '\n', blen))) {
+               int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
+               if(result == -1 && errno == EINTR)
                        continue;
-               else if(rv == -1)
-                       return rv;
-               else if(rv == 0) {
-#ifdef HAVE_MINGW
-                       errno = 0;
-#else
-                       errno = ENODATA;
-#endif
-                       return -1;
-               }
-               len += rv;
+               else if(result <= 0)
+                       return false;
+               blen += result;
        }
-       return 0;
-}
-
-/*
-   Send a request (raw)
-*/
-static int send_ctl_request(int fd, enum request_type type,
-                                                  void const *outdata, size_t outdatalen,
-                                                  int *res_errno_p, void **indata_p,
-                                                  size_t *indatalen_p) {
-       tinc_ctl_request_t req;
-       int rv;
-       void *indata;
-
-       memset(&req, 0, sizeof req);
-       req.length = sizeof req + outdatalen;
-       req.type = type;
-       req.res_errno = 0;
-
-#ifdef HAVE_MINGW
-       if(send(fd, (void *)&req, sizeof req, 0) != sizeof req || send(fd, outdata, outdatalen, 0) != outdatalen)
-               return -1;
-#else
-       struct iovec vector[2] = {
-               {&req, sizeof req},
-               {(void*) outdata, outdatalen}
-       };
-
-       if(res_errno_p == NULL)
-               return -1;
 
-       while((rv = writev(fd, vector, 2)) == -1 && errno == EINTR) ;
-       if(rv != req.length)
-               return -1;
-#endif
-
-       if(fullread(fd, &req, sizeof req) == -1)
-               return -1;
-
-       if(req.length < sizeof req) {
-               errno = EINVAL;
-               return -1;
-       }
+       if(newline - buffer >= len)
+               return false;
 
-       if(req.length > sizeof req) {
-               if(indata_p == NULL) {
-                       errno = EINVAL;
-                       return -1;
-               }
+       len = newline - buffer;
 
-               indata = xmalloc(req.length - sizeof req);
+       memcpy(line, buffer, len);
+       line[len] = 0;
+       memmove(buffer, newline + 1, blen - len - 1);
+       blen -= len + 1;
 
-               if(fullread(fd, indata, req.length - sizeof req) == -1) {
-                       free(indata);
-                       return -1;
-               }
+       return true;
+}
 
-               *indata_p = indata;
-               if(indatalen_p != NULL)
-                       *indatalen_p = req.length - sizeof req;
-       }
+static bool sendline(int fd, char *format, ...) {
+       static char buffer[4096];
+       char *p = buffer;
+       size_t blen = 0;
+       va_list ap;
 
-       *res_errno_p = req.res_errno;
+       va_start(ap, format);
+       blen = vsnprintf(buffer, sizeof buffer, format, ap);
+       va_end(ap);
 
-       return 0;
-}
-
-/*
-   Send a request (with printfs)
-*/
-static int send_ctl_request_cooked(int fd, enum request_type type, void const *outdata, size_t outdatalen) {
-       int res_errno = -1;
-       char *buf = NULL;
-       size_t buflen = 0;
-
-       if(send_ctl_request(fd, type, outdata, outdatalen, &res_errno,
-                                               (void**) &buf, &buflen)) {
-               fprintf(stderr, "Error sending request: %s\n", strerror(errno));
-               return -1;
-       }
+       if(blen < 0 || blen >= sizeof buffer)
+               return false;
 
-       if(buf != NULL) {
-               printf("%*s", (int)buflen, buf);
-               free(buf);
-       }
+       buffer[blen] = '\n';
+       blen++;
 
-       if(res_errno != 0) {
-               fprintf(stderr, "Server reported error: %s\n", strerror(res_errno));
-               return -1;
+       while(blen) {
+               int result = send(fd, p, blen, 0);
+               if(result == -1 && errno == EINTR)
+                       continue;
+               else if(result <= 0);
+                       return false;
+               p += result;
+               blen -= result;
        }
 
-       return 0;
+       return true;    
 }
 
 int main(int argc, char *argv[], char *envp[]) {
-       tinc_ctl_greeting_t greeting;
        int fd;
        int result;
+       int port;
+       int pid;
 
        program_name = argv[0];
 
@@ -460,17 +407,28 @@ int main(int argc, char *argv[], char *envp[]) {
         * ancestors are writable only by trusted users, which we don't verify.
         */
 
+       FILE *f = fopen(controlcookiename, "r");
+       if(!f) {
+               fprintf(stderr, "Could not open control socket cookie file %s: %s\n", controlcookiename, strerror(errno));
+               return 1;
+       }
+       if(fscanf(f, "%1024s %d %d", controlcookie, &port, &pid) != 3) {
+               fprintf(stderr, "Could not parse control socket cookie file %s\n", controlcookiename);
+               return 1;
+       }
+
 #ifdef HAVE_MINGW
        if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
                fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
                return 1;
        }
+#endif
 
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof addr);
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(0x7f000001);
-       addr.sin_port = htons(55555);
+       addr.sin_port = htons(port);
 
        fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(fd < 0) {
@@ -483,77 +441,69 @@ int main(int argc, char *argv[], char *envp[]) {
        if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
                fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
        }
-#else
-       struct sockaddr_un addr;
-       struct stat statbuf;
-       char *lastslash = strrchr(controlsocketname, '/');
-       if(lastslash != NULL) {
-               /* control socket is not in cwd; stat its parent */
-               *lastslash = 0;
-               result = stat(controlsocketname, &statbuf);
-               *lastslash = '/';
-       } else
-               result = stat(".", &statbuf);
-
-       if(result < 0) {
-               fprintf(stderr, "Unable to check control socket directory permissions: %s\n", strerror(errno));
-               return 1;
-       }
-
-       if(statbuf.st_uid != 0 || (statbuf.st_mode & S_IXOTH) != 0 || (statbuf.st_gid != 0 && (statbuf.st_mode & S_IXGRP)) != 0) {
-               fprintf(stderr, "Insecure permissions on control socket directory\n");
-               return 1;
-       }
-
-       if(strlen(controlsocketname) >= 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, controlsocketname, sizeof addr.sun_path - 1);
-#endif
 
        if(connect(fd, (struct sockaddr *)&addr, sizeof addr) < 0) {
                        
-               fprintf(stderr, "Cannot connect to %s: %s\n", controlsocketname, sockstrerror(sockerrno));
+               fprintf(stderr, "Cannot connect to %s: %s\n", controlcookiename, sockstrerror(sockerrno));
                return 1;
        }
 
-       if(fullread(fd, &greeting, sizeof greeting) == -1) {
+       char line[4096];
+       char data[4096];
+       int code, version, req;
+
+       if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
                fprintf(stderr, "Cannot read greeting from control socket: %s\n",
                                sockstrerror(sockerrno));
                return 1;
        }
 
-       if(greeting.version != TINC_CTL_VERSION_CURRENT) {
-               fprintf(stderr, "Version mismatch: server %d, client %d\n",
-                               greeting.version, TINC_CTL_VERSION_CURRENT);
+       sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
+       
+       if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
+               fprintf(stderr, "Could not fully establish control socket connection\n");
                return 1;
        }
 
        if(!strcasecmp(argv[optind], "pid")) {
-               printf("%d\n", greeting.pid);
+               printf("%d\n", pid);
                return 0;
        }
 
        if(!strcasecmp(argv[optind], "stop")) {
-               return send_ctl_request_cooked(fd, REQ_STOP, NULL, 0) != -1;
+               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;
+               }
+               return 0;
        }
 
        if(!strcasecmp(argv[optind], "reload")) {
-               return send_ctl_request_cooked(fd, REQ_RELOAD, NULL, 0) != -1;
+               sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
+                       fprintf(stderr, "Could not reload tinc daemon\n");
+                       return 1;
+               }
+               return 0;
        }
-       
+
        if(!strcasecmp(argv[optind], "restart")) {
-               return send_ctl_request_cooked(fd, REQ_RESTART, NULL, 0) != -1;
+               sendline(fd, "%d %d", CONTROL, REQ_RESTART);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RESTART || result) {
+                       fprintf(stderr, "Could not restart tinc daemon\n");
+                       return 1;
+               }
+               return 0;
+       }
+
+       if(!strcasecmp(argv[optind], "retry")) {
+               sendline(fd, "%d %d", CONTROL, REQ_RETRY);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
+                       fprintf(stderr, "Could not retry outgoing connections\n");
+                       return 1;
+               }
+               return 0;
        }
 
        if(!strcasecmp(argv[optind], "dump")) {
@@ -563,53 +513,84 @@ int main(int argc, char *argv[], char *envp[]) {
                        return 1;
                }
 
-               if(!strcasecmp(argv[optind+1], "nodes")) {
-                       return send_ctl_request_cooked(fd, REQ_DUMP_NODES, NULL, 0) != -1;
-               }
-
-               if(!strcasecmp(argv[optind+1], "edges")) {
-                       return send_ctl_request_cooked(fd, REQ_DUMP_EDGES, NULL, 0) != -1;
-               }
-
-               if(!strcasecmp(argv[optind+1], "subnets")) {
-                       return send_ctl_request_cooked(fd, REQ_DUMP_SUBNETS, NULL, 0) != -1;
+               bool do_graph = false;
+               int dumps = 1;
+
+               if(!strcasecmp(argv[optind+1], "nodes"))
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
+               else if(!strcasecmp(argv[optind+1], "edges"))
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
+               else if(!strcasecmp(argv[optind+1], "subnets"))
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
+               else if(!strcasecmp(argv[optind+1], "connections"))
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
+               else if(!strcasecmp(argv[optind+1], "graph")) {
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
+                       do_graph = true;
+                       dumps = 2;
+                       printf("digraph {\n");
+               } else {
+                       fprintf(stderr, "Unknown dump type '%s'.\n", argv[optind+1]);
+                       usage(true);
+                       return 1;
                }
 
-               if(!strcasecmp(argv[optind+1], "connections")) {
-                       return send_ctl_request_cooked(fd, REQ_DUMP_CONNECTIONS, NULL, 0) != -1;
-               }
+               while(recvline(fd, line, sizeof line)) {
+                       char node1[4096], node2[4096];
+                       int n = sscanf(line, "%d %d %s to %s", &code, &req, &node1, &node2);
+                       if(n == 2) {
+                               if(do_graph && req == REQ_DUMP_NODES)
+                                       continue;
+                               else {
+                                       if(do_graph)
+                                               printf("}\n");
+                                       return 0;
+                               }
+                       }
+                       if(n < 2)
+                               break;
 
-               if(!strcasecmp(argv[optind+1], "graph")) {
-                       return send_ctl_request_cooked(fd, REQ_DUMP_GRAPH, NULL, 0) != -1;
+                       if(!do_graph)
+                               printf("%s\n", line + 5);
+                       else {
+                               if(req == REQ_DUMP_NODES)
+                                       printf(" %s [label = \"%s\"];\n", node1, node1);
+                               else
+                                       printf(" %s -> %s;\n", node1, node2);
+                       }
                }
 
-               fprintf(stderr, "Unknown dump type '%s'.\n", argv[optind+1]);
-               usage(true);
+               fprintf(stderr, "Error receiving dump\n");
                return 1;
        }
 
        if(!strcasecmp(argv[optind], "purge")) {
-               return send_ctl_request_cooked(fd, REQ_PURGE, NULL, 0) != -1;
+               sendline(fd, "%d %d", CONTROL, REQ_PURGE);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
+                       fprintf(stderr, "Could not purge tinc daemon\n");
+                       return 1;
+               }
+               return 0;
        }
 
        if(!strcasecmp(argv[optind], "debug")) {
-               int debuglevel;
+               int debuglevel, origlevel;
 
                if(argc != optind + 2) {
                        fprintf(stderr, "Invalid arguments.\n");
                        return 1;
                }
                debuglevel = atoi(argv[optind+1]);
-               return send_ctl_request_cooked(fd, REQ_SET_DEBUG, &debuglevel,
-                                                                          sizeof debuglevel) != -1;
-       }
 
-       if(!strcasecmp(argv[optind], "retry")) {
-               return send_ctl_request_cooked(fd, REQ_RETRY, NULL, 0) != -1;
-       }
+               sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
+                       fprintf(stderr, "Could not purge tinc daemon\n");
+                       return 1;
+               }
 
-       if(!strcasecmp(argv[optind], "reload")) {
-               return send_ctl_request_cooked(fd, REQ_RELOAD, NULL, 0) != -1;
+               fprintf(stderr, "Old level %d, new level %d\n", origlevel, debuglevel);
+               return 0;
        }
 
        fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
index 1761dc2..f6cda97 100644 (file)
@@ -78,8 +78,8 @@ static const char *switchuser = NULL;
 bool use_logfile = false;
 
 char *identname = NULL;                                /* program name for syslog */
-char *controlsocketname = NULL;                        /* control socket location */
 char *logfilename = NULL;                      /* log file location */
+char *controlcookiename = NULL;
 char **g_argv;                                 /* a copy of the cmdline arguments */
 
 static int status;
@@ -96,7 +96,7 @@ static struct option const long_options[] = {
        {"chroot", no_argument, NULL, 'R'},
        {"user", required_argument, NULL, 'U'},
        {"logfile", optional_argument, NULL, 4},
-       {"controlsocket", required_argument, NULL, 5},
+       {"controlcookie", required_argument, NULL, 5},
        {NULL, 0, NULL, 0}
 };
 
@@ -117,7 +117,7 @@ static void usage(bool status) {
                                "  -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"
-                               "      --controlsocket=FILENAME  Open control socket at FILENAME.\n"
+                               "      --controlcookie=FILENAME  Write control socket cookie to FILENAME.\n"
                                "      --bypass-security         Disables meta protocol security, for debugging.\n"
                                "  -R, --chroot                  chroot to NET dir at startup.\n"
                                "  -U, --user=USER               setuid to given USER at startup.\n"                            "      --help                    Display this help and exit.\n"
@@ -190,7 +190,7 @@ static bool parse_options(int argc, char **argv) {
                                break;
 
                        case 5:                                 /* open control socket here */
-                               controlsocketname = xstrdup(optarg);
+                               controlcookiename = xstrdup(optarg);
                                break;
 
                        case '?':
@@ -231,6 +231,8 @@ static void make_names(void) {
                                else
                                        xasprintf(&confbase, "%s", installdir);
                        }
+                       if(!controlcookiename)
+                               xasprintf(&controlcookiename, "%s/cookie", confbase);
                }
                RegCloseKey(key);
                if(*installdir)
@@ -238,9 +240,6 @@ static void make_names(void) {
        }
 #endif
 
-       if(!controlsocketname)
-               xasprintf(&controlsocketname, "%s/run/%s.control/socket", LOCALSTATEDIR, identname);
-
        if(!logfilename)
                xasprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname);
 
@@ -258,7 +257,7 @@ static void make_names(void) {
 static void free_names() {
        if (identname) free(identname);
        if (netname) free(netname);
-       if (controlsocketname) free(controlsocketname);
+       if (controlcookiename) free(controlcookiename);
        if (logfilename) free(logfilename);
        if (confbase) free(confbase);
 }
@@ -359,9 +358,6 @@ int main(int argc, char **argv) {
                return 1;
        }
 
-       if(!init_control())
-               return 1;
-
        g_argv = argv;
 
        init_configuration(&config_tree);
@@ -410,6 +406,9 @@ int main2(int argc, char **argv) {
        if(!setup_network())
                goto end;
 
+       if(!init_control())
+               return 1;
+
        /* Initiate all outgoing connections. */
 
        try_outgoing_connections();
@@ -449,9 +448,7 @@ int main2(int argc, char **argv) {
 end:
        logger(LOG_NOTICE, "Terminating");
 
-#ifndef HAVE_MINGW
        exit_control();
-#endif
 
        crypto_exit();