[or-cvs] Implement RESOLVE/RESOLVED cells and socks resolve code

Nick Mathewson nickm at seul.org
Thu Jun 17 18:13:13 UTC 2004


Update of /home/or/cvsroot/src/or
In directory moria.mit.edu:/tmp/cvs-serv430/src/or

Modified Files:
	buffers.c circuituse.c connection.c connection_edge.c dns.c 
	or.h relay.c 
Log Message:
Implement RESOLVE/RESOLVED cells and socks resolve code

Index: buffers.c
===================================================================
RCS file: /home/or/cvsroot/src/or/buffers.c,v
retrieving revision 1.95
retrieving revision 1.96
diff -u -d -r1.95 -r1.96
--- buffers.c	1 Jun 2004 17:31:13 -0000	1.95
+++ buffers.c	17 Jun 2004 18:13:09 -0000	1.96
@@ -409,6 +409,9 @@
   return 1;
 }
 
+#define SOCKS_COMMAND_CONNECT 0x01
+#define SOCKS_COMMAND_RESOLVE 0xF0
+
 /** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one
  * of the forms
  *  - socks4: "socksheader username\\0"
@@ -467,8 +470,12 @@
       log_fn(LOG_DEBUG,"socks5: checking request");
       if(buf->datalen < 8) /* basic info plus >=2 for addr plus 2 for port */
         return 0; /* not yet */
-      if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */
-        log_fn(LOG_WARN,"socks5: command %d not '1'. Rejecting.",*(buf->mem+1));
+      req->command = (unsigned char) *(buf->mem+1);
+      if(req->command != SOCKS_COMMAND_CONNECT &&
+         req->command != SOCKS_COMMAND_RESOLVE) {
+        /* not a connect or resolve? we don't support it. */
+        log_fn(LOG_WARN,"socks5: command %d not recognized. Rejecting.",
+               req->command);
         return -1;
       }
       switch(*(buf->mem+3)) { /* address type */
@@ -516,14 +523,18 @@
       if(buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */
         return 0; /* not yet */
 
-      if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */
-        log_fn(LOG_WARN,"socks4: command %d not '1'. Rejecting.",*(buf->mem+1));
+      req->command = (unsigned char) *(buf->mem+1);
+      if(req->command != SOCKS_COMMAND_CONNECT &&
+         req->command != SOCKS_COMMAND_RESOLVE) {
+        /* not a connect or resolve? we don't support it. */
+        log_fn(LOG_WARN,"socks4: command %d not recognized. Rejecting.",
+               req->command);
         return -1;
       }
 
       req->port = ntohs(*(uint16_t*)(buf->mem+2));
       destip = ntohl(*(uint32_t*)(buf->mem+4));
-      if(!req->port || !destip) {
+      if((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) {
         log_fn(LOG_WARN,"socks4: Port or DestIP is zero. Rejecting.");
         return -1;
       }

Index: circuituse.c
===================================================================
RCS file: /home/or/cvsroot/src/or/circuituse.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- circuituse.c	15 May 2004 07:21:24 -0000	1.2
+++ circuituse.c	17 Jun 2004 18:13:09 -0000	1.3
@@ -73,7 +73,14 @@
       return 0; /* this circuit is screwed and doesn't know it yet */
     }
 
-    if(purpose == CIRCUIT_PURPOSE_C_GENERAL) {
+    if (conn->socks_request &&
+        conn->socks_request->command == SOCKS_COMMAND_RESOLVE) {
+      /* 0.0.7 servers and earlier don't support DNS resolution.  There are no
+       * ORs running code before 0.0.7, so we only worry about 0.0.7.  Once all
+       * servers are running 0.0.8, remove this check. */
+      if (!strncmp(exitrouter->platform, "Tor 0.0.7", 9))
+        return 0;
+    } else if(purpose == CIRCUIT_PURPOSE_C_GENERAL) {
       if(connection_ap_can_use_exit(conn, exitrouter) == ADDR_POLICY_REJECTED) {
         /* can't exit from this router */
         return 0;
@@ -618,10 +625,12 @@
                                 circuit_t **circp) {
   circuit_t *circ;
   uint32_t addr;
+  int is_resolve;
 
   tor_assert(conn);
   tor_assert(circp);
   tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+  is_resolve = conn->socks_request->command == SOCKS_COMMAND_RESOLVE;
 
   circ = circuit_get_best(conn, 1, desired_circuit_purpose);
 
@@ -630,7 +639,8 @@
     return 1; /* we're happy */
   }
 
-  if(!connection_edge_is_rendezvous_stream(conn)) { /* general purpose circ */
+  /* Do we need to check exit policy? */
+  if(!is_resolve && !connection_edge_is_rendezvous_stream(conn)) {
     addr = client_dns_lookup_entry(conn->socks_request->address);
     if(router_exit_policy_all_routers_reject(addr, conn->socks_request->port)) {
       log_fn(LOG_WARN,"No Tor server exists that allows exit to %s:%d. Rejecting.",
@@ -742,10 +752,13 @@
       circ->timestamp_dirty = time(NULL);
 
     link_apconn_to_circ(conn, circ);
-    connection_ap_handshake_send_begin(conn, circ);
+    tor_assert(conn->socks_request);
+    if (conn->socks_request->command == SOCKS_COMMAND_CONNECT)
+      connection_ap_handshake_send_begin(conn, circ);
+    else
+      connection_ap_handshake_send_resolve(conn, circ);
 
     return 1;
-
   } else { /* we're a rendezvous conn */
     circuit_t *rendcirc=NULL, *introcirc=NULL;
 

Index: connection.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection.c,v
retrieving revision 1.231
retrieving revision 1.232
diff -u -d -r1.231 -r1.232
--- connection.c	5 Jun 2004 01:50:35 -0000	1.231
+++ connection.c	17 Jun 2004 18:13:09 -0000	1.232
@@ -1276,7 +1276,10 @@
   } else {
     tor_assert(!conn->socks_request);
   }
-  if(conn->type != CONN_TYPE_DIR) {
+  if (conn->type == CONN_TYPE_EXIT) {
+    tor_assert(conn->purpose == EXIT_PURPOSE_CONNECT ||
+               conn->purpose == EXIT_PURPOSE_RESOLVE);
+  } else if(conn->type != CONN_TYPE_DIR) {
     tor_assert(!conn->purpose); /* only used for dir types currently */
   }
 

Index: connection_edge.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection_edge.c,v
retrieving revision 1.193
retrieving revision 1.194
diff -u -d -r1.193 -r1.194
--- connection_edge.c	2 Jun 2004 18:32:24 -0000	1.193
+++ connection_edge.c	17 Jun 2004 18:13:09 -0000	1.194
@@ -371,6 +371,23 @@
     return sockshere;
   } /* else socks handshake is done, continue processing */
 
+  if (socks->command == SOCKS_COMMAND_RESOLVE) {
+    /* Reply to resolves immediately if we can. */
+    if (strlen(socks->address) > RELAY_PAYLOAD_SIZE) {
+      connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_ERROR,0,NULL);
+      conn->socks_request->has_finished = 1;
+      connection_mark_for_close(conn);
+    }
+    uint32_t answer = htonl(client_dns_lookup_entry(socks->address));
+    if (answer) {
+      connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_IPV4,4,
+                                             (char*)&answer);
+      conn->socks_request->has_finished = 1;
+      connection_mark_for_close(conn);
+      return 0;
+    }
+  }
+
   /* this call _modifies_ socks->address iff it's a hidden-service request */
   if (rend_parse_rendezvous_address(socks->address) < 0) {
     /* normal request */
@@ -487,6 +504,46 @@
   return 0;
 }
 
+/** Write a relay resolve cell, using destaddr and destport from ap_conn's
+ * socks_request field, and send it down circ.
+ *
+ * If ap_conn is broken, mark it for close and return -1. Else return 0.
+ */
+int connection_ap_handshake_send_resolve(connection_t *ap_conn, circuit_t *circ)
+{
+  int payload_len;
+  const char *string_addr;
+
+  tor_assert(ap_conn->type == CONN_TYPE_AP);
+  tor_assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+  tor_assert(ap_conn->socks_request);
+  tor_assert(ap_conn->socks_request->command == SOCKS_COMMAND_RESOLVE);
+  tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_GENERAL);
+
+  ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
+  if (ap_conn->stream_id==0) {
+    /* Don't send end: there is no 'other side' yet */
+    ap_conn->has_sent_end = 1;
+    connection_mark_for_close(ap_conn);
+    circuit_mark_for_close(circ);
+    return -1;
+  }
+
+  string_addr = ap_conn->socks_request->address;
+  payload_len = strlen(string_addr);
+  tor_assert(strlen(string_addr) <= RELAY_PAYLOAD_SIZE);
+
+  log_fn(LOG_DEBUG,"Sending relay cell to begin stream %d.",ap_conn->stream_id);
+
+  if(connection_edge_send_command(ap_conn, circ, RELAY_COMMAND_RESOLVE,
+                           string_addr, payload_len, ap_conn->cpath_layer) < 0)
+    return -1; /* circuit is closed, don't continue */
+
+  ap_conn->state = AP_CONN_STATE_RESOLVE_WAIT;
+  log_fn(LOG_INFO,"Address sent for resolve, ap socket %d, n_circ_id %d",ap_conn->s,circ->n_circ_id);
+  return 0;
+}
+
 /** Make an AP connection_t, do a socketpair and attach one side
  * to the conn, connection_add it, initialize it to circuit_wait,
  * and call connection_ap_handshake_attach_circuit(conn) on it.
@@ -544,6 +601,59 @@
   return fd[1];
 }
 
+void connection_ap_handshake_socks_resolved(connection_t *conn,
+                                            int answer_type,
+                                            int answer_len,
+                                            const char *answer)
+{
+  char buf[256];
+  int replylen;
+
+  if (answer_type == RESOLVED_TYPE_IPV4) {
+    uint32_t a = get_uint32(answer);
+    client_dns_set_entry(conn->socks_request->address, ntohl(a));
+  }
+
+  if (conn->socks_request->socks_version == 4) {
+    buf[0] = 0x00; /* version */
+    if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) {
+      buf[1] = 90; /* "Granted" */
+      set_uint16(buf+2, 0);
+      memcpy(buf+4, answer, 4); /* address */
+      replylen = SOCKS4_NETWORK_LEN;
+   } else {
+      buf[1] = 91; /* "error" */
+      memset(buf+2, 0, 6);
+      replylen = SOCKS4_NETWORK_LEN;
+    }
+  } else {
+    /* SOCKS5 */
+    buf[0] = 0x05; /* version */
+    if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) {
+      buf[1] = 0; /* succeeded */
+      buf[2] = 0; /* reserved */
+      buf[3] = 0x01; /* IPv4 address type */
+      memcpy(buf+4, answer, 4); /* address */
+      set_uint16(buf+8, 0); /* port == 0. */
+      replylen = 10;
+    } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) {
+      buf[1] = 0; /* succeeded */
+      buf[2] = 0; /* reserved */
+      buf[3] = 0x04; /* IPv6 address type */
+      memcpy(buf+4, answer, 16); /* address */
+      set_uint16(buf+20, 0); /* port == 0. */
+      replylen = 22;
+    } else {
+      buf[1] = 0x04; /* host unreachable */
+      memset(buf+2, 0, 8);
+      replylen = 10;
+    }
+  }
+  connection_ap_handshake_socks_reply(conn, buf, replylen,
+                                      answer_type == RESOLVED_TYPE_IPV4 ||
+                                      answer_type == RESOLVED_TYPE_IPV6);
+}
+
 /** Send a socks reply to stream <b>conn</b>, using the appropriate
  * socks version, etc.
  *
@@ -631,6 +741,7 @@
 
   log_fn(LOG_DEBUG,"Creating new exit connection.");
   n_stream = connection_new(CONN_TYPE_EXIT);
+  n_stream->purpose = EXIT_PURPOSE_CONNECT;
 
   n_stream->stream_id = rh.stream_id;
   n_stream->port = atoi(colon+1);
@@ -694,6 +805,52 @@
   return 0;
 }
 
+/**
+ * Called when we receive a RELAY_RESOLVE cell 'cell' along the circuit 'circ';
+ * begin resolving the hostname, and (eventually) reply with a RESOLVED cell.
+ */
+int connection_exit_begin_resolve(cell_t *cell, circuit_t *circ) {
+  connection_t *dummy_conn;
+  relay_header_t rh;
+
+  assert_circuit_ok(circ);
+  relay_header_unpack(&rh, cell->payload);
+
+
+  /* This 'dummy_conn' only exists to remember the stream ID
+   * associated with the resolve request; and to make the
+   * implementation of dns.c more uniform.  (We really only need to
+   * remember the circuit, the stream ID, and the hostname to be
+   * 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 = connection_new(CONN_TYPE_EXIT);
+  dummy_conn->stream_id = rh.stream_id;
+  dummy_conn->address = tor_strndup(cell->payload+RELAY_HEADER_SIZE,
+                                    rh.length);
+  dummy_conn->port = 0;
+  dummy_conn->state = EXIT_CONN_STATE_RESOLVEFAILED;
+  dummy_conn->purpose = EXIT_PURPOSE_RESOLVE;
+
+  /* send it off to the gethostbyname farm */
+  switch(dns_resolve(dummy_conn)) {
+    case 1: /* resolve worked; resolved cell was sent. */
+      connection_free(dummy_conn);
+      return 0;
+    case -1: /* resolve failed; resolved cell was sent. */
+      log_fn(LOG_INFO,"Resolve failed (%s).",dummy_conn->address);
+      connection_free(dummy_conn);
+      break;
+    case 0: /* resolve added to pending list */
+      /* add it into the linked list of resolving_streams on this circuit */
+      dummy_conn->next_stream = circ->resolving_streams;
+      circ->resolving_streams = dummy_conn;
+      assert_circuit_ok(circ);
+      ;
+  }
+  return 0;
+}
+
 /** Connect to conn's specified addr and port. If it worked, conn
  * has now been added to the connection_array.
  *
@@ -776,6 +933,12 @@
   log_fn(LOG_DEBUG,"considering nickname %s, for address %s / port %d:",
          exit->nickname, conn->socks_request->address,
          conn->socks_request->port);
+  if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE) {
+    /* 0.0.7 servers and earlier don't support DNS resolution.  There are no
+     * ORs running code before 0.0.7, so we only worry about 0.0.7.  Once all
+     * servers are running 0.0.8, remove this check. */
+    return strncmp(exit->platform, "Tor 0.0.7", 9) ? 1 : 0;
+  }
   addr = client_dns_lookup_entry(conn->socks_request->address);
   return router_compare_addr_to_exit_policy(addr,
            conn->socks_request->port, exit->exit_policy);

Index: dns.c
===================================================================
RCS file: /home/or/cvsroot/src/or/dns.c,v
retrieving revision 1.99
retrieving revision 1.100
diff -u -d -r1.99 -r1.100
--- dns.c	6 Jun 2004 03:38:31 -0000	1.99
+++ dns.c	17 Jun 2004 18:13:09 -0000	1.100
@@ -71,6 +71,7 @@
 static int dnsworker_main(void *data);
 static int spawn_dnsworker(void);
 static void spawn_enough_dnsworkers(void);
+static void send_resolved_cell(connection_t *conn, uint8_t answer_type);
 
 /** Splay tree of cached_resolve objects. */
 static SPLAY_HEAD(cache_tree, cached_resolve) cache_root;
@@ -141,6 +142,34 @@
   }
 }
 
+static void send_resolved_cell(connection_t *conn, uint8_t answer_type)
+{
+  char buf[RELAY_PAYLOAD_SIZE];
+  int buflen;
+
+  buf[0] = answer_type;
+
+  switch (answer_type)
+    {
+    case RESOLVED_TYPE_IPV4:
+      buf[1] = 4;
+      set_uint32(buf+2, htonl(conn->addr));
+      buflen = 6;
+      break;
+    case RESOLVED_TYPE_ERROR_TRANSIENT:
+    case RESOLVED_TYPE_ERROR:
+      buf[1] = 24; /* length of "error resolving hostname" */
+      strcpy(buf+2, "error resolving hostname");
+      buflen = 26;
+      break;
+    default:
+      tor_assert(0);
+    }
+  connection_edge_send_command(conn, circuit_get_by_conn(conn),
+                               RELAY_COMMAND_RESOLVED, buf, buflen,
+                               conn->cpath_layer);
+}
+
 /** See if we have a cache entry for <b>exitconn</b>-\>address. if so,
  * if resolve valid, put it into <b>exitconn</b>-\>addr and return 1.
  * If resolve failed, return -1.
@@ -179,7 +208,8 @@
     switch(resolve->state) {
       case CACHE_STATE_PENDING:
         /* add us to the pending list */
-        pending_connection = tor_malloc(sizeof(struct pending_connection_t));
+        pending_connection = tor_malloc_zero(
+                                      sizeof(struct pending_connection_t));
         pending_connection->conn = exitconn;
         pending_connection->next = resolve->pending_connections;
         resolve->pending_connections = pending_connection;
@@ -191,8 +221,12 @@
         exitconn->addr = resolve->addr;
         log_fn(LOG_DEBUG,"Connection (fd %d) found cached answer for '%s'",
                exitconn->s, exitconn->address);
+        if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
+          send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
         return 1;
       case CACHE_STATE_FAILED:
+        if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
+          send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
         return -1;
     }
     tor_assert(0);
@@ -205,7 +239,7 @@
   resolve->address[MAX_ADDRESSLEN-1] = 0;
 
   /* add us to the pending list */
-  pending_connection = tor_malloc(sizeof(struct pending_connection_t));
+  pending_connection = tor_malloc_zero(sizeof(struct pending_connection_t));
   pending_connection->conn = exitconn;
   pending_connection->next = NULL;
   resolve->pending_connections = pending_connection;
@@ -240,6 +274,7 @@
   if(!dnsconn) {
     log_fn(LOG_WARN,"no idle dns workers. Failing.");
     dns_cancel_pending_resolve(exitconn->address);
+    send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT);
     return -1;
   }
 
@@ -453,28 +488,42 @@
     pend = resolve->pending_connections;
     assert_connection_ok(pend->conn,time(NULL));
     pend->conn->addr = resolve->addr;
+    pendconn = pend->conn; /* don't pass complex things to the
+                              connection_mark_for_close macro */
 
     if(resolve->state == CACHE_STATE_FAILED) {
-      pendconn = pend->conn; /* don't pass complex things to the
-                                connection_mark_for_close macro */
       /* prevent double-remove. */
       pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
       circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn);
-      connection_edge_end(pendconn, END_STREAM_REASON_MISC, pendconn->cpath_layer);
+      if (pendconn->purpose == EXIT_PURPOSE_CONNECT)
+        connection_edge_end(pendconn, END_STREAM_REASON_MISC, pendconn->cpath_layer);
+      else
+        send_resolved_cell(pendconn, RESOLVED_TYPE_ERROR);
       connection_free(pendconn);
     } else {
-      /* prevent double-remove. */
-      pend->conn->state = EXIT_CONN_STATE_CONNECTING;
+      if (pendconn->purpose == EXIT_PURPOSE_CONNECT) {
+        /* prevent double-remove. */
+        pend->conn->state = EXIT_CONN_STATE_CONNECTING;
 
-      circ = circuit_get_by_conn(pend->conn);
-      assert(circ);
-      /* unlink pend->conn from resolving_streams, */
-      circuit_detach_stream(circ, pend->conn);
-      /* and link it to n_streams */
-      pend->conn->next_stream = circ->n_streams;
-      circ->n_streams = pend->conn;
+        circ = circuit_get_by_conn(pend->conn);
+        tor_assert(circ);
+        /* unlink pend->conn from resolving_streams, */
+        circuit_detach_stream(circ, pend->conn);
+        /* and link it to n_streams */
+        pend->conn->next_stream = circ->n_streams;
+        circ->n_streams = pend->conn;
 
-      connection_exit_connect(pend->conn);
+        connection_exit_connect(pend->conn);
+      } else {
+        /* prevent double-remove.  This isn't really an accurate state,
+         * but it does the right thing. */
+        pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
+        send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4);
+        circ = circuit_get_by_conn(pendconn);
+        tor_assert(circ);
+        circuit_detach_stream(circ, pendconn);
+        connection_free(pendconn);
+      }
     }
     resolve->pending_connections = pend->next;
     tor_free(pend);

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.368
retrieving revision 1.369
diff -u -d -r1.368 -r1.369
--- or.h	16 Jun 2004 21:08:29 -0000	1.368
+++ or.h	17 Jun 2004 18:13:09 -0000	1.369
@@ -221,9 +221,11 @@
 #define AP_CONN_STATE_CIRCUIT_WAIT 7
 /** State for a SOCKS connection: sent BEGIN, waiting for CONNECTED. */
 #define AP_CONN_STATE_CONNECT_WAIT 8
+/** State for a SOCKS connection: send RESOLVE, waiting for RESOLVED. */
+#define AP_CONN_STATE_RESOLVE_WAIT 9
 /** State for a SOCKS connection: ready to send and receive. */
-#define AP_CONN_STATE_OPEN 9
-#define _AP_CONN_STATE_MAX 9
+#define AP_CONN_STATE_OPEN 10
+#define _AP_CONN_STATE_MAX 10
 
 #define _DIR_CONN_STATE_MIN 1
 /** State for connection to directory server: waiting for connect(). */
@@ -259,6 +261,11 @@
 #define DIR_PURPOSE_SERVER 7
 #define _DIR_PURPOSE_MAX 7
 
+#define _EXIT_PURPOSE_MIN 1
+#define EXIT_PURPOSE_CONNECT 1
+#define EXIT_PURPOSE_RESOLVE 2
+#define _EXIT_PURPOSE_MAX 2
+
 /** Circuit state: I'm the OP, still haven't done all my handshakes. */
 #define CIRCUIT_STATE_BUILDING 0
 /** Circuit state: Waiting to process the onionskin. */
@@ -371,6 +378,11 @@
 #define END_STREAM_REASON_TIMEOUT 7
 #define _MAX_END_STREAM_REASON 7
 
+#define RESOLVED_TYPE_IPV4 4
+#define RESOLVED_TYPE_IPV6 6
+#define RESOLVED_TYPE_ERROR_TRANSIENT 0xF0
+#define RESOLVED_TYPE_ERROR 0xF1
+
 /** Length of 'y' portion of 'y.onion' URL. */
 #define REND_SERVICE_ID_LEN 16
 
@@ -841,9 +853,12 @@
 /* XXX are these good enough defaults? */
 #define MAX_SOCKS_REPLY_LEN 1024
 #define MAX_SOCKS_ADDR_LEN 256
+#define SOCKS_COMMAND_CONNECT 0x01
+#define SOCKS_COMMAND_RESOLVE 0xF0
 /** State of a SOCKS request from a user to an OP */
 struct socks_request_t {
   char socks_version; /**< Which version of SOCKS did the client use? */
+  int command; /**< What has the user requested? One of CONNECT or RESOLVE. */
   int replylen; /**< Length of <b>reply</b>. */
   char reply[MAX_SOCKS_REPLY_LEN]; /**< Write an entry into this string if
                                     * we want to specify our own socks reply,
@@ -1048,13 +1063,18 @@
 int connection_edge_finished_connecting(connection_t *conn);
 
 int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ);
+int connection_ap_handshake_send_resolve(connection_t *ap_conn, circuit_t *circ);
 
 int connection_ap_make_bridge(char *address, uint16_t port);
-
 void connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
                                          int replylen, char success);
+void connection_ap_handshake_socks_resolved(connection_t *conn,
+                                            int answer_type,
+                                            int answer_len,
+                                            const char *answer);
 
 int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
+int connection_exit_begin_resolve(cell_t *cell, circuit_t *circ);
 void connection_exit_connect(connection_t *conn);
 int connection_edge_is_rendezvous_stream(connection_t *conn);
 int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit);

Index: relay.c
===================================================================
RCS file: /home/or/cvsroot/src/or/relay.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- relay.c	15 May 2004 07:21:25 -0000	1.3
+++ relay.c	17 Jun 2004 18:13:09 -0000	1.4
@@ -562,6 +562,26 @@
     }
     return 0;
   }
+  if(conn->type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_RESOLVED) {
+    if (conn->state != AP_CONN_STATE_RESOLVE_WAIT) {
+      log_fn(LOG_WARN,"Got a 'resolved' cell while not in state resolve_wait. Dropping.");
+      return 0;
+    }
+    tor_assert(conn->socks_request->command == SOCKS_COMMAND_RESOLVE);
+    if (rh->length < 2 || cell->payload[RELAY_HEADER_SIZE+1]+2>rh->length) {
+      log_fn(LOG_WARN, "Dropping malformed 'resolved' cell");
+      connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
+      connection_mark_for_close(conn);
+      return 0;
+    }
+    connection_ap_handshake_socks_resolved(conn,
+		   cell->payload[RELAY_HEADER_SIZE], /*answer_type*/
+		   cell->payload[RELAY_HEADER_SIZE+1], /*answer_len*/
+		   cell->payload+RELAY_HEADER_SIZE+2); /* answer */
+    conn->socks_request->has_finished = 1;
+    connection_mark_for_close(conn);
+    return 0;
+  }
 
   log_fn(LOG_WARN,"Got an unexpected relay command %d, in state %d (%s). Closing.",
          rh->command, conn->state, conn_state_to_string[conn->type][conn->state]);
@@ -744,6 +764,27 @@
       connection_start_reading(conn);
       connection_edge_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */
       return 0;
+    case RELAY_COMMAND_RESOLVE:
+      if (layer_hint) {
+        log_fn(LOG_WARN,"resolve request unsupported at AP; dropping.");
+	return 0;
+      } else if (conn) {
+	log_fn(LOG_WARN, "resolve request for known stream; dropping.");
+	return 0;
+      } else if (circ->purpose != CIRCUIT_PURPOSE_OR) {
+	log_fn(LOG_WARN, "resolve request on circ with purpose %d; dropping",
+	       circ->purpose);
+	return 0;
+      }
+      connection_exit_begin_resolve(cell, circ);
+      return 0;
+    case RELAY_COMMAND_RESOLVED:
+      if(conn) {
+        log_fn(LOG_WARN,"'resolved' unsupported while open. Closing circ.");
+        return -1;
+      }
+      log_fn(LOG_INFO,"'resolved' received, no conn attached anymore. Ignoring.");
+      return 0;
     case RELAY_COMMAND_ESTABLISH_INTRO:
     case RELAY_COMMAND_ESTABLISH_RENDEZVOUS:
     case RELAY_COMMAND_INTRODUCE1:



More information about the tor-commits mailing list