[or-cvs] Make AP connections wait for a circuit if none exists.

Nick Mathewson nickm at seul.org
Tue Nov 11 02:41:33 UTC 2003


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

Modified Files:
	buffers.c circuit.c connection.c connection_edge.c or.h 
Log Message:
Make AP connections wait for a circuit if none exists.
Also:
  - Refactor socks request into a separate struct
  - Add a separate 'waiting for circuit' state to AP connections
    between 'waiting for socks' and 'open'.

Arma: can you check out the XXX's I've added to connection_edge? I may
be mishandling some async and close logic.


Index: buffers.c
===================================================================
RCS file: /home/or/cvsroot/src/or/buffers.c,v
retrieving revision 1.51
retrieving revision 1.52
diff -u -d -r1.51 -r1.52
--- buffers.c	19 Oct 2003 01:10:38 -0000	1.51
+++ buffers.c	11 Nov 2003 02:41:31 -0000	1.52
@@ -395,19 +395,16 @@
  *   socks4a: "socksheader username\0 destaddr\0"
  *   socks5 phase one: "version #methods methods"
  *   socks5 phase two: "version command 0 addresstype..."
- * If it's a complete and valid handshake, and destaddr fits in addr_out,
- *   then pull the handshake off the buf, assign to addr_out and port_out,
- *   and return 1.
+ * If it's a complete and valid handshake, and destaddr fits in
+ *   MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf,
+ *   assign to *req, and return 1.
  * If it's invalid or too big, return -1.
  * Else it's not all there yet, leave buf alone and return 0.
  * If you want to specify the socks reply, write it into *reply
  *   and set *replylen, else leave *replylen alone.
  * If returning 0 or -1, *addr_out and *port_out are undefined.
  */
-int fetch_from_buf_socks(buf_t *buf, char *socks_version,
-                         char *reply, int *replylen,
-                         char *addr_out, int max_addrlen,
-                         uint16_t *port_out) {
+int fetch_from_buf_socks(buf_t *buf, socks_request_t *req) {
   unsigned char len;
   char *tmpbuf=NULL;
   uint32_t destip;
@@ -421,25 +418,25 @@
 
     case 5: /* socks5 */
 
-      if(*socks_version != 5) { /* we need to negotiate a method */
+      if(req->socks_version != 5) { /* we need to negotiate a method */
         unsigned char nummethods = (unsigned char)*(buf->mem+1);
-        assert(!*socks_version);
+        assert(!req->socks_version);
         log_fn(LOG_DEBUG,"socks5: learning offered methods");
         if(buf->datalen < 2+nummethods)
           return 0;
         if(!nummethods || !memchr(buf->mem+2, 0, nummethods)) {
           log_fn(LOG_WARN,"socks5: offered methods don't include 'no auth'. Rejecting.");
-          *replylen = 2; /* 2 bytes of response */
-          *reply = 5; /* socks5 reply */
-          *(reply+1) = 0xFF; /* reject all methods */
+          req->replylen = 2; /* 2 bytes of response */
+          req->reply[0] = 5; /* socks5 reply */
+          req->reply[1] = 0xFF; /* reject all methods */
           return -1;
         }          
         buf_remove_from_front(buf,2+nummethods);/* remove packet from buf */
 
-        *replylen = 2; /* 2 bytes of response */
-        *reply = 5; /* socks5 reply */
-        *(reply+1) = 0; /* choose the 'no auth' method */
-        *socks_version = 5; /* remember that we've already negotiated auth */
+        req->replylen = 2; /* 2 bytes of response */
+        req->reply[0] = 5; /* socks5 reply */
+        req->reply[1] = 0; /* choose the 'no auth' method */
+        req->socks_version = 5; /* remember that we've already negotiated auth */
         log_fn(LOG_DEBUG,"socks5: accepted method 0");
         return 0;
       }
@@ -459,13 +456,13 @@
           destip = ntohl(*(uint32_t*)(buf->mem+4));
           in.s_addr = htonl(destip);
           tmpbuf = inet_ntoa(in);
-          if(strlen(tmpbuf)+1 > max_addrlen) {
+          if(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
             log_fn(LOG_WARN,"socks5 IP takes %d bytes, which doesn't fit in %d",
-                   strlen(tmpbuf)+1,max_addrlen);
+                   strlen(tmpbuf)+1,MAX_SOCKS_ADDR_LEN);
             return -1;
           }
-          strcpy(addr_out,tmpbuf);
-          *port_out = ntohs(*(uint16_t*)(buf->mem+8));
+          strcpy(req->addr,tmpbuf);
+          req->port = ntohs(*(uint16_t*)(buf->mem+8));
           buf_remove_from_front(buf, 10);
           return 1;
         case 3: /* fqdn */
@@ -473,14 +470,14 @@
           len = (unsigned char)*(buf->mem+4);
           if(buf->datalen < 7+len) /* addr/port there? */
             return 0; /* not yet */
-          if(len+1 > max_addrlen) {
+          if(len+1 > MAX_SOCKS_ADDR_LEN) {
             log_fn(LOG_WARN,"socks5 hostname is %d bytes, which doesn't fit in %d",
-                   len+1,max_addrlen);
+                   len+1,MAX_SOCKS_ADDR_LEN);
             return -1;
           }
-          memcpy(addr_out,buf->mem+5,len);
-          addr_out[len] = 0;
-          *port_out = ntohs(*(uint16_t*)(buf->mem+5+len));
+          memcpy(req->addr,buf->mem+5,len);
+          req->addr[len] = 0;
+          req->port = ntohs(*(uint16_t*)(buf->mem+5+len));
           buf_remove_from_front(buf, 5+len+2);
           return 1;
         default: /* unsupported */
@@ -490,7 +487,7 @@
       assert(0);
     case 4: /* socks4 */
 
-       *socks_version = 4;
+      req->socks_version = 4;
       if(buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */
         return 0; /* not yet */
 
@@ -499,9 +496,9 @@
         return -1;
       }
 
-      *port_out = ntohs(*(uint16_t*)(buf->mem+2));
+      req->port = ntohs(*(uint16_t*)(buf->mem+2));
       destip = ntohl(*(uint32_t*)(buf->mem+4));
-      if(!*port_out || !destip) {
+      if(!req->port || !destip) {
         log_fn(LOG_WARN,"socks4: Port or DestIP is zero.");
         return -1;
       }
@@ -509,7 +506,7 @@
         log_fn(LOG_DEBUG,"socks4: destip not in form 0.0.0.x.");
         in.s_addr = htonl(destip);
         tmpbuf = inet_ntoa(in);
-        if(strlen(tmpbuf)+1 > max_addrlen) {
+        if(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
           log_fn(LOG_WARN,"socks4 addr (%d bytes) too long.", strlen(tmpbuf));
           return -1;
         }
@@ -530,13 +527,13 @@
           log_fn(LOG_DEBUG,"Destaddr not here yet.");
           return 0;
         }
-        if(max_addrlen <= next-startaddr) {
+        if(MAX_SOCKS_ADDR_LEN <= next-startaddr) {
           log_fn(LOG_WARN,"Destaddr too long.");
           return -1;
         }
       }
       log_fn(LOG_DEBUG,"Everything is here. Success.");
-      strcpy(addr_out, socks4_prot == socks4 ? tmpbuf : startaddr);
+      strcpy(req->addr, socks4_prot == socks4 ? tmpbuf : startaddr);
       buf_remove_from_front(buf, next-buf->mem+1); /* next points to the final \0 on inbuf */
       return 1;
 

Index: circuit.c
===================================================================
RCS file: /home/or/cvsroot/src/or/circuit.c,v
retrieving revision 1.81
retrieving revision 1.82
diff -u -d -r1.81 -r1.82
--- circuit.c	21 Oct 2003 09:48:17 -0000	1.81
+++ circuit.c	11 Nov 2003 02:41:31 -0000	1.82
@@ -755,6 +755,9 @@
     if(hop == circ->cpath) { /* done building the circuit. whew. */
       circ->state = CIRCUIT_STATE_OPEN;
       log_fn(LOG_INFO,"circuit built!");
+      /* Tell any AP connections that have been waiting for a new
+       * circuit that one is ready. */
+      connection_ap_attach_pending();
       return 0;
     }
 

Index: connection.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection.c,v
retrieving revision 1.127
retrieving revision 1.128
diff -u -d -r1.127 -r1.128
--- connection.c	10 Nov 2003 08:06:53 -0000	1.127
+++ connection.c	11 Nov 2003 02:41:31 -0000	1.128
@@ -85,6 +85,10 @@
     conn->inbuf = buf_new();
     conn->outbuf = buf_new();
   }
+  if (type == CONN_TYPE_AP) {
+    conn->socks_request = tor_malloc(sizeof(socks_request_t));
+    memset(conn->socks_request, 0, sizeof(socks_request_t));
+  }
 
   conn->timestamp_created = now;
   conn->timestamp_lastread = now;
@@ -115,6 +119,7 @@
   if (conn->identity_pkey)
     crypto_free_pk_env(conn->identity_pkey);
   tor_free(conn->nickname);
+  tor_free(conn->socks_request);
 
   if(conn->s >= 0) {
     log_fn(LOG_INFO,"closing fd %d.",conn->s);
@@ -760,6 +765,9 @@
       assert_cpath_layer_ok(conn->cpath_layer);
     /* XXX unchecked, package window, deliver window. */
   }
+  if (conn->type != CONN_TYPE_AP) {
+    assert(!conn->socks_request);
+  }
 
   switch(conn->type) 
     {
@@ -779,6 +787,12 @@
     case CONN_TYPE_AP:
       assert(conn->state >= _AP_CONN_STATE_MIN &&
              conn->state <= _AP_CONN_STATE_MAX);
+      if (conn->state == AP_CONN_STATE_SOCKS_WAIT ||
+          conn->state == AP_CONN_STATE_CIRCUIT_WAIT) {
+        assert(conn->socks_request);
+      } else {
+        assert(!conn->socks_request);
+      }
       break;
     case CONN_TYPE_DIR:
       assert(conn->state >= _DIR_CONN_STATE_MIN &&

Index: connection_edge.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection_edge.c,v
retrieving revision 1.45
retrieving revision 1.46
diff -u -d -r1.45 -r1.46
--- connection_edge.c	10 Nov 2003 08:06:53 -0000	1.45
+++ connection_edge.c	11 Nov 2003 02:41:31 -0000	1.46
@@ -7,8 +7,8 @@
 extern or_options_t options; /* command-line and config-file options */
 
 static int connection_ap_handshake_process_socks(connection_t *conn);
-static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ,
-                                              char *destaddr, uint16_t destport);
+static int connection_ap_handshake_attach_circuit(connection_t *conn);
+static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ);
 static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
                                                int replylen, char success);
 
@@ -465,6 +465,31 @@
   goto repeat_connection_edge_package_raw_inbuf;
 }
 
+/* Tell any APs that are waiting for a new circuit that one is available */
+void connection_ap_attach_pending(void)
+{
+  connection_t *conn;
+  int r;
+
+  while ((conn = connection_get_by_type_state(CONN_TYPE_AP,
+                                              AP_CONN_STATE_CIRCUIT_WAIT))) {
+    r = connection_ap_handshake_attach_circuit(conn);
+    if (r == 0) {
+      /* r==0: We're attached; do nothing. */
+    } else if (r>0) {
+      /* r>0: There was no circuit to attach to: stop the loop. */
+      break;
+    } else {
+      /* r<0: There was an error sending the begin cell; other pending  
+       *   AP connections may succeed.
+       */
+      /* XXX Is this right? How do we say that the connection failed?
+       * Should I close it?  mark it for close? -NM */
+      connection_ap_handshake_socks_reply(conn, NULL, 0, 0);
+    }
+  }
+}
+
 static void connection_edge_consider_sending_sendme(connection_t *conn) {
   circuit_t *circ;
  
@@ -491,23 +516,23 @@
 }
 
 static int connection_ap_handshake_process_socks(connection_t *conn) {
-  circuit_t *circ;
-  char destaddr[200]; /* XXX why 200? but not 256, because it won't fit in a cell */
-  char reply[256];
-  uint16_t destport;
-  int replylen=0;
+  socks_request_t *socks;
   int sockshere;
 
   assert(conn);
+  assert(conn->type == CONN_TYPE_AP);
+  assert(conn->state == AP_CONN_STATE_SOCKS_WAIT);
+  assert(conn->socks_request);
+  socks = conn->socks_request;
 
   log_fn(LOG_DEBUG,"entered.");
 
-  sockshere = fetch_from_buf_socks(conn->inbuf, &conn->socks_version, reply, &replylen,
-                                   destaddr, sizeof(destaddr), &destport);
+  sockshere = fetch_from_buf_socks(conn->inbuf, socks);
+  conn->socks_version = socks->socks_version;
   if(sockshere == -1 || sockshere == 0) {
-    if(replylen) { /* we should send reply back */
+    if(socks->replylen) { /* we should send reply back */
       log_fn(LOG_DEBUG,"reply is already set for us. Using it.");
-      connection_ap_handshake_socks_reply(conn, reply, replylen, 0);
+      connection_ap_handshake_socks_reply(conn, socks->reply, socks->replylen, 0);
     } else if(sockshere == -1) { /* send normal reject */
       log_fn(LOG_WARN,"Fetching socks handshake failed. Closing.");
       connection_ap_handshake_socks_reply(conn, NULL, 0, 0);
@@ -517,13 +542,35 @@
     return sockshere;
   } /* else socks handshake is done, continue processing */
 
+  conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+  if (connection_ap_handshake_attach_circuit(conn)<0)
+    return -1;
+  return 0;
+}
+
+/* Try to find a live circuit.  If we don't find one, tell 'conn' to
+ * stop reading and return 1.  Otherwise, associate the CONN_TYPE_AP
+ * connection 'conn' with the newest live circuit, and start sending a
+ * BEGIN cell down the circuit.  Returns 0 on success, and -1 on
+ * error.
+ */
+static int connection_ap_handshake_attach_circuit(connection_t *conn) {
+  circuit_t *circ;
+  
+  assert(conn);
+  assert(conn->type == CONN_TYPE_AP);
+  assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+  assert(conn->socks_request);
+  
   /* find the circuit that we should use, if there is one. */
   circ = circuit_get_newest_open();
 
   if(!circ) {
-    log_fn(LOG_INFO,"No circuit ready. Closing.");
-    return -1;
+    log_fn(LOG_INFO,"No circuit ready for edge connection; delaying.");
+    connection_stop_reading(conn); /* XXX Is this correct? -NM */
+    return 1;
   }
+  connection_start_reading(conn); /* XXX Is this correct? -NM */
 
   circ->dirty = 1;
 
@@ -536,7 +583,7 @@
   assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
   conn->cpath_layer = circ->cpath->prev;
 
-  if(connection_ap_handshake_send_begin(conn, circ, destaddr, destport) < 0) {
+  if(connection_ap_handshake_send_begin(conn, circ) < 0) {
     circuit_close(circ);
     return -1;
   }
@@ -545,11 +592,15 @@
 }
 
 /* deliver the destaddr:destport in a relay cell */
-static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ,
-                                              char *destaddr, uint16_t destport) {
+static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ)
+{
   char payload[CELL_PAYLOAD_SIZE];
   int payload_len;
 
+  assert(ap_conn->type == CONN_TYPE_AP);
+  assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+  assert(ap_conn->socks_request);
+
   if(crypto_pseudo_rand(STREAM_ID_SIZE, ap_conn->stream_id) < 0)
     return -1;
   /* FIXME check for collisions */
@@ -557,7 +608,7 @@
   memcpy(payload, ap_conn->stream_id, STREAM_ID_SIZE);
   payload_len = STREAM_ID_SIZE + 1 +
     snprintf(payload+STREAM_ID_SIZE,CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE-STREAM_ID_SIZE,
-             "%s:%d", destaddr, destport);
+             "%s:%d", ap_conn->socks_request->addr, ap_conn->socks_request->port);
 
   log_fn(LOG_DEBUG,"Sending relay cell to begin stream %d.",*(int *)ap_conn->stream_id);
 
@@ -568,6 +619,8 @@
   ap_conn->package_window = STREAMWINDOW_START;
   ap_conn->deliver_window = STREAMWINDOW_START;
   ap_conn->state = AP_CONN_STATE_OPEN;
+  tor_free(ap_conn->socks_request);
+  ap_conn->socks_request = NULL;
   log_fn(LOG_INFO,"Address/port sent, ap socket %d, n_aci %d",ap_conn->s,circ->n_aci);
   return 0;
 }

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.175
retrieving revision 1.176
diff -u -d -r1.175 -r1.176
--- or.h	10 Nov 2003 08:06:53 -0000	1.175
+++ or.h	11 Nov 2003 02:41:31 -0000	1.176
@@ -160,7 +160,7 @@
 /* the AP state values must be disjoint from the EXIT state values */
 #define _AP_CONN_STATE_MIN 4
 #define AP_CONN_STATE_SOCKS_WAIT 4
-#define AP_CONN_STATE_OR_WAIT 5
+#define AP_CONN_STATE_CIRCUIT_WAIT 5
 #define AP_CONN_STATE_OPEN 6
 #define _AP_CONN_STATE_MAX 6
 
@@ -254,6 +254,7 @@
 #define ZERO_STREAM "\0\0\0\0\0\0\0\0"
 
 typedef struct buf_t buf_t;
+typedef struct socks_request_t socks_request_t;
 
 struct connection_t { 
 
@@ -304,7 +305,6 @@
                         */
 
 /* Used only by edge connections: */
-  char socks_version;
   char stream_id[STREAM_ID_SIZE];
   struct connection_t *next_stream; /* points to the next stream at this edge, if any */
   struct crypt_path_t *cpath_layer; /* a pointer to which node in the circ this conn exits at */
@@ -315,6 +315,10 @@
   int done_receiving;
   char has_sent_end; /* for debugging: set once we've set the stream end,
                         and check in circuit_about_to_close_connection() */
+  
+  /* Used only by AP connections */
+  char socks_version;
+  socks_request_t *socks_request;
 };
 
 typedef struct connection_t connection_t;
@@ -455,6 +459,17 @@
   int loglevel;
 } or_options_t;
 
+#define MAX_SOCKS_REPLY_LEN 256
+/* Not 256; addresses must fit in a begin cell. */
+#define MAX_SOCKS_ADDR_LEN 200
+struct socks_request_t {
+  char socks_version;
+  int replylen;
+  char reply[MAX_SOCKS_REPLY_LEN];
+  char addr[MAX_SOCKS_ADDR_LEN];
+  uint16_t port;
+};
+
 /* all the function prototypes go here */
 
 /********************************* buffers.c ***************************/
@@ -480,10 +495,7 @@
 int fetch_from_buf_http(buf_t *buf,
                         char *headers_out, int max_headerlen,
                         char *body_out, int max_bodylen);
-int fetch_from_buf_socks(buf_t *buf, char *socks_version,
-                         char *reply, int *replylen,
-                         char *addr_out, int max_addrlen,
-                         uint16_t *port_out);
+int fetch_from_buf_socks(buf_t *buf, socks_request_t *req);
 
 /********************************* circuit.c ***************************/
 
@@ -602,6 +614,8 @@
 int connection_edge_package_raw_inbuf(connection_t *conn);
 
 void connection_exit_connect(connection_t *conn);
+
+void connection_ap_attach_pending(void);
 
 extern uint64_t stats_n_data_cells_packaged;
 extern uint64_t stats_n_data_bytes_packaged;



More information about the tor-commits mailing list