[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

control interface via unix domain socket



Hi,

this is a first cut at a patch to also offer the control interface over
a unix domain socket, not just a TCP port.

Things that remain to be done:
 - make relative filenames relative to datadir?
 - document in manpage

Please let me know if this is something that should go in, and if yes
how to change it so that it can.




To test you can use socat, e.g. socat - UNIX-CONNECT:./foo
Make sure you don't forget the CRLF line endings, i.e. do ^V^M<enter> at
the end of a line.

here a diff -w, a normal diff is attached:

Index: configure.in
===================================================================
--- configure.in	(revision 10485)
+++ configure.in	(working copy)
@@ -240,7 +240,7 @@
 
 AC_CHECK_HEADERS(unistd.h string.h signal.h ctype.h sys/stat.h sys/types.h fcntl.h sys/fcntl.h sys/time.h errno.h assert.h time.h, , AC_MSG_WARN(Some headers were not found, compilation may fail.  If compilation succeeds, please send your orconfig.h to the developers so we can fix this warning.))
 
-AC_CHECK_HEADERS(netdb.h sys/ioctl.h sys/socket.h arpa/inet.h netinet/in.h pwd.h grp.h)
+AC_CHECK_HEADERS(netdb.h sys/ioctl.h sys/socket.h arpa/inet.h netinet/in.h pwd.h grp.h sys/un.h)
 
 dnl These headers are not essential
 
Index: src/or/config.c
===================================================================
--- src/or/config.c	(revision 10487)
+++ src/or/config.c	(working copy)
@@ -149,6 +149,9 @@
   VAR("ContactInfo",         STRING,   ContactInfo,          NULL),
   VAR("ControlListenAddress",LINELIST, ControlListenAddress, NULL),
   VAR("ControlPort",         UINT,     ControlPort,          "0"),
+#ifdef HAVE_SYS_UN_H
+  VAR("ControlSocket",       LINELIST, ControlSocket,        NULL),
+#endif
   VAR("CookieAuthentication",BOOL,     CookieAuthentication, "0"),
   VAR("DataDirectory",       STRING,   DataDirectory,        NULL),
   OBSOLETE("DebugLogFile"),
Index: src/or/or.h
===================================================================
--- src/or/or.h	(revision 10487)
+++ src/or/or.h	(working copy)
@@ -56,6 +56,9 @@
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
@@ -239,7 +242,9 @@
 #define CONN_TYPE_AP_NATD_LISTENER 14
 /** Type for sockets listening for DNS requests. */
 #define CONN_TYPE_AP_DNS_LISTENER 15
-#define _CONN_TYPE_MAX 15
+/** Type for unix domain sockets for control interface */
+#define CONN_TYPE_CONTROL_UDS_LISTENER 16
+#define _CONN_TYPE_MAX 16
 
 #define CONN_IS_EDGE(x) \
   ((x)->type == CONN_TYPE_EXIT || (x)->type == CONN_TYPE_AP)
@@ -1803,6 +1808,7 @@
   int TransPort;
   int NatdPort; /**< Port to listen on for transparent natd connections. */
   int ControlPort; /**< Port to listen on for control connections. */
+  config_line_t * ControlSocket; /**< Unix Domain Socket to listen on for control connections. */
   int DirPort; /**< Port to listen on for directory connections. */
   int DNSPort; /**< Port to listen on for DNS requests. */
   int AssumeReachable; /**< Whether to publish our descriptor regardless. */
Index: src/or/connection.c
===================================================================
--- src/or/connection.c	(revision 10487)
+++ src/or/connection.c	(working copy)
@@ -54,6 +54,7 @@
     case CONN_TYPE_DIR: return "Directory";
     case CONN_TYPE_CPUWORKER: return "CPU worker";
     case CONN_TYPE_CONTROL_LISTENER: return "Control listener";
+    case CONN_TYPE_CONTROL_UDS_LISTENER: return "Control unix-socket listener";
     case CONN_TYPE_CONTROL: return "Control";
     default:
       log_warn(LD_BUG, "unknown connection type %d", type);
@@ -78,6 +79,7 @@
     case CONN_TYPE_AP_DNS_LISTENER:
     case CONN_TYPE_DIR_LISTENER:
     case CONN_TYPE_CONTROL_LISTENER:
+    case CONN_TYPE_CONTROL_UDS_LISTENER:
       if (state == LISTENER_STATE_READY)
         return "ready";
       break;
@@ -295,6 +297,13 @@
     buf_free(conn->outbuf);
   }
 
+  if (conn->type == CONN_TYPE_CONTROL_UDS_LISTENER) {
+    if (unlink(conn->address) < 0 && errno != ENOENT) {
+      log_warn(LD_NET, "Could not unlink %s: %s", conn->address,
+                       strerror(errno));
+    }
+  }
+
   tor_free(conn->address);
 
   if (connection_speaks_cells(conn)) {
@@ -343,6 +352,7 @@
     connection_or_remove_from_identity_map(TO_OR_CONN(conn));
   }
 
+
   memset(conn, 0xAA, sizeof(connection_t)); /* poison memory */
   tor_free(mem);
 }
@@ -632,16 +642,30 @@
 connection_create_listener(const char *listenaddress, uint16_t listenport,
                            int type)
 {
-  struct sockaddr_in listenaddr; /* where to bind */
   char *address = NULL;
   connection_t *conn;
   uint16_t usePort;
+  int s; /* the socket we're going to make */
+  int is_inet = (type != CONN_TYPE_CONTROL_UDS_LISTENER);
+  int is_tcp = (is_inet && type != CONN_TYPE_AP_DNS_LISTENER);
+
+  if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
+    int n_conns = get_n_open_sockets();
+    log_warn(LD_NET,"Failing because we have %d connections already. Please "
+             "raise your ulimit -n.", n_conns);
+    control_event_general_status(LOG_WARN, "TOO_MANY_CONNECTIONS CURRENT=%d",
+                                 n_conns);
+    return NULL;
+  }
+
+
+
+  if (is_inet) {
+    struct sockaddr_in listenaddr; /* where to bind */
   uint32_t addr;
-  int s; /* the socket we're going to make */
 #ifndef MS_WINDOWS
   int one=1;
 #endif
-  int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
 
   memset(&listenaddr,0,sizeof(struct sockaddr_in));
   if (parse_addr_port(LOG_WARN, listenaddress, &address, &addr, &usePort)<0) {
@@ -659,15 +683,6 @@
   log_notice(LD_NET, "Opening %s on %s:%d",
              conn_type_to_string(type), address, usePort);
 
-  if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
-    int n_conns = get_n_open_sockets();
-    log_warn(LD_NET,"Failing because we have %d connections already. Please "
-             "raise your ulimit -n.", n_conns);
-    control_event_general_status(LOG_WARN, "TOO_MANY_CONNECTIONS CURRENT=%d",
-                                 n_conns);
-    return NULL;
-  }
-
   s = tor_open_socket(PF_INET,
                       is_tcp ? SOCK_STREAM : SOCK_DGRAM,
                       is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
@@ -703,7 +718,46 @@
       goto err;
     }
   }
+  } else {
+#ifdef HAVE_SYS_UN_H
+    struct sockaddr_un sockaddr;
+    sockaddr.sun_family = AF_UNIX;
+    int len;
+    address = tor_strdup(listenaddress);
 
+    log_notice(LD_NET, "Opening %s on %s",
+	       conn_type_to_string(type), address);
+
+    strncpy(sockaddr.sun_path, address, sizeof(sockaddr.sun_path));
+    if (unlink(sockaddr.sun_path) < 0 && errno != ENOENT) {
+      log_warn(LD_NET, "Could not unlink %s: %s", address,
+                       strerror(errno));
+      goto err;
+    }
+    s = tor_open_socket(AF_UNIX, SOCK_STREAM, 0);
+    if (s < 0) {
+      log_warn(LD_NET,"Socket creation failed: %s.", strerror(errno));
+      goto err;
+    }
+
+    len = strlen(sockaddr.sun_path) + sizeof(sockaddr.sun_family);
+    if (bind(s, (struct sockaddr *)&sockaddr, len) == -1) {
+      log_warn(LD_NET,"Bind to %s failed: %s.", address,
+               tor_socket_strerror(tor_socket_errno(s)));
+      goto err;
+    }
+
+    if (listen(s,SOMAXCONN) < 0) {
+      log_warn(LD_NET, "Could not listen on %s: %s", address,
+	       tor_socket_strerror(tor_socket_errno(s)));
+      tor_close_socket(s);
+      goto err;
+    }
+#else
+    assert(0);
+#endif /* HAVE_SYS_UN_H */
+  }
+
   set_socket_nonblocking(s);
 
   conn = connection_new(type);
@@ -722,7 +776,7 @@
             conn_type_to_string(type), usePort);
 
   conn->state = LISTENER_STATE_READY;
-  if (is_tcp) {
+  if (is_tcp || !is_inet) {
     connection_start_reading(conn);
   } else {
     tor_assert(type == CONN_TYPE_AP_DNS_LISTENER);
@@ -801,6 +855,9 @@
 
   set_socket_nonblocking(news);
 
+  if (((struct sockaddr*)addrbuf)->sa_family == AF_INET) {
+    assert(conn->type != CONN_TYPE_CONTROL_UDS_LISTENER);
+
   if (check_sockaddr_in((struct sockaddr*)addrbuf, remotelen, LOG_INFO)<0) {
     log_info(LD_NET,
              "accept() returned a strange address; trying getsockname().");
@@ -851,6 +908,20 @@
   newconn->port = ntohs(remote.sin_port);
   newconn->address = tor_dup_addr(newconn->addr);
 
+  } else if (((struct sockaddr*)addrbuf)->sa_family == AF_UNIX) {
+    assert(conn->type == CONN_TYPE_CONTROL_UDS_LISTENER);
+
+    newconn = connection_new(new_type);
+    newconn->s = news;
+
+    /* remember the remote address  --  do we have anything sane to put here? */
+    newconn->addr = 0;
+    newconn->port = 1;
+    newconn->address = tor_strdup(conn->address);
+  } else {
+    assert(0);
+  };
+
   if (connection_add(newconn) < 0) { /* no space, forget it */
     connection_free(newconn);
     return 0; /* no need to tear down the parent */
@@ -1042,6 +1113,12 @@
     line = NULL;
     SMARTLIST_FOREACH(launch, config_line_t *, wanted,
       {
+	if (type == CONN_TYPE_CONTROL_UDS_LISTENER) {
+	  if (!strcasecmp(wanted->value, conn->address)) {
+	    line = wanted;
+	    break;
+	  }
+	} else {
         char *address=NULL;
         uint16_t port;
         if (!parse_addr_port(LOG_WARN, wanted->value, &address, NULL, &port)) {
@@ -1054,6 +1131,7 @@
             break;
           }
         }
+	}
       });
     if (! line) {
       /* This one isn't configured. Close it. */
@@ -1142,6 +1220,11 @@
                       options->ControlPort, "127.0.0.1",
                       replaced_conns, new_conns, 0)<0)
     return -1;
+  if (retry_listeners(CONN_TYPE_CONTROL_UDS_LISTENER,
+                      options->ControlSocket,
+                      options->ControlSocket ? 1 : 0, NULL,
+                      replaced_conns, new_conns, 0)<0)
+    return -1;
 
   return 0;
 }
@@ -1546,6 +1629,7 @@
     case CONN_TYPE_DIR_LISTENER:
       return connection_handle_listener_read(conn, CONN_TYPE_DIR);
     case CONN_TYPE_CONTROL_LISTENER:
+    case CONN_TYPE_CONTROL_UDS_LISTENER:
       return connection_handle_listener_read(conn, CONN_TYPE_CONTROL);
     case CONN_TYPE_AP_DNS_LISTENER:
       /* This should never happen; eventdns.c handles the reads here. */
@@ -2631,6 +2715,7 @@
     case CONN_TYPE_AP_NATD_LISTENER:
     case CONN_TYPE_DIR_LISTENER:
     case CONN_TYPE_CONTROL_LISTENER:
+    case CONN_TYPE_CONTROL_UDS_LISTENER:
     case CONN_TYPE_AP_DNS_LISTENER:
       tor_assert(conn->state == LISTENER_STATE_READY);
       break;
-- 
                           |  .''`.  ** Debian GNU/Linux **
      Peter Palfrader      | : :' :      The  universal
 http://www.palfrader.org/ | `. `'      Operating System
                           |   `-    http://www.debian.org/
Index: configure.in
===================================================================
--- configure.in	(revision 10485)
+++ configure.in	(working copy)
@@ -240,7 +240,7 @@
 
 AC_CHECK_HEADERS(unistd.h string.h signal.h ctype.h sys/stat.h sys/types.h fcntl.h sys/fcntl.h sys/time.h errno.h assert.h time.h, , AC_MSG_WARN(Some headers were not found, compilation may fail.  If compilation succeeds, please send your orconfig.h to the developers so we can fix this warning.))
 
-AC_CHECK_HEADERS(netdb.h sys/ioctl.h sys/socket.h arpa/inet.h netinet/in.h pwd.h grp.h)
+AC_CHECK_HEADERS(netdb.h sys/ioctl.h sys/socket.h arpa/inet.h netinet/in.h pwd.h grp.h sys/un.h)
 
 dnl These headers are not essential
 
Index: src/or/config.c
===================================================================
--- src/or/config.c	(revision 10487)
+++ src/or/config.c	(working copy)
@@ -149,6 +149,9 @@
   VAR("ContactInfo",         STRING,   ContactInfo,          NULL),
   VAR("ControlListenAddress",LINELIST, ControlListenAddress, NULL),
   VAR("ControlPort",         UINT,     ControlPort,          "0"),
+#ifdef HAVE_SYS_UN_H
+  VAR("ControlSocket",       LINELIST, ControlSocket,        NULL),
+#endif
   VAR("CookieAuthentication",BOOL,     CookieAuthentication, "0"),
   VAR("DataDirectory",       STRING,   DataDirectory,        NULL),
   OBSOLETE("DebugLogFile"),
Index: src/or/or.h
===================================================================
--- src/or/or.h	(revision 10487)
+++ src/or/or.h	(working copy)
@@ -56,6 +56,9 @@
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
@@ -239,7 +242,9 @@
 #define CONN_TYPE_AP_NATD_LISTENER 14
 /** Type for sockets listening for DNS requests. */
 #define CONN_TYPE_AP_DNS_LISTENER 15
-#define _CONN_TYPE_MAX 15
+/** Type for unix domain sockets for control interface */
+#define CONN_TYPE_CONTROL_UDS_LISTENER 16
+#define _CONN_TYPE_MAX 16
 
 #define CONN_IS_EDGE(x) \
   ((x)->type == CONN_TYPE_EXIT || (x)->type == CONN_TYPE_AP)
@@ -1803,6 +1808,7 @@
   int TransPort;
   int NatdPort; /**< Port to listen on for transparent natd connections. */
   int ControlPort; /**< Port to listen on for control connections. */
+  config_line_t * ControlSocket; /**< Unix Domain Socket to listen on for control connections. */
   int DirPort; /**< Port to listen on for directory connections. */
   int DNSPort; /**< Port to listen on for DNS requests. */
   int AssumeReachable; /**< Whether to publish our descriptor regardless. */
Index: src/or/connection.c
===================================================================
--- src/or/connection.c	(revision 10487)
+++ src/or/connection.c	(working copy)
@@ -54,6 +54,7 @@
     case CONN_TYPE_DIR: return "Directory";
     case CONN_TYPE_CPUWORKER: return "CPU worker";
     case CONN_TYPE_CONTROL_LISTENER: return "Control listener";
+    case CONN_TYPE_CONTROL_UDS_LISTENER: return "Control unix-socket listener";
     case CONN_TYPE_CONTROL: return "Control";
     default:
       log_warn(LD_BUG, "unknown connection type %d", type);
@@ -78,6 +79,7 @@
     case CONN_TYPE_AP_DNS_LISTENER:
     case CONN_TYPE_DIR_LISTENER:
     case CONN_TYPE_CONTROL_LISTENER:
+    case CONN_TYPE_CONTROL_UDS_LISTENER:
       if (state == LISTENER_STATE_READY)
         return "ready";
       break;
@@ -295,6 +297,13 @@
     buf_free(conn->outbuf);
   }
 
+  if (conn->type == CONN_TYPE_CONTROL_UDS_LISTENER) {
+    if (unlink(conn->address) < 0 && errno != ENOENT) {
+      log_warn(LD_NET, "Could not unlink %s: %s", conn->address,
+                       strerror(errno));
+    }
+  }
+
   tor_free(conn->address);
 
   if (connection_speaks_cells(conn)) {
@@ -343,6 +352,7 @@
     connection_or_remove_from_identity_map(TO_OR_CONN(conn));
   }
 
+
   memset(conn, 0xAA, sizeof(connection_t)); /* poison memory */
   tor_free(mem);
 }
@@ -632,33 +642,13 @@
 connection_create_listener(const char *listenaddress, uint16_t listenport,
                            int type)
 {
-  struct sockaddr_in listenaddr; /* where to bind */
   char *address = NULL;
   connection_t *conn;
   uint16_t usePort;
-  uint32_t addr;
   int s; /* the socket we're going to make */
-#ifndef MS_WINDOWS
-  int one=1;
-#endif
-  int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
+  int is_inet = (type != CONN_TYPE_CONTROL_UDS_LISTENER);
+  int is_tcp = (is_inet && type != CONN_TYPE_AP_DNS_LISTENER);
 
-  memset(&listenaddr,0,sizeof(struct sockaddr_in));
-  if (parse_addr_port(LOG_WARN, listenaddress, &address, &addr, &usePort)<0) {
-    log_warn(LD_CONFIG,
-             "Error parsing/resolving ListenAddress %s", listenaddress);
-    return NULL;
-  }
-
-  if (usePort==0)
-    usePort = listenport;
-  listenaddr.sin_addr.s_addr = htonl(addr);
-  listenaddr.sin_family = AF_INET;
-  listenaddr.sin_port = htons((uint16_t) usePort);
-
-  log_notice(LD_NET, "Opening %s on %s:%d",
-             conn_type_to_string(type), address, usePort);
-
   if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
     int n_conns = get_n_open_sockets();
     log_warn(LD_NET,"Failing because we have %d connections already. Please "
@@ -668,40 +658,104 @@
     return NULL;
   }
 
-  s = tor_open_socket(PF_INET,
-                      is_tcp ? SOCK_STREAM : SOCK_DGRAM,
-                      is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
-  if (s < 0) {
-    log_warn(LD_NET,"Socket creation failed.");
-    goto err;
-  }
 
+
+  if (is_inet) {
+    struct sockaddr_in listenaddr; /* where to bind */
+    uint32_t addr;
 #ifndef MS_WINDOWS
-  /* REUSEADDR on normal places means you can rebind to the port
-   * right after somebody else has let it go. But REUSEADDR on win32
-   * means you can bind to the port _even when somebody else
-   * already has it bound_. So, don't do that on Win32. */
-  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one));
+    int one=1;
 #endif
 
-  if (bind(s,(struct sockaddr *)&listenaddr,sizeof(listenaddr)) < 0) {
-    const char *helpfulhint = "";
-    int e = tor_socket_errno(s);
-    if (ERRNO_IS_EADDRINUSE(e))
-      helpfulhint = ". Is Tor already running?";
-    log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort,
-             tor_socket_strerror(e), helpfulhint);
-    tor_close_socket(s);
-    goto err;
-  }
+    memset(&listenaddr,0,sizeof(struct sockaddr_in));
+    if (parse_addr_port(LOG_WARN, listenaddress, &address, &addr, &usePort)<0) {
+      log_warn(LD_CONFIG,
+	       "Error parsing/resolving ListenAddress %s", listenaddress);
+      return NULL;
+    }
 
-  if (is_tcp) {
+    if (usePort==0)
+      usePort = listenport;
+    listenaddr.sin_addr.s_addr = htonl(addr);
+    listenaddr.sin_family = AF_INET;
+    listenaddr.sin_port = htons((uint16_t) usePort);
+
+    log_notice(LD_NET, "Opening %s on %s:%d",
+	       conn_type_to_string(type), address, usePort);
+
+    s = tor_open_socket(PF_INET,
+			is_tcp ? SOCK_STREAM : SOCK_DGRAM,
+			is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
+    if (s < 0) {
+      log_warn(LD_NET,"Socket creation failed.");
+      goto err;
+    }
+
+#ifndef MS_WINDOWS
+    /* REUSEADDR on normal places means you can rebind to the port
+     * right after somebody else has let it go. But REUSEADDR on win32
+     * means you can bind to the port _even when somebody else
+     * already has it bound_. So, don't do that on Win32. */
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one));
+#endif
+
+    if (bind(s,(struct sockaddr *)&listenaddr,sizeof(listenaddr)) < 0) {
+      const char *helpfulhint = "";
+      int e = tor_socket_errno(s);
+      if (ERRNO_IS_EADDRINUSE(e))
+	helpfulhint = ". Is Tor already running?";
+      log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort,
+	       tor_socket_strerror(e), helpfulhint);
+      tor_close_socket(s);
+      goto err;
+    }
+
+    if (is_tcp) {
+      if (listen(s,SOMAXCONN) < 0) {
+	log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort,
+		 tor_socket_strerror(tor_socket_errno(s)));
+	tor_close_socket(s);
+	goto err;
+      }
+    }
+  } else {
+#ifdef HAVE_SYS_UN_H
+    struct sockaddr_un sockaddr;
+    sockaddr.sun_family = AF_UNIX;
+    int len;
+    address = tor_strdup(listenaddress);
+
+    log_notice(LD_NET, "Opening %s on %s",
+	       conn_type_to_string(type), address);
+
+    strncpy(sockaddr.sun_path, address, sizeof(sockaddr.sun_path));
+    if (unlink(sockaddr.sun_path) < 0 && errno != ENOENT) {
+      log_warn(LD_NET, "Could not unlink %s: %s", address,
+                       strerror(errno));
+      goto err;
+    }
+    s = tor_open_socket(AF_UNIX, SOCK_STREAM, 0);
+    if (s < 0) {
+      log_warn(LD_NET,"Socket creation failed: %s.", strerror(errno));
+      goto err;
+    }
+
+    len = strlen(sockaddr.sun_path) + sizeof(sockaddr.sun_family);
+    if (bind(s, (struct sockaddr *)&sockaddr, len) == -1) {
+      log_warn(LD_NET,"Bind to %s failed: %s.", address,
+               tor_socket_strerror(tor_socket_errno(s)));
+      goto err;
+    }
+
     if (listen(s,SOMAXCONN) < 0) {
-      log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort,
-               tor_socket_strerror(tor_socket_errno(s)));
+      log_warn(LD_NET, "Could not listen on %s: %s", address,
+	       tor_socket_strerror(tor_socket_errno(s)));
       tor_close_socket(s);
       goto err;
     }
+#else
+    assert(0);
+#endif /* HAVE_SYS_UN_H */
   }
 
   set_socket_nonblocking(s);
@@ -722,7 +776,7 @@
             conn_type_to_string(type), usePort);
 
   conn->state = LISTENER_STATE_READY;
-  if (is_tcp) {
+  if (is_tcp || !is_inet) {
     connection_start_reading(conn);
   } else {
     tor_assert(type == CONN_TYPE_AP_DNS_LISTENER);
@@ -801,56 +855,73 @@
 
   set_socket_nonblocking(news);
 
-  if (check_sockaddr_in((struct sockaddr*)addrbuf, remotelen, LOG_INFO)<0) {
-    log_info(LD_NET,
-             "accept() returned a strange address; trying getsockname().");
-    remotelen=256;
-    memset(addrbuf, 0, sizeof(addrbuf));
-    if (getsockname(news, (struct sockaddr*)addrbuf, &remotelen)<0) {
-      int e = tor_socket_errno(news);
-      log_warn(LD_NET, "getsockname() for new connection failed: %s",
-               tor_socket_strerror(e));
-    } else {
-      if (check_sockaddr_in((struct sockaddr*)addrbuf, remotelen,
-                            LOG_WARN) < 0) {
-        log_warn(LD_NET,"Something's wrong with this conn. Closing it.");
-        tor_close_socket(news);
-        return 0;
+  if (((struct sockaddr*)addrbuf)->sa_family == AF_INET) {
+    assert(conn->type != CONN_TYPE_CONTROL_UDS_LISTENER);
+
+    if (check_sockaddr_in((struct sockaddr*)addrbuf, remotelen, LOG_INFO)<0) {
+      log_info(LD_NET,
+	       "accept() returned a strange address; trying getsockname().");
+      remotelen=256;
+      memset(addrbuf, 0, sizeof(addrbuf));
+      if (getsockname(news, (struct sockaddr*)addrbuf, &remotelen)<0) {
+	int e = tor_socket_errno(news);
+	log_warn(LD_NET, "getsockname() for new connection failed: %s",
+		 tor_socket_strerror(e));
+      } else {
+	if (check_sockaddr_in((struct sockaddr*)addrbuf, remotelen,
+			      LOG_WARN) < 0) {
+	  log_warn(LD_NET,"Something's wrong with this conn. Closing it.");
+	  tor_close_socket(news);
+	  return 0;
+	}
       }
     }
-  }
-  memcpy(&remote, addrbuf, sizeof(struct sockaddr_in));
+    memcpy(&remote, addrbuf, sizeof(struct sockaddr_in));
 
-  /* process entrance policies here, before we even create the connection */
-  if (new_type == CONN_TYPE_AP) {
-    /* check sockspolicy to see if we should accept it */
-    if (socks_policy_permits_address(ntohl(remote.sin_addr.s_addr)) == 0) {
-      tor_inet_ntoa(&remote.sin_addr, tmpbuf, sizeof(tmpbuf));
-      log_notice(LD_APP,"Denying socks connection from untrusted address %s.",
-                 tmpbuf);
-      tor_close_socket(news);
-      return 0;
+    /* process entrance policies here, before we even create the connection */
+    if (new_type == CONN_TYPE_AP) {
+      /* check sockspolicy to see if we should accept it */
+      if (socks_policy_permits_address(ntohl(remote.sin_addr.s_addr)) == 0) {
+	tor_inet_ntoa(&remote.sin_addr, tmpbuf, sizeof(tmpbuf));
+	log_notice(LD_APP,"Denying socks connection from untrusted address %s.",
+		   tmpbuf);
+	tor_close_socket(news);
+	return 0;
+      }
     }
-  }
-  if (new_type == CONN_TYPE_DIR) {
-    /* check dirpolicy to see if we should accept it */
-    if (dir_policy_permits_address(ntohl(remote.sin_addr.s_addr)) == 0) {
-      tor_inet_ntoa(&remote.sin_addr, tmpbuf, sizeof(tmpbuf));
-      log_notice(LD_DIRSERV,"Denying dir connection from address %s.",
-                 tmpbuf);
-      tor_close_socket(news);
-      return 0;
+    if (new_type == CONN_TYPE_DIR) {
+      /* check dirpolicy to see if we should accept it */
+      if (dir_policy_permits_address(ntohl(remote.sin_addr.s_addr)) == 0) {
+	tor_inet_ntoa(&remote.sin_addr, tmpbuf, sizeof(tmpbuf));
+	log_notice(LD_DIRSERV,"Denying dir connection from address %s.",
+		   tmpbuf);
+	tor_close_socket(news);
+	return 0;
+      }
     }
-  }
 
-  newconn = connection_new(new_type);
-  newconn->s = news;
+    newconn = connection_new(new_type);
+    newconn->s = news;
 
-  /* remember the remote address */
-  newconn->addr = ntohl(remote.sin_addr.s_addr);
-  newconn->port = ntohs(remote.sin_port);
-  newconn->address = tor_dup_addr(newconn->addr);
+    /* remember the remote address */
+    newconn->addr = ntohl(remote.sin_addr.s_addr);
+    newconn->port = ntohs(remote.sin_port);
+    newconn->address = tor_dup_addr(newconn->addr);
 
+  } else if (((struct sockaddr*)addrbuf)->sa_family == AF_UNIX) {
+    assert(conn->type == CONN_TYPE_CONTROL_UDS_LISTENER);
+
+    newconn = connection_new(new_type);
+    newconn->s = news;
+
+    /* remember the remote address  --  do we have anything sane to put here? */
+    newconn->addr = 0;
+    newconn->port = 1;
+    newconn->address = tor_strdup(conn->address);
+  } else {
+    assert(0);
+  };
+
   if (connection_add(newconn) < 0) { /* no space, forget it */
     connection_free(newconn);
     return 0; /* no need to tear down the parent */
@@ -1042,18 +1113,25 @@
     line = NULL;
     SMARTLIST_FOREACH(launch, config_line_t *, wanted,
       {
-        char *address=NULL;
-        uint16_t port;
-        if (!parse_addr_port(LOG_WARN, wanted->value, &address, NULL, &port)) {
-          int addr_matches = !strcasecmp(address, conn->address);
-          tor_free(address);
-          if (! port)
-            port = port_option;
-          if (port == conn->port && addr_matches) {
-            line = wanted;
-            break;
-          }
-        }
+	if (type == CONN_TYPE_CONTROL_UDS_LISTENER) {
+	  if (!strcasecmp(wanted->value, conn->address)) {
+	    line = wanted;
+	    break;
+	  }
+	} else {
+	  char *address=NULL;
+	  uint16_t port;
+	  if (!parse_addr_port(LOG_WARN, wanted->value, &address, NULL, &port)) {
+	    int addr_matches = !strcasecmp(address, conn->address);
+	    tor_free(address);
+	    if (! port)
+	      port = port_option;
+	    if (port == conn->port && addr_matches) {
+	      line = wanted;
+	      break;
+	    }
+	  }
+	}
       });
     if (! line) {
       /* This one isn't configured. Close it. */
@@ -1142,6 +1220,11 @@
                       options->ControlPort, "127.0.0.1",
                       replaced_conns, new_conns, 0)<0)
     return -1;
+  if (retry_listeners(CONN_TYPE_CONTROL_UDS_LISTENER,
+                      options->ControlSocket,
+                      options->ControlSocket ? 1 : 0, NULL,
+                      replaced_conns, new_conns, 0)<0)
+    return -1;
 
   return 0;
 }
@@ -1546,6 +1629,7 @@
     case CONN_TYPE_DIR_LISTENER:
       return connection_handle_listener_read(conn, CONN_TYPE_DIR);
     case CONN_TYPE_CONTROL_LISTENER:
+    case CONN_TYPE_CONTROL_UDS_LISTENER:
       return connection_handle_listener_read(conn, CONN_TYPE_CONTROL);
     case CONN_TYPE_AP_DNS_LISTENER:
       /* This should never happen; eventdns.c handles the reads here. */
@@ -2631,6 +2715,7 @@
     case CONN_TYPE_AP_NATD_LISTENER:
     case CONN_TYPE_DIR_LISTENER:
     case CONN_TYPE_CONTROL_LISTENER:
+    case CONN_TYPE_CONTROL_UDS_LISTENER:
     case CONN_TYPE_AP_DNS_LISTENER:
       tor_assert(conn->state == LISTENER_STATE_READY);
       break;