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

[or-cvs] r7007: Patch from Tup to add support for transparent AP connections (in tor/trunk: . src/or)



Author: nickm
Date: 2006-08-10 05:01:37 -0400 (Thu, 10 Aug 2006)
New Revision: 7007

Modified:
   tor/trunk/
   tor/trunk/configure.in
   tor/trunk/src/or/config.c
   tor/trunk/src/or/connection.c
   tor/trunk/src/or/connection_edge.c
   tor/trunk/src/or/control.c
   tor/trunk/src/or/hibernate.c
   tor/trunk/src/or/or.h
   tor/trunk/src/or/relay.c
Log:
 r7299@Kushana:  nickm | 2006-08-10 01:08:58 -0700
 Patch from Tup to add support for transparent AP connections: this basically bundles the functionality of trans-proxy-tor into the tor mainline.  Now hosts with compliant pf/netfilter implementations can redirect TCP connections straight to Tor without diverting through SOCKS.



Property changes on: tor/trunk
___________________________________________________________________
Name: svk:merge
   - 17f730b7-d419-0410-b50f-85ee4b70197a:/local/or/tor/trunk:8245
1f724f9b-111a-0410-b636-93f1a77c1813:/local/or/tor/trunk:8207
96637b51-b116-0410-a10e-9941ebb49b64:/tor/branches/spec:7005
c95137ef-5f19-0410-b913-86e773d04f59:/tor/branches/eventdns:7014
c95137ef-5f19-0410-b913-86e773d04f59:/tor/branches/mmap:7030
c95137ef-5f19-0410-b913-86e773d04f59:/tor/branches/oo-connections:6950
   + 17f730b7-d419-0410-b50f-85ee4b70197a:/local/or/tor/trunk:8245
1f724f9b-111a-0410-b636-93f1a77c1813:/local/or/tor/trunk:8207
96637b51-b116-0410-a10e-9941ebb49b64:/tor/branches/spec:7005
c95137ef-5f19-0410-b913-86e773d04f59:/tor/branches/eventdns:7014
c95137ef-5f19-0410-b913-86e773d04f59:/tor/branches/mmap:7030
c95137ef-5f19-0410-b913-86e773d04f59:/tor/branches/oo-connections:6950
c95137ef-5f19-0410-b913-86e773d04f59:/tor/branches/trans-ap:7299

Modified: tor/trunk/configure.in
===================================================================
--- tor/trunk/configure.in	2006-08-10 08:13:41 UTC (rev 7006)
+++ tor/trunk/configure.in	2006-08-10 09:01:37 UTC (rev 7007)
@@ -56,6 +56,26 @@
    AC_DEFINE([USE_EVENTDNS], 1, "Define to 1 if we'll be using eventdns.c")
 fi
 
+AC_ARG_ENABLE(transparent,
+     AC_HELP_STRING(--disable-transparent, disable transparent proxy support),
+     [case "${enableval}" in 
+        yes) transparent=true ;;
+        no)  transparent=false ;;
+        *) AC_MSG_ERROR(bad value for --enable-transparent) ;;
+      esac], [transparent=true])
+if test x$transparent = xtrue; then
+   AC_DEFINE(USE_TRANSPARENT, 1, "Define to enable transparent proxy support")
+   case $host in
+     *-*-linux* )
+       AC_DEFINE(TRANS_NETFILTER, 1, "Define for transparent netfilter") ;;
+     *-*-openbsd*)
+       AC_DEFINE(TRANS_PF, 1, "Define for transparent pf")
+       AC_DEFINE(OPENBSD, 1, "Define to handle pf on OpenBSD properly") ;;
+     *-*-*bsd* )
+       AC_DEFINE(TRANS_PF, 1, "Define for transparent pf") ;;
+   esac
+fi
+
 case $host in
    *-*-solaris* )
      AC_DEFINE(_REENTRANT, 1, [Define on some platforms to activate x_r() functions in time.h])

Modified: tor/trunk/src/or/config.c
===================================================================
--- tor/trunk/src/or/config.c	2006-08-10 08:13:41 UTC (rev 7006)
+++ tor/trunk/src/or/config.c	2006-08-10 09:01:37 UTC (rev 7007)
@@ -235,6 +235,8 @@
   VAR("TrackHostExits",      CSV,      TrackHostExits,       NULL),
   VAR("TrackHostExitsExpire",INTERVAL, TrackHostExitsExpire, "30 minutes"),
   OBSOLETE("TrafficShaping"),
+  VAR("TransListenAddress",  LINELIST, TransListenAddress,   NULL),
+  VAR("TransPort",           UINT,     TransPort,            "0"),
   VAR("UseEntryGuards",      BOOL,     UseEntryGuards,       "1"),
   VAR("User",                STRING,   User,                 NULL),
   VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir,   "0"),
@@ -2067,6 +2069,7 @@
     REJECT("SocksPort must be defined if SocksListenAddress is defined.");
 #endif
 
+  /* XXX TransListenAddress should be checked here as well */
   if (options->SocksListenAddress) {
     config_line_t *line = NULL;
     char *address = NULL;
@@ -2144,15 +2147,24 @@
   if (options->SocksPort < 0 || options->SocksPort > 65535)
     REJECT("SocksPort option out of bounds.");
 
-  if (options->SocksPort == 0 && options->ORPort == 0)
-    REJECT("SocksPort and ORPort are both undefined? Quitting.");
+  if (options->TransPort < 0 || options->TransPort > 65535)
+    REJECT("TransPort option out of bounds.");
 
+  if (options->SocksPort == 0 && options->TransPort == 0 &&
+      options->ORPort == 0)
+    REJECT("SocksPort, TransPort, and ORPort are all undefined? Quitting.");
+
   if (options->ControlPort < 0 || options->ControlPort > 65535)
     REJECT("ControlPort option out of bounds.");
 
   if (options->DirPort < 0 || options->DirPort > 65535)
     REJECT("DirPort option out of bounds.");
 
+#ifndef USE_TRANSPARENT
+  if (options->TransPort || options->TransListenAddress)
+    REJECT("TransPort and TransListenAddress are disabled in this build.");
+#endif
+
   if (options->StrictExitNodes &&
       (!options->ExitNodes || !strlen(options->ExitNodes)) &&
       (!old_options ||

Modified: tor/trunk/src/or/connection.c
===================================================================
--- tor/trunk/src/or/connection.c	2006-08-10 08:13:41 UTC (rev 7006)
+++ tor/trunk/src/or/connection.c	2006-08-10 09:01:37 UTC (rev 7007)
@@ -16,7 +16,8 @@
 
 static connection_t *connection_create_listener(const char *listenaddress,
                                                 uint16_t listenport, int type);
-static int connection_init_accepted_conn(connection_t *conn);
+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);
 static int connection_receiver_bucket_should_increase(or_connection_t *conn);
 static int connection_finished_flushing(connection_t *conn);
@@ -44,6 +45,7 @@
     case CONN_TYPE_OR: return "OR";
     case CONN_TYPE_EXIT: return "Exit";
     case CONN_TYPE_AP_LISTENER: return "Socks listener";
+    case CONN_TYPE_AP_TRANS_LISTENER: return "Transparent listener";
     case CONN_TYPE_AP: return "Socks";
     case CONN_TYPE_DIR_LISTENER: return "Directory listener";
     case CONN_TYPE_DIR: return "Directory";
@@ -69,6 +71,7 @@
   switch (type) {
     case CONN_TYPE_OR_LISTENER:
     case CONN_TYPE_AP_LISTENER:
+    case CONN_TYPE_AP_TRANS_LISTENER:
     case CONN_TYPE_DIR_LISTENER:
     case CONN_TYPE_CONTROL_LISTENER:
       if (state == LISTENER_STATE_READY)
@@ -93,6 +96,7 @@
       break;
     case CONN_TYPE_AP:
       switch (state) {
+        case AP_CONN_STATE_ORIGDST_WAIT:
         case AP_CONN_STATE_SOCKS_WAIT: return "waiting for dest info";
         case AP_CONN_STATE_RENDDESC_WAIT: return "waiting for rendezvous desc";
         case AP_CONN_STATE_CONTROLLER_WAIT: return "waiting for controller";
@@ -786,7 +790,7 @@
     return 0; /* no need to tear down the parent */
   }
 
-  if (connection_init_accepted_conn(newconn) < 0) {
+  if (connection_init_accepted_conn(newconn, conn->type) < 0) {
     connection_mark_for_close(newconn);
     return 0;
   }
@@ -797,7 +801,7 @@
  * If conn is an OR, start the tls handshake.
  */
 static int
-connection_init_accepted_conn(connection_t *conn)
+connection_init_accepted_conn(connection_t *conn, uint8_t listener_type)
 {
   connection_start_reading(conn);
 
@@ -806,7 +810,14 @@
       control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW);
       return connection_tls_start_handshake(TO_OR_CONN(conn), 1);
     case CONN_TYPE_AP:
-      conn->state = AP_CONN_STATE_SOCKS_WAIT;
+      switch (listener_type) {
+        case CONN_TYPE_AP_LISTENER:
+          conn->state = AP_CONN_STATE_SOCKS_WAIT;
+          break;
+        case CONN_TYPE_AP_TRANS_LISTENER:
+          conn->state = AP_CONN_STATE_ORIGDST_WAIT;
+          break;
+      }
       break;
     case CONN_TYPE_DIR:
       conn->purpose = DIR_PURPOSE_SERVER;
@@ -1046,6 +1057,10 @@
                       options->SocksPort, "127.0.0.1", force,
                       replaced_conns, new_conns, 0)<0)
     return -1;
+  if (retry_listeners(CONN_TYPE_AP_TRANS_LISTENER, options->TransListenAddress,
+                      options->TransPort, "127.0.0.1", force,
+                      replaced_conns, new_conns, 0)<0)
+    return -1;
   if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
                       options->ControlListenAddress,
                       options->ControlPort, "127.0.0.1", force,
@@ -1260,6 +1275,7 @@
     case CONN_TYPE_OR_LISTENER:
       return connection_handle_listener_read(conn, CONN_TYPE_OR);
     case CONN_TYPE_AP_LISTENER:
+    case CONN_TYPE_AP_TRANS_LISTENER:
       return connection_handle_listener_read(conn, CONN_TYPE_AP);
     case CONN_TYPE_DIR_LISTENER:
       return connection_handle_listener_read(conn, CONN_TYPE_DIR);
@@ -1896,6 +1912,7 @@
 {
   if (conn->type == CONN_TYPE_OR_LISTENER ||
       conn->type == CONN_TYPE_AP_LISTENER ||
+      conn->type == CONN_TYPE_AP_TRANS_LISTENER ||
       conn->type == CONN_TYPE_DIR_LISTENER ||
       conn->type == CONN_TYPE_CONTROL_LISTENER)
     return 1;
@@ -2248,6 +2265,7 @@
     {
     case CONN_TYPE_OR_LISTENER:
     case CONN_TYPE_AP_LISTENER:
+    case CONN_TYPE_AP_TRANS_LISTENER:
     case CONN_TYPE_DIR_LISTENER:
     case CONN_TYPE_CONTROL_LISTENER:
       tor_assert(conn->state == LISTENER_STATE_READY);

Modified: tor/trunk/src/or/connection_edge.c
===================================================================
--- tor/trunk/src/or/connection_edge.c	2006-08-10 08:13:41 UTC (rev 7006)
+++ tor/trunk/src/or/connection_edge.c	2006-08-10 09:01:37 UTC (rev 7007)
@@ -17,6 +17,7 @@
 static smartlist_t *redirect_exit_list = NULL;
 
 static int connection_ap_handshake_process_socks(edge_connection_t *conn);
+static int connection_ap_process_transparent(edge_connection_t *conn);
 
 /** An AP stream has failed/finished. If it hasn't already sent back
  * a socks reply, send one now (based on endreason). Also set
@@ -80,6 +81,7 @@
 /** Handle new bytes on conn->inbuf based on state:
  *   - If it's waiting for socks info, try to read another step of the
  *     socks handshake out of conn->inbuf.
+ *   - If it's waiting for the original destination, fetch it.
  *   - If it's open, then package more relay cells from the stream.
  *   - Else, leave the bytes on inbuf alone for now.
  *
@@ -98,6 +100,12 @@
         return -1;
       }
       return 0;
+    case AP_CONN_STATE_ORIGDST_WAIT:
+      if (connection_ap_process_transparent(conn) < 0) {
+        /* already marked */
+        return -1;
+      }
+      return 0;
     case AP_CONN_STATE_OPEN:
     case EXIT_CONN_STATE_OPEN:
       if (connection_edge_package_raw_inbuf(conn, package_partial) < 0) {
@@ -234,6 +242,7 @@
       connection_edge_consider_sending_sendme(conn);
       return 0;
     case AP_CONN_STATE_SOCKS_WAIT:
+    case AP_CONN_STATE_ORIGDST_WAIT:
     case AP_CONN_STATE_RENDDESC_WAIT:
     case AP_CONN_STATE_CIRCUIT_WAIT:
     case AP_CONN_STATE_CONNECT_WAIT:
@@ -1216,6 +1225,89 @@
   return 0; /* unreached but keeps the compiler happy */
 }
 
+/** Fetch the original destination address and port from a
+ * system-specific interface and put them into a
+ * socks_request_t as if they came from a socks request.
+ *
+ * Return -1 if an error prevents fetching the destination,
+ * else return 0.
+ */
+static int
+connection_ap_get_original_destination(edge_connection_t *conn,
+                                       socks_request_t *req)
+{
+#ifdef TRANS_NETFILTER
+  /* Linux 2.4+ */
+  struct sockaddr_in orig_dst;
+  socklen_t orig_dst_len = sizeof(orig_dst);
+  char tmpbuf[INET_NTOA_BUF_LEN];
+
+  if (getsockopt(conn->_base.s, SOL_IP, SO_ORIGINAL_DST,
+                 (struct sockaddr*)&orig_dst, &orig_dst_len) < 0) {
+    int e = tor_socket_errno(conn->_base.s);
+    log_warn(LD_NET, "getsockopt() failed: %s", tor_socket_strerror(e));
+    return -1;
+  }
+
+  tor_inet_ntoa(&orig_dst.sin_addr, tmpbuf, sizeof(tmpbuf));
+  strlcpy(req->address, tmpbuf, sizeof(req->address));
+  req->port = ntohs(orig_dst.sin_port);
+#endif
+
+#ifdef TRANS_PF
+  struct sockaddr_in proxy_addr;
+  socklen_t proxy_addr_len = sizeof(proxy_addr);
+  char tmpbuf[INET_NTOA_BUF_LEN];
+  struct pfioc_natlook pnl;
+  int pf = -1;
+
+  if (getsockname(conn->_base.s, (struct sockaddr*)&proxy_addr,
+                  &proxy_addr_len) < 0) {
+    int e = tor_socket_errno(conn->_base.s);
+    log_warn(LD_NET, "getsockname() failed: %s", tor_socket_strerror(e));
+    return -1;
+  }
+
+  memset(&pnl, 0, sizeof(pnl));
+  pnl.af              = AF_INET;
+  pnl.proto           = IPPROTO_TCP;
+  pnl.direction       = PF_OUT;
+  pnl.saddr.v4.s_addr = htonl(conn->_base.addr);
+  pnl.sport           = htons(conn->_base.port);
+  pnl.daddr.v4.s_addr = proxy_addr.sin_addr.s_addr;
+  pnl.dport           = proxy_addr.sin_port;
+
+  /* XXX We should open the /dev/pf device once and close it at cleanup time
+   * instead of reopening it for every connection. Ideally, it should be
+   * opened before dropping privs. */
+#ifdef OPENBSD
+  /* only works on OpenBSD */
+  pf = open("/dev/pf", O_RDONLY);
+#else
+  /* works on NetBSD and FreeBSD */
+  pf = open("/dev/pf", O_RDWR);
+#endif
+
+  if (pf < 0) {
+    log_warn(LD_NET, "open(\"/dev/pf\") failed: %s", strerror(errno));
+    return -1;
+  }
+
+  if (ioctl(pf, DIOCNATLOOK, &pnl) < 0) {
+    log_warn(LD_NET, "ioctl(DIOCNATLOOK) failed: %s", strerror(errno));
+    close(pf);
+    return -1;
+  }
+  close(pf);
+
+  tor_inet_ntoa(&pnl.rdaddr.v4, tmpbuf, sizeof(tmpbuf));
+  strlcpy(req->address, tmpbuf, sizeof(req->address));
+  req->port = ntohs(pnl.rdport);
+#endif
+
+  return 0;
+}
+
 /** connection_edge_process_inbuf() found a conn in state
  * socks_wait. See if conn->inbuf has the right bytes to proceed with
  * the socks handshake.
@@ -1278,6 +1370,48 @@
   return connection_ap_handshake_rewrite_and_attach(conn, NULL);
 }
 
+/** connection_edge_process_inbuf() found a conn in state
+ * origdst_wait. Get the original destination and
+ * send it to connection_ap_handshake_rewrite_and_attach().
+ *
+ * Return -1 if an unexpected error with conn (and it should be marked
+ * for close), else return 0.
+ */
+static int
+connection_ap_process_transparent(edge_connection_t *conn)
+{
+  socks_request_t *socks;
+  or_options_t *options = get_options();
+
+  tor_assert(conn);
+  tor_assert(conn->_base.type == CONN_TYPE_AP);
+  tor_assert(conn->_base.state == AP_CONN_STATE_ORIGDST_WAIT);
+  tor_assert(conn->socks_request);
+  socks = conn->socks_request;
+
+  /* pretend that a socks handshake completed so we don't try to
+   * send a socks reply down a transparent conn */
+  socks->command = SOCKS_COMMAND_CONNECT;
+  socks->has_finished = 1;
+
+  log_debug(LD_APP,"entered.");
+
+  if (connection_ap_get_original_destination(conn, socks) < 0) {
+    log_warn(LD_APP,"Fetching original destination failed. Closing.");
+    connection_mark_unattached_ap(conn, 0);
+    return -1;
+  }
+  /* we have the original destination */
+
+  control_event_stream_status(conn, STREAM_EVENT_NEW);
+
+  if (options->LeaveStreamsUnattached) {
+    conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
+    return 0;
+  }
+  return connection_ap_handshake_rewrite_and_attach(conn, NULL);
+}
+
 /** Iterate over the two bytes of stream_id until we get one that is not
  * already in use; return it. Return 0 if can't get a unique stream_id.
  */

Modified: tor/trunk/src/or/control.c
===================================================================
--- tor/trunk/src/or/control.c	2006-08-10 08:13:41 UTC (rev 7006)
+++ tor/trunk/src/or/control.c	2006-08-10 09:01:37 UTC (rev 7007)
@@ -1423,7 +1423,8 @@
       origin_circuit_t *origin_circ = NULL;
       if (conns[i]->type != CONN_TYPE_AP ||
           conns[i]->marked_for_close ||
-          conns[i]->state == AP_CONN_STATE_SOCKS_WAIT)
+          conns[i]->state == AP_CONN_STATE_SOCKS_WAIT ||
+          conns[i]->state == AP_CONN_STATE_ORIGDST_WAIT)
         continue;
       conn = TO_EDGE_CONN(conns[i]);
       switch (conn->_base.state)

Modified: tor/trunk/src/or/hibernate.c
===================================================================
--- tor/trunk/src/or/hibernate.c	2006-08-10 08:13:41 UTC (rev 7006)
+++ tor/trunk/src/or/hibernate.c	2006-08-10 09:01:37 UTC (rev 7007)
@@ -712,6 +712,7 @@
   /* close listeners. leave control listener(s). */
   while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) ||
          (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) ||
+         (conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) ||
          (conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) {
     log_info(LD_NET,"Closing listener type %d", conn->type);
     connection_mark_for_close(conn);

Modified: tor/trunk/src/or/or.h
===================================================================
--- tor/trunk/src/or/or.h	2006-08-10 08:13:41 UTC (rev 7006)
+++ tor/trunk/src/or/or.h	2006-08-10 09:01:37 UTC (rev 7007)
@@ -113,6 +113,15 @@
 #error "Tor requires libevent to build."
 #endif
 
+#ifdef TRANS_NETFILTER
+#include <linux/netfilter_ipv4.h>
+#endif
+
+#ifdef TRANS_PF
+#include <net/if.h>
+#include <net/pfvar.h>
+#endif
+
 #include "../common/crypto.h"
 #include "../common/tortls.h"
 #include "../common/log.h"
@@ -215,7 +224,9 @@
 #define CONN_TYPE_CONTROL_LISTENER 12
 /** Type for connections from user interface process. */
 #define CONN_TYPE_CONTROL 13
-#define _CONN_TYPE_MAX 13
+/** Type for sockets listening for transparent proxy connections. */
+#define CONN_TYPE_AP_TRANS_LISTENER 14
+#define _CONN_TYPE_MAX 14
 
 #define CONN_IS_EDGE(x) \
   ((x)->type == CONN_TYPE_EXIT || (x)->type == CONN_TYPE_AP)
@@ -283,7 +294,10 @@
 #define AP_CONN_STATE_RESOLVE_WAIT 10
 /** State for a SOCKS connection: ready to send and receive. */
 #define AP_CONN_STATE_OPEN 11
-#define _AP_CONN_STATE_MAX 11
+/** State for a transparent proxy connection: waiting for original
+ * destination. */
+#define AP_CONN_STATE_ORIGDST_WAIT 12
+#define _AP_CONN_STATE_MAX 12
 
 #define _DIR_CONN_STATE_MIN 1
 /** State for connection to directory server: waiting for connect(). */
@@ -1386,6 +1400,8 @@
   config_line_t *DirPolicy; /**< Lists of dir policy components */
   /** Addresses to bind for listening for SOCKS connections. */
   config_line_t *SocksListenAddress;
+  /** Addresses to bind for listening for transparent connections. */
+  config_line_t *TransListenAddress;
   /** Addresses to bind for listening for OR connections. */
   config_line_t *ORListenAddress;
   /** Addresses to bind for listening for directory connections. */
@@ -1407,6 +1423,7 @@
                              * length (alpha in geometric distribution). */
   int ORPort; /**< Port to listen on for OR connections. */
   int SocksPort; /**< Port to listen on for SOCKS connections. */
+  int TransPort; /**< Port to listen on for transparent connections. */
   int ControlPort; /**< Port to listen on for control connections. */
   int DirPort; /**< Port to listen on for directory connections. */
   int AssumeReachable; /**< Whether to publish our descriptor regardless. */

Modified: tor/trunk/src/or/relay.c
===================================================================
--- tor/trunk/src/or/relay.c	2006-08-10 08:13:41 UTC (rev 7006)
+++ tor/trunk/src/or/relay.c	2006-08-10 09:01:37 UTC (rev 7007)
@@ -845,7 +845,9 @@
                                 conn->chosen_exit_name, ttl);
     }
     circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
-    connection_ap_handshake_socks_reply(conn, NULL, 0, SOCKS5_SUCCEEDED);
+    /* don't send a socks reply to transparent conns */
+    if (!conn->socks_request->has_finished)
+      connection_ap_handshake_socks_reply(conn, NULL, 0, SOCKS5_SUCCEEDED);
     /* handle anything that might have queued */
     if (connection_edge_package_raw_inbuf(conn, 1) < 0) {
       /* (We already sent an end cell if possible) */