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

Re: control interface via unix domain socket



On Mon, 04 Jun 2007, Nick Mathewson wrote:

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

>   - Right now, your code rejects the ControlSocket option as
>     unrecognized.  if the code can't support it.  That's not how we
>     handle unsupported options right now; instead, we try to give a
>     message that says, "this Tor can't support that (otherwise valid)
>     option."  (In some cases, we just ignore the option.  That's bad
>     too.)

Fixed.

>   - Make sure your code passes "make check-spaces".

It should now.

>   - I'm not sure we need to have a new listener type for this.
>     I think, instead, that connection_create_listener should
>     take a "struct sockaddr" as its argument, and that its callers
>     should pass it a sockaddr of the right kind.  This will avoid
>     bloating connection_create_listener, and will make the eventual
>     IPv6 listeners easier too.
> 
>     To handle the special-cases on AF_UNIX later on, we could add a
>     one-bit is_unix_socket flag to connection_t.

I added a new field to connection_t, sa_family.  Right now it will
always be AF_INET or AF_UNIX, but it may be AF_INET6 in the future.
Also, the additional connection type is gone.  relaunch_listener()
now takes an extra argument specifying if you want unix or inet sockets.

>   - I think you missed the code to make connection_is_listener() work,
>     in case you don't go with the above.

N/A anymore :)

> With that in mind, this looks pretty close to complete.  Would you
> like to do the cleanup, or should I?

How does this look?
-- 
                           |  .''`.  ** Debian GNU/Linux **
      Peter Palfrader      | : :' :      The  universal
 http://www.palfrader.org/ | `. `'      Operating System
                           |   `-    http://www.debian.org/
Index: configure.in
===================================================================
--- configure.in	(revision 10502)
+++ 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/connection_or.c
===================================================================
--- src/or/connection_or.c	(revision 10502)
+++ src/or/connection_or.c	(working copy)
@@ -465,7 +465,7 @@
     return NULL;
   }
 
-  conn = TO_OR_CONN(connection_new(CONN_TYPE_OR));
+  conn = TO_OR_CONN(connection_new(CONN_TYPE_OR, AF_INET));
 
   /* set up conn so it's got all the data we need to remember */
   connection_or_init_conn_from_address(conn, addr, port, id_digest, 1);
Index: src/or/config.c
===================================================================
--- src/or/config.c	(revision 10502)
+++ src/or/config.c	(working copy)
@@ -149,6 +149,7 @@
   VAR("ContactInfo",         STRING,   ContactInfo,          NULL),
   VAR("ControlListenAddress",LINELIST, ControlListenAddress, NULL),
   VAR("ControlPort",         UINT,     ControlPort,          "0"),
+  VAR("ControlSocket",       LINELIST, ControlSocket,        NULL),
   VAR("CookieAuthentication",BOOL,     CookieAuthentication, "0"),
   VAR("DataDirectory",       STRING,   DataDirectory,        NULL),
   OBSOLETE("DebugLogFile"),
@@ -811,6 +812,14 @@
     start_daemon();
   }
 
+#ifndef HAVE_SYS_UN_H
+  if (options->ControlSocket) {
+    *msg = tor_strdup("Unix domain sockets (ControlSocket) not supported"
+                      " on this OS/with this build.");
+    goto rollback;
+  }
+#endif
+
   if (running_tor) {
     /* We need to set the connection limit before we can open the listeners. */
     options->_ConnLimit =
Index: src/or/connection_edge.c
===================================================================
--- src/or/connection_edge.c	(revision 10502)
+++ src/or/connection_edge.c	(working copy)
@@ -1916,7 +1916,7 @@
   log_notice(LD_APP,"Making internal anonymized tunnel to %s:%d ...",
              safe_str(address),port); /* XXXX020 Downgrade back to info. */
 
-  conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP));
+  conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP, AF_INET));
   conn->_base.linked = 1; /* so that we can add it safely below. */
 
   /* populate conn->socks_request */
@@ -2210,7 +2210,7 @@
   }
 
   log_debug(LD_EXIT,"Creating new exit connection.");
-  n_stream = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
+  n_stream = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT, AF_INET));
   n_stream->_base.purpose = EXIT_PURPOSE_CONNECT;
 
   n_stream->stream_id = rh.stream_id;
@@ -2316,7 +2316,7 @@
    * resolved; but if we didn't store them in a connection like this,
    * the housekeeping in dns.c would get way more complicated.)
    */
-  dummy_conn = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
+  dummy_conn = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT, AF_INET));
   dummy_conn->stream_id = rh.stream_id;
   dummy_conn->_base.address = tor_strndup(cell->payload+RELAY_HEADER_SIZE,
                                           rh.length);
@@ -2450,7 +2450,7 @@
 
   exitconn->_base.state = EXIT_CONN_STATE_OPEN;
 
-  dirconn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR));
+  dirconn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
 
   dirconn->_base.addr = 0x7f000001;
   dirconn->_base.port = 0;
Index: src/or/or.h
===================================================================
--- src/or/or.h	(revision 10502)
+++ 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
@@ -790,6 +793,9 @@
                                  * could write? */
   time_t timestamp_created; /**< When was this connection_t created? */
 
+  int sa_family; /**< Address family of this connection's socket.  Usually
+                  * AF_INET, but it can also be AF_UNIX, or in the future
+                  * AF_INET6 */
   uint32_t addr; /**< IP of the other side of the connection; used to identify
                   * routers, along with port. */
   uint16_t port; /**< If non-zero, port  on the other end
@@ -1819,6 +1825,8 @@
   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. */
@@ -2341,7 +2349,7 @@
 const char *conn_type_to_string(int type);
 const char *conn_state_to_string(int type, int state);
 
-connection_t *connection_new(int type);
+connection_t *connection_new(int type, int sa_family);
 void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
 void connection_unregister_events(connection_t *conn);
 void connection_free(connection_t *conn);
Index: src/or/connection.c
===================================================================
--- src/or/connection.c	(revision 10502)
+++ src/or/connection.c	(working copy)
@@ -14,8 +14,9 @@
 
 #include "or.h"
 
-static connection_t *connection_create_listener(const char *listenaddress,
-                                                uint16_t listenport, int type);
+static connection_t *connection_create_listener(
+                               struct sockaddr *listensockaddr, int type,
+                               char* address);
 static int connection_init_accepted_conn(connection_t *conn,
                                          uint8_t listener_type);
 static int connection_handle_listener_read(connection_t *conn, int new_type);
@@ -156,7 +157,7 @@
  * Initialize conn's timestamps to now.
  */
 connection_t *
-connection_new(int type)
+connection_new(int type, int sa_family)
 {
   static uint32_t n_connections_allocated = 1;
   connection_t *conn;
@@ -194,6 +195,7 @@
   conn->conn_array_index = -1; /* also default to 'not used' */
 
   conn->type = type;
+  conn->sa_family = sa_family;
   if (!connection_is_listener(conn)) { /* listeners never use their buf */
     conn->inbuf = buf_new();
     conn->outbuf = buf_new();
@@ -293,7 +295,18 @@
   if (!connection_is_listener(conn)) {
     buf_free(conn->inbuf);
     buf_free(conn->outbuf);
+  } else {
+    if (conn->sa_family == AF_UNIX) {
+      /* For now only control ports can be unix domain sockets
+       * and listeners at the same time */
+      tor_assert(conn->type == CONN_TYPE_CONTROL_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);
 
@@ -621,44 +634,97 @@
   });
 }
 
-/** Bind a new non-blocking socket listening to
- * <b>listenaddress</b>:<b>listenport</b>, and add this new connection
- * (of type <b>type</b>) to the connection array.
+/** Create an AF_INET listenaddr struct.
+ * <b>listenaddress</b> provides the host and optionally the port information
+ * for the new structure.  If no port is provided in <b>listenaddress</b> then
+ * <b>listenport</b> is used.
  *
- * If <b>listenaddress</b> includes a port, we bind on that port;
- * otherwise, we use listenport.
+ * If not NULL <b>readable_addrress</b> will contain a copy of the host part of
+ * <b>listenaddress</b>.
+ *
+ * The listenaddr struct has to be freed by the caller.
  */
-static connection_t *
-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;
+struct sockaddr_in *
+create_inet_sockaddr(const char *listenaddress, uint16_t listenport,
+                     char **readable_address) {
+  struct sockaddr_in *listenaddr = NULL;
   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);
+  uint16_t usePort = 0;
 
-  memset(&listenaddr,0,sizeof(struct sockaddr_in));
-  if (parse_addr_port(LOG_WARN, listenaddress, &address, &addr, &usePort)<0) {
+  if (parse_addr_port(LOG_WARN,
+                      listenaddress, readable_address, &addr, &usePort)<0) {
     log_warn(LD_CONFIG,
              "Error parsing/resolving ListenAddress %s", listenaddress);
-    return NULL;
+    goto err;
   }
-
   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);
+  listenaddr = tor_malloc_zero(sizeof(struct sockaddr_in));
+  listenaddr->sin_addr.s_addr = htonl(addr);
+  listenaddr->sin_family = AF_INET;
+  listenaddr->sin_port = htons((uint16_t) usePort);
 
+  return listenaddr;
+
+ err:
+  tor_free(listenaddr);
+  return NULL;
+}
+
+#ifdef HAVE_SYS_UN_H
+/** Create an AF_UNIX listenaddr struct.
+ * <b>listenaddress</b> provides the path to the unix socket.
+ *
+ * Eventually <b>listenaddress</b> will also optionally contain user, group,
+ * and file permissions for the new socket.  But not yet. XXX
+ * Also, since we do not create the socket here the information doesn't help
+ * here.
+ *
+ * If not NULL <b>readable_addrress</b> will contain a copy of the path part of
+ * <b>listenaddress</b>.
+ *
+ * The listenaddr struct has to be freed by the caller.
+ */
+struct sockaddr_un *
+create_unix_sockaddr(const char *listenaddress, char **readable_address)
+{
+  struct sockaddr_un *sockaddr = NULL;
+
+  sockaddr = tor_malloc_zero(sizeof(struct sockaddr_un));
+  sockaddr->sun_family = AF_UNIX;
+  strncpy(sockaddr->sun_path, listenaddress, sizeof(sockaddr->sun_path));
+
+  if (readable_address)
+    *readable_address = tor_strdup(listenaddress);
+
+  return sockaddr;
+}
+#else
+struct sockaddr *
+create_unix_sockaddr(const char *listenaddress, char **readable_address)
+{
+  log_fn(LOG_ERR, LD_BUG,
+         "Unix domain sockets not supported, yet we tried to create one.");
+  assert(0);
+};
+#endif /* HAVE_SYS_UN_H */
+
+/** Bind a new non-blocking socket listening to the socket described
+ * by <b>listensockaddr</b>.
+ *
+ * <b>address</b> is only used for logging purposes and to add the information
+ * to the conn.
+ */
+static connection_t *
+connection_create_listener(struct sockaddr *listensockaddr, int type,
+  char* address)
+{
+  connection_t *conn;
+  int s; /* the socket we're going to make */
+  uint16_t usePort = 0;
+  int start_reading = 0;
+
   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,6 +734,20 @@
     return NULL;
   }
 
+  if (listensockaddr->sa_family == AF_INET) {
+    int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
+#ifndef MS_WINDOWS
+    int one=1;
+#endif
+    if (is_tcp)
+      start_reading = 1;
+
+    usePort = ntohs( (uint16_t)
+                     ((struct sockaddr_in *)listensockaddr)->sin_port);
+
+    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);
@@ -684,7 +764,7 @@
   setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one));
 #endif
 
-  if (bind(s,(struct sockaddr *)&listenaddr,sizeof(listenaddr)) < 0) {
+    if (bind(s,listensockaddr,sizeof(struct sockaddr_in)) < 0) {
     const char *helpfulhint = "";
     int e = tor_socket_errno(s);
     if (ERRNO_IS_EADDRINUSE(e))
@@ -703,13 +783,56 @@
       goto err;
     }
   }
+#ifdef HAVE_SYS_UN_H
+  } else if (listensockaddr->sa_family == AF_UNIX) {
+    int len;
+    start_reading = 1;
 
+    /* For now only control ports can be unix domain sockets
+     * and listeners at the same time */
+    tor_assert(type == CONN_TYPE_CONTROL_LISTENER);
+
+    log_notice(LD_NET, "Opening %s on %s",
+               conn_type_to_string(type), address);
+
+    if (unlink(address) < 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(((struct sockaddr_un *)listensockaddr)->sun_path) +
+          sizeof(((struct sockaddr_un *)listensockaddr)->sun_family);
+    if (bind(s, listensockaddr, 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;
+    }
+#endif /* HAVE_SYS_UN_H */
+  } else {
+      log_err(LD_BUG,"Got unexpected address family %d.",
+              listensockaddr->sa_family);
+      tor_assert(0);
+  }
+
   set_socket_nonblocking(s);
 
-  conn = connection_new(type);
+  conn = connection_new(type, listensockaddr->sa_family);
+  conn->sa_family = listensockaddr->sa_family;
   conn->s = s;
-  conn->address = address;
-  address = NULL;
+  conn->address = tor_strdup(address);
   conn->port = usePort;
 
   if (connection_add(conn) < 0) { /* no space, forget it */
@@ -722,7 +845,7 @@
             conn_type_to_string(type), usePort);
 
   conn->state = LISTENER_STATE_READY;
-  if (is_tcp) {
+  if (start_reading) {
     connection_start_reading(conn);
   } else {
     tor_assert(type == CONN_TYPE_AP_DNS_LISTENER);
@@ -732,7 +855,6 @@
   return conn;
 
  err:
-  tor_free(address);
   return NULL;
 }
 
@@ -801,6 +923,9 @@
 
   set_socket_nonblocking(news);
 
+  tor_assert(((struct sockaddr*)addrbuf)->sa_family == conn->sa_family);
+
+  if (conn->sa_family == AF_INET) {
   if (check_sockaddr_in((struct sockaddr*)addrbuf, remotelen, LOG_INFO)<0) {
     log_info(LD_NET,
              "accept() returned a strange address; trying getsockname().");
@@ -826,7 +951,8 @@
     /* 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.",
+        log_notice(LD_APP,
+                   "Denying socks connection from untrusted address %s.",
                  tmpbuf);
       tor_close_socket(news);
       return 0;
@@ -843,7 +969,7 @@
     }
   }
 
-  newconn = connection_new(new_type);
+    newconn = connection_new(new_type, conn->sa_family);
   newconn->s = news;
 
   /* remember the remote address */
@@ -851,6 +977,22 @@
   newconn->port = ntohs(remote.sin_port);
   newconn->address = tor_dup_addr(newconn->addr);
 
+  } else if (conn->sa_family == AF_UNIX) {
+    /* For now only control ports can be unix domain sockets
+     * and listeners at the same time */
+    tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER);
+
+    newconn = connection_new(new_type, conn->sa_family);
+    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 {
+    tor_assert(0);
+  };
+
   if (connection_add(newconn) < 0) { /* no space, forget it */
     connection_free(newconn);
     return 0; /* no need to tear down the parent */
@@ -1007,7 +1149,8 @@
                 int port_option, const char *default_addr,
                 smartlist_t *replaced_conns,
                 smartlist_t *new_conns,
-                int never_open_conns)
+                int never_open_conns,
+                int sa_family)
 {
   smartlist_t *launch = smartlist_create(), *conns;
   int free_launch_elts = 1;
@@ -1016,6 +1159,8 @@
   connection_t *conn;
   config_line_t *line;
 
+  tor_assert(sa_family == AF_INET || sa_family == AF_UNIX);
+
   if (cfg && port_option) {
     for (c = cfg; c; c = c->next) {
       smartlist_add(launch, c);
@@ -1036,7 +1181,9 @@
   conns = get_connection_array();
   SMARTLIST_FOREACH(conns, connection_t *, conn,
   {
-    if (conn->type != type || conn->marked_for_close)
+    if (conn->type != type ||
+        conn->sa_family != sa_family ||
+        conn->marked_for_close)
       continue;
     /* Okay, so this is a listener.  Is it configured? */
     line = NULL;
@@ -1044,7 +1191,10 @@
       {
         char *address=NULL;
         uint16_t port;
-        if (!parse_addr_port(LOG_WARN, wanted->value, &address, NULL, &port)) {
+        switch (sa_family) {
+          case AF_INET:
+            if (!parse_addr_port(LOG_WARN,
+                                 wanted->value, &address, NULL, &port)) {
           int addr_matches = !strcasecmp(address, conn->address);
           tor_free(address);
           if (! port)
@@ -1054,6 +1204,16 @@
             break;
           }
         }
+            break;
+          case AF_UNIX:
+            if (!strcasecmp(wanted->value, conn->address)) {
+              line = wanted;
+              break;
+            }
+            break;
+          default:
+            tor_assert(0);
+        }
       });
     if (! line) {
       /* This one isn't configured. Close it. */
@@ -1080,8 +1240,31 @@
   if (!never_open_conns) {
     SMARTLIST_FOREACH(launch, config_line_t *, cfg_line,
       {
-        conn = connection_create_listener(cfg_line->value,
-                                          (uint16_t) port_option, type);
+        char *address = NULL;
+        struct sockaddr *listensockaddr;
+
+        switch (sa_family) {
+          case AF_INET:
+            listensockaddr = (struct sockaddr *)
+                             create_inet_sockaddr(cfg_line->value,
+                                                  (uint16_t) port_option,
+                                                  &address);
+            break;
+          case AF_UNIX:
+            listensockaddr = (struct sockaddr *)
+                             create_unix_sockaddr(cfg_line->value,
+                                                  &address);
+            break;
+          default:
+            tor_assert(0);
+        }
+
+        if (listensockaddr) {
+          conn = connection_create_listener(listensockaddr, type, address);
+          tor_free(address);
+        } else
+          conn = NULL;
+
         if (!conn) {
           r = -1;
         } else {
@@ -1115,33 +1298,46 @@
 
   if (retry_listeners(CONN_TYPE_OR_LISTENER, options->ORListenAddress,
                       options->ORPort, "0.0.0.0",
-                      replaced_conns, new_conns, options->ClientOnly)<0)
+                      replaced_conns, new_conns, options->ClientOnly,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
                       options->DirPort, "0.0.0.0",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_AP_LISTENER, options->SocksListenAddress,
                       options->SocksPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_AP_TRANS_LISTENER, options->TransListenAddress,
                       options->TransPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_AP_NATD_LISTENER, options->NatdListenAddress,
                       options->NatdPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_AP_DNS_LISTENER, options->DNSListenAddress,
                       options->DNSPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
                       options->ControlListenAddress,
                       options->ControlPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
+  if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
+                      options->ControlSocket,
+                      options->ControlSocket ? 1 : 0, NULL,
+                      replaced_conns, new_conns, 0,
+                      AF_UNIX)<0)
+    return -1;
 
   return 0;
 }
Index: src/or/directory.c
===================================================================
--- src/or/directory.c	(revision 10502)
+++ src/or/directory.c	(working copy)
@@ -490,7 +490,7 @@
       tor_assert(0);
   }
 
-  conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR));
+  conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
 
   /* set up conn so it's got all the data we need to remember */
   conn->_base.addr = addr;
Index: src/or/cpuworker.c
===================================================================
--- src/or/cpuworker.c	(revision 10502)
+++ src/or/cpuworker.c	(working copy)
@@ -339,7 +339,7 @@
   tor_free(fdarray);
 #endif
 
-  conn = connection_new(CONN_TYPE_CPUWORKER);
+  conn = connection_new(CONN_TYPE_CPUWORKER, AF_UNIX);
 
   set_socket_nonblocking(fd);
 
Index: src/or/dnsserv.c
===================================================================
--- src/or/dnsserv.c	(revision 10502)
+++ src/or/dnsserv.c	(working copy)
@@ -110,7 +110,7 @@
   /* XXXX020 Send a stream event to the controller. */
 
   /* Make a new dummy AP connection, and attach the request to it. */
-  conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP));
+  conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP, AF_INET));
   conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
   if (q->type == EVDNS_TYPE_A)
     conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
Index: configure.in
===================================================================
--- configure.in	(revision 10502)
+++ 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/connection_or.c
===================================================================
--- src/or/connection_or.c	(revision 10502)
+++ src/or/connection_or.c	(working copy)
@@ -465,7 +465,7 @@
     return NULL;
   }
 
-  conn = TO_OR_CONN(connection_new(CONN_TYPE_OR));
+  conn = TO_OR_CONN(connection_new(CONN_TYPE_OR, AF_INET));
 
   /* set up conn so it's got all the data we need to remember */
   connection_or_init_conn_from_address(conn, addr, port, id_digest, 1);
Index: src/or/config.c
===================================================================
--- src/or/config.c	(revision 10502)
+++ src/or/config.c	(working copy)
@@ -149,6 +149,7 @@
   VAR("ContactInfo",         STRING,   ContactInfo,          NULL),
   VAR("ControlListenAddress",LINELIST, ControlListenAddress, NULL),
   VAR("ControlPort",         UINT,     ControlPort,          "0"),
+  VAR("ControlSocket",       LINELIST, ControlSocket,        NULL),
   VAR("CookieAuthentication",BOOL,     CookieAuthentication, "0"),
   VAR("DataDirectory",       STRING,   DataDirectory,        NULL),
   OBSOLETE("DebugLogFile"),
@@ -811,6 +812,14 @@
     start_daemon();
   }
 
+#ifndef HAVE_SYS_UN_H
+  if (options->ControlSocket) {
+    *msg = tor_strdup("Unix domain sockets (ControlSocket) not supported"
+                      " on this OS/with this build.");
+    goto rollback;
+  }
+#endif
+
   if (running_tor) {
     /* We need to set the connection limit before we can open the listeners. */
     options->_ConnLimit =
Index: src/or/connection_edge.c
===================================================================
--- src/or/connection_edge.c	(revision 10502)
+++ src/or/connection_edge.c	(working copy)
@@ -1916,7 +1916,7 @@
   log_notice(LD_APP,"Making internal anonymized tunnel to %s:%d ...",
              safe_str(address),port); /* XXXX020 Downgrade back to info. */
 
-  conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP));
+  conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP, AF_INET));
   conn->_base.linked = 1; /* so that we can add it safely below. */
 
   /* populate conn->socks_request */
@@ -2210,7 +2210,7 @@
   }
 
   log_debug(LD_EXIT,"Creating new exit connection.");
-  n_stream = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
+  n_stream = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT, AF_INET));
   n_stream->_base.purpose = EXIT_PURPOSE_CONNECT;
 
   n_stream->stream_id = rh.stream_id;
@@ -2316,7 +2316,7 @@
    * resolved; but if we didn't store them in a connection like this,
    * the housekeeping in dns.c would get way more complicated.)
    */
-  dummy_conn = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
+  dummy_conn = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT, AF_INET));
   dummy_conn->stream_id = rh.stream_id;
   dummy_conn->_base.address = tor_strndup(cell->payload+RELAY_HEADER_SIZE,
                                           rh.length);
@@ -2450,7 +2450,7 @@
 
   exitconn->_base.state = EXIT_CONN_STATE_OPEN;
 
-  dirconn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR));
+  dirconn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
 
   dirconn->_base.addr = 0x7f000001;
   dirconn->_base.port = 0;
Index: src/or/or.h
===================================================================
--- src/or/or.h	(revision 10502)
+++ 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
@@ -790,6 +793,9 @@
                                  * could write? */
   time_t timestamp_created; /**< When was this connection_t created? */
 
+  int sa_family; /**< Address family of this connection's socket.  Usually
+                  * AF_INET, but it can also be AF_UNIX, or in the future
+                  * AF_INET6 */
   uint32_t addr; /**< IP of the other side of the connection; used to identify
                   * routers, along with port. */
   uint16_t port; /**< If non-zero, port  on the other end
@@ -1819,6 +1825,8 @@
   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. */
@@ -2341,7 +2349,7 @@
 const char *conn_type_to_string(int type);
 const char *conn_state_to_string(int type, int state);
 
-connection_t *connection_new(int type);
+connection_t *connection_new(int type, int sa_family);
 void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
 void connection_unregister_events(connection_t *conn);
 void connection_free(connection_t *conn);
Index: src/or/connection.c
===================================================================
--- src/or/connection.c	(revision 10502)
+++ src/or/connection.c	(working copy)
@@ -14,8 +14,9 @@
 
 #include "or.h"
 
-static connection_t *connection_create_listener(const char *listenaddress,
-                                                uint16_t listenport, int type);
+static connection_t *connection_create_listener(
+                               struct sockaddr *listensockaddr, int type,
+                               char* address);
 static int connection_init_accepted_conn(connection_t *conn,
                                          uint8_t listener_type);
 static int connection_handle_listener_read(connection_t *conn, int new_type);
@@ -156,7 +157,7 @@
  * Initialize conn's timestamps to now.
  */
 connection_t *
-connection_new(int type)
+connection_new(int type, int sa_family)
 {
   static uint32_t n_connections_allocated = 1;
   connection_t *conn;
@@ -194,6 +195,7 @@
   conn->conn_array_index = -1; /* also default to 'not used' */
 
   conn->type = type;
+  conn->sa_family = sa_family;
   if (!connection_is_listener(conn)) { /* listeners never use their buf */
     conn->inbuf = buf_new();
     conn->outbuf = buf_new();
@@ -293,6 +295,17 @@
   if (!connection_is_listener(conn)) {
     buf_free(conn->inbuf);
     buf_free(conn->outbuf);
+  } else {
+    if (conn->sa_family == AF_UNIX) {
+      /* For now only control ports can be unix domain sockets
+       * and listeners at the same time */
+      tor_assert(conn->type == CONN_TYPE_CONTROL_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);
@@ -621,44 +634,97 @@
   });
 }
 
-/** Bind a new non-blocking socket listening to
- * <b>listenaddress</b>:<b>listenport</b>, and add this new connection
- * (of type <b>type</b>) to the connection array.
+/** Create an AF_INET listenaddr struct.
+ * <b>listenaddress</b> provides the host and optionally the port information
+ * for the new structure.  If no port is provided in <b>listenaddress</b> then
+ * <b>listenport</b> is used.
  *
- * If <b>listenaddress</b> includes a port, we bind on that port;
- * otherwise, we use listenport.
+ * If not NULL <b>readable_addrress</b> will contain a copy of the host part of
+ * <b>listenaddress</b>.
+ *
+ * The listenaddr struct has to be freed by the caller.
  */
-static connection_t *
-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;
+struct sockaddr_in *
+create_inet_sockaddr(const char *listenaddress, uint16_t listenport,
+                     char **readable_address) {
+  struct sockaddr_in *listenaddr = NULL;
   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);
+  uint16_t usePort = 0;
 
-  memset(&listenaddr,0,sizeof(struct sockaddr_in));
-  if (parse_addr_port(LOG_WARN, listenaddress, &address, &addr, &usePort)<0) {
+  if (parse_addr_port(LOG_WARN,
+                      listenaddress, readable_address, &addr, &usePort)<0) {
     log_warn(LD_CONFIG,
              "Error parsing/resolving ListenAddress %s", listenaddress);
-    return NULL;
+    goto err;
   }
-
   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);
+  listenaddr = tor_malloc_zero(sizeof(struct sockaddr_in));
+  listenaddr->sin_addr.s_addr = htonl(addr);
+  listenaddr->sin_family = AF_INET;
+  listenaddr->sin_port = htons((uint16_t) usePort);
 
+  return listenaddr;
+
+ err:
+  tor_free(listenaddr);
+  return NULL;
+}
+
+#ifdef HAVE_SYS_UN_H
+/** Create an AF_UNIX listenaddr struct.
+ * <b>listenaddress</b> provides the path to the unix socket.
+ *
+ * Eventually <b>listenaddress</b> will also optionally contain user, group,
+ * and file permissions for the new socket.  But not yet. XXX
+ * Also, since we do not create the socket here the information doesn't help
+ * here.
+ *
+ * If not NULL <b>readable_addrress</b> will contain a copy of the path part of
+ * <b>listenaddress</b>.
+ *
+ * The listenaddr struct has to be freed by the caller.
+ */
+struct sockaddr_un *
+create_unix_sockaddr(const char *listenaddress, char **readable_address)
+{
+  struct sockaddr_un *sockaddr = NULL;
+
+  sockaddr = tor_malloc_zero(sizeof(struct sockaddr_un));
+  sockaddr->sun_family = AF_UNIX;
+  strncpy(sockaddr->sun_path, listenaddress, sizeof(sockaddr->sun_path));
+
+  if (readable_address)
+    *readable_address = tor_strdup(listenaddress);
+
+  return sockaddr;
+}
+#else
+struct sockaddr *
+create_unix_sockaddr(const char *listenaddress, char **readable_address)
+{
+  log_fn(LOG_ERR, LD_BUG,
+         "Unix domain sockets not supported, yet we tried to create one.");
+  assert(0);
+};
+#endif /* HAVE_SYS_UN_H */
+
+/** Bind a new non-blocking socket listening to the socket described
+ * by <b>listensockaddr</b>.
+ *
+ * <b>address</b> is only used for logging purposes and to add the information
+ * to the conn.
+ */
+static connection_t *
+connection_create_listener(struct sockaddr *listensockaddr, int type,
+  char* address)
+{
+  connection_t *conn;
+  int s; /* the socket we're going to make */
+  uint16_t usePort = 0;
+  int start_reading = 0;
+
   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,48 +734,105 @@
     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 (listensockaddr->sa_family == AF_INET) {
+    int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
+#ifndef MS_WINDOWS
+    int one=1;
+#endif
+    if (is_tcp)
+      start_reading = 1;
 
+    usePort = ntohs( (uint16_t)
+                     ((struct sockaddr_in *)listensockaddr)->sin_port);
+
+    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));
+    /* 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 (bind(s,listensockaddr,sizeof(struct sockaddr_in)) < 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 (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;
+      }
+    }
+#ifdef HAVE_SYS_UN_H
+  } else if (listensockaddr->sa_family == AF_UNIX) {
+    int len;
+    start_reading = 1;
+
+    /* For now only control ports can be unix domain sockets
+     * and listeners at the same time */
+    tor_assert(type == CONN_TYPE_CONTROL_LISTENER);
+
+    log_notice(LD_NET, "Opening %s on %s",
+               conn_type_to_string(type), address);
+
+    if (unlink(address) < 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(((struct sockaddr_un *)listensockaddr)->sun_path) +
+          sizeof(((struct sockaddr_un *)listensockaddr)->sun_family);
+    if (bind(s, listensockaddr, 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,
+      log_warn(LD_NET, "Could not listen on %s: %s", address,
                tor_socket_strerror(tor_socket_errno(s)));
       tor_close_socket(s);
       goto err;
     }
+#endif /* HAVE_SYS_UN_H */
+  } else {
+      log_err(LD_BUG,"Got unexpected address family %d.",
+              listensockaddr->sa_family);
+      tor_assert(0);
   }
 
   set_socket_nonblocking(s);
 
-  conn = connection_new(type);
+  conn = connection_new(type, listensockaddr->sa_family);
+  conn->sa_family = listensockaddr->sa_family;
   conn->s = s;
-  conn->address = address;
-  address = NULL;
+  conn->address = tor_strdup(address);
   conn->port = usePort;
 
   if (connection_add(conn) < 0) { /* no space, forget it */
@@ -722,7 +845,7 @@
             conn_type_to_string(type), usePort);
 
   conn->state = LISTENER_STATE_READY;
-  if (is_tcp) {
+  if (start_reading) {
     connection_start_reading(conn);
   } else {
     tor_assert(type == CONN_TYPE_AP_DNS_LISTENER);
@@ -732,7 +855,6 @@
   return conn;
 
  err:
-  tor_free(address);
   return NULL;
 }
 
@@ -801,56 +923,76 @@
 
   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_assert(((struct sockaddr*)addrbuf)->sa_family == conn->sa_family);
+
+  if (conn->sa_family == AF_INET) {
+    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));
+
+    /* 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;
       }
     }
-  }
-  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;
+    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, conn->sa_family);
+    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 (conn->sa_family == AF_UNIX) {
+    /* For now only control ports can be unix domain sockets
+     * and listeners at the same time */
+    tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER);
+
+    newconn = connection_new(new_type, conn->sa_family);
+    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 {
+    tor_assert(0);
+  };
+
   if (connection_add(newconn) < 0) { /* no space, forget it */
     connection_free(newconn);
     return 0; /* no need to tear down the parent */
@@ -1007,7 +1149,8 @@
                 int port_option, const char *default_addr,
                 smartlist_t *replaced_conns,
                 smartlist_t *new_conns,
-                int never_open_conns)
+                int never_open_conns,
+                int sa_family)
 {
   smartlist_t *launch = smartlist_create(), *conns;
   int free_launch_elts = 1;
@@ -1016,6 +1159,8 @@
   connection_t *conn;
   config_line_t *line;
 
+  tor_assert(sa_family == AF_INET || sa_family == AF_UNIX);
+
   if (cfg && port_option) {
     for (c = cfg; c; c = c->next) {
       smartlist_add(launch, c);
@@ -1036,7 +1181,9 @@
   conns = get_connection_array();
   SMARTLIST_FOREACH(conns, connection_t *, conn,
   {
-    if (conn->type != type || conn->marked_for_close)
+    if (conn->type != type ||
+        conn->sa_family != sa_family ||
+        conn->marked_for_close)
       continue;
     /* Okay, so this is a listener.  Is it configured? */
     line = NULL;
@@ -1044,15 +1191,28 @@
       {
         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;
+        switch (sa_family) {
+          case AF_INET:
+            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;
+              }
+            }
             break;
-          }
+          case AF_UNIX:
+            if (!strcasecmp(wanted->value, conn->address)) {
+              line = wanted;
+              break;
+            }
+            break;
+          default:
+            tor_assert(0);
         }
       });
     if (! line) {
@@ -1080,8 +1240,31 @@
   if (!never_open_conns) {
     SMARTLIST_FOREACH(launch, config_line_t *, cfg_line,
       {
-        conn = connection_create_listener(cfg_line->value,
-                                          (uint16_t) port_option, type);
+        char *address = NULL;
+        struct sockaddr *listensockaddr;
+
+        switch (sa_family) {
+          case AF_INET:
+            listensockaddr = (struct sockaddr *)
+                             create_inet_sockaddr(cfg_line->value,
+                                                  (uint16_t) port_option,
+                                                  &address);
+            break;
+          case AF_UNIX:
+            listensockaddr = (struct sockaddr *)
+                             create_unix_sockaddr(cfg_line->value,
+                                                  &address);
+            break;
+          default:
+            tor_assert(0);
+        }
+
+        if (listensockaddr) {
+          conn = connection_create_listener(listensockaddr, type, address);
+          tor_free(address);
+        } else
+          conn = NULL;
+
         if (!conn) {
           r = -1;
         } else {
@@ -1115,33 +1298,46 @@
 
   if (retry_listeners(CONN_TYPE_OR_LISTENER, options->ORListenAddress,
                       options->ORPort, "0.0.0.0",
-                      replaced_conns, new_conns, options->ClientOnly)<0)
+                      replaced_conns, new_conns, options->ClientOnly,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
                       options->DirPort, "0.0.0.0",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_AP_LISTENER, options->SocksListenAddress,
                       options->SocksPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_AP_TRANS_LISTENER, options->TransListenAddress,
                       options->TransPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_AP_NATD_LISTENER, options->NatdListenAddress,
                       options->NatdPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_AP_DNS_LISTENER, options->DNSListenAddress,
                       options->DNSPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
   if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
                       options->ControlListenAddress,
                       options->ControlPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0)<0)
+                      replaced_conns, new_conns, 0,
+                      AF_INET)<0)
     return -1;
+  if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
+                      options->ControlSocket,
+                      options->ControlSocket ? 1 : 0, NULL,
+                      replaced_conns, new_conns, 0,
+                      AF_UNIX)<0)
+    return -1;
 
   return 0;
 }
Index: src/or/directory.c
===================================================================
--- src/or/directory.c	(revision 10502)
+++ src/or/directory.c	(working copy)
@@ -490,7 +490,7 @@
       tor_assert(0);
   }
 
-  conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR));
+  conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
 
   /* set up conn so it's got all the data we need to remember */
   conn->_base.addr = addr;
Index: src/or/cpuworker.c
===================================================================
--- src/or/cpuworker.c	(revision 10502)
+++ src/or/cpuworker.c	(working copy)
@@ -339,7 +339,7 @@
   tor_free(fdarray);
 #endif
 
-  conn = connection_new(CONN_TYPE_CPUWORKER);
+  conn = connection_new(CONN_TYPE_CPUWORKER, AF_UNIX);
 
   set_socket_nonblocking(fd);
 
Index: src/or/dnsserv.c
===================================================================
--- src/or/dnsserv.c	(revision 10502)
+++ src/or/dnsserv.c	(working copy)
@@ -110,7 +110,7 @@
   /* XXXX020 Send a stream event to the controller. */
 
   /* Make a new dummy AP connection, and attach the request to it. */
-  conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP));
+  conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP, AF_INET));
   conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
   if (q->type == EVDNS_TYPE_A)
     conn->socks_request->command = SOCKS_COMMAND_RESOLVE;