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

[or-cvs] socks5 now works



Update of /home/or/cvsroot/src/or
In directory moria.mit.edu:/home2/arma/work/onion/cvs/src/or

Modified Files:
	buffers.c connection_edge.c main.c or.h 
Log Message:
socks5 now works

(or at least, we can talk to mozilla.)


Index: buffers.c
===================================================================
RCS file: /home/or/cvsroot/src/or/buffers.c,v
retrieving revision 1.41
retrieving revision 1.42
diff -u -d -r1.41 -r1.42
--- buffers.c	27 Sep 2003 07:33:07 -0000	1.41
+++ buffers.c	4 Oct 2003 01:37:00 -0000	1.42
@@ -317,93 +317,165 @@
   return 1;
 }
 
-/* There is a (possibly incomplete) socks handshake on *buf, of the
- * forms
- *   socks4: "socksheader || username\0".
- *   socks4a: "socksheader || username\0 || destaddr\0".
+/* There is a (possibly incomplete) socks handshake on buf, of one
+ * of the forms
+ *   socks4: "socksheader username\0"
+ *   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 invalid or too big, return -1.
- * Else it's not all there yet, change nothing and return 0.
+ * 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,
+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) {
-  socks4_t socks4_info;
+  unsigned char len;
   char *tmpbuf=NULL;
-  uint16_t port;
-  enum {socks4, socks4a } socks_prot = socks4a;
+  uint32_t destip;
+  enum {socks4, socks4a} socks4_prot = socks4a;
   char *next, *startaddr;
+  struct in_addr in;
 
-  if(buf->datalen < sizeof(socks4_t)) /* basic info available? */
-    return 0; /* not yet */
+  if(buf->datalen < 2) /* version and another byte */
+    return 0;
+  switch(*(buf->buf)) { /* which version of socks? */
 
-  /* an inlined socks4_unpack() */
-  socks4_info.version = (unsigned char) *(buf->buf);
-  socks4_info.command = (unsigned char) *(buf->buf+1);
-  socks4_info.destport = ntohs(*(uint16_t*)(buf->buf+2));
-  socks4_info.destip = ntohl(*(uint32_t*)(buf->buf+4));
+    case 5: /* socks5 */
 
-  if(socks4_info.version != 4) {
-    log_fn(LOG_WARNING,"Unrecognized version %d.",socks4_info.version);
-    return -1;
-  }
+      if(*socks_version != 5) { /* we need to negotiate a method */
+        unsigned char nummethods = (unsigned char)*(buf->buf+1);
+        assert(!*socks_version);
+        log_fn(LOG_DEBUG,"socks5: learning offered methods");
+        if(buf->datalen < 2+nummethods)
+          return 0;
+        if(!nummethods || !memchr(buf->buf+2, 0, nummethods)) {
+          log_fn(LOG_WARNING,"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 */
+          return -1;
+        }          
+        buf->datalen -= (2+nummethods); /* remove packet from buf */
+        memmove(buf->buf, buf->buf + 2 + nummethods, buf->datalen);
 
-  if(socks4_info.command != 1) { /* not a connect? we don't support it. */
-    log_fn(LOG_WARNING,"command %d not '1'.",socks4_info.command);
-    return -1;
-  }
+        *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 */
+        log_fn(LOG_DEBUG,"socks5: accepted method 0");
+        return 0;
+      }
+      /* we know the method; read in the request */
+      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->buf+1) != 1) { /* not a connect? we don't support it. */
+        log_fn(LOG_WARNING,"socks5: command %d not '1'.",*(buf->buf+1));
+        return -1;
+      }
+      switch(*(buf->buf+3)) { /* address type */
+        case 1: /* IPv4 address */
+          log_fn(LOG_DEBUG,"socks5: ipv4 address type");
+          if(buf->datalen < 10) /* ip/port there? */
+            return 0; /* not yet */
+          destip = ntohl(*(uint32_t*)(buf->buf+4));
+          in.s_addr = htonl(destip);
+          tmpbuf = inet_ntoa(in);
+          if(strlen(tmpbuf)+1 > max_addrlen) {
+            log_fn(LOG_WARNING,"socks5 IP takes %d bytes, which doesn't fit in %d",
+                   strlen(tmpbuf)+1,max_addrlen);
+            return -1;
+          }
+          strcpy(addr_out,tmpbuf);
+          *port_out = ntohs(*(uint16_t*)(buf->buf+8));
+          buf->datalen -= 10;
+          memmove(buf->buf, buf->buf+10, buf->datalen);
+          return 1;
+        case 3: /* fqdn */
+          log_fn(LOG_DEBUG,"socks5: fqdn address type");
+          len = (unsigned char)*(buf->buf+4);
+          if(buf->datalen < 7+len) /* addr/port there? */
+            return 0; /* not yet */
+          if(len+1 > max_addrlen) {
+            log_fn(LOG_WARNING,"socks5 hostname is %d bytes, which doesn't fit in %d",
+                   len+1,max_addrlen);
+            return -1;
+          }
+          memcpy(addr_out,buf->buf+5,len);
+          addr_out[len] = 0;
+          *port_out = ntohs(*(uint16_t*)(buf->buf+5+len));
+          buf->datalen -= (5+len+2);
+          memmove(buf->buf, buf->buf+(5+len+2), buf->datalen);
+          return 1;
+        default: /* unsupported */
+          log_fn(LOG_WARNING,"socks5: unsupported address type %d",*(buf->buf+3));
+          return -1;
+      }
+      assert(0);
+    case 4: /* socks4 */
 
-  port = socks4_info.destport;
-  if(!port) {
-    log_fn(LOG_WARNING,"Port is zero.");
-    return -1;
-  }
+       *socks_version = 4;
+      if(buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */
+        return 0; /* not yet */
 
-  if(!socks4_info.destip) {
-    log_fn(LOG_WARNING,"DestIP is zero.");
-    return -1;
-  }
+      if(*(buf->buf+1) != 1) { /* not a connect? we don't support it. */
+        log_fn(LOG_WARNING,"socks4: command %d not '1'.",*(buf->buf+1));
+        return -1;
+      }
 
-  if(socks4_info.destip >> 8) {
-    struct in_addr in;
-    log_fn(LOG_DEBUG,"destip not in form 0.0.0.x.");
-    in.s_addr = htonl(socks4_info.destip);
-    tmpbuf = inet_ntoa(in);
-    if(max_addrlen <= strlen(tmpbuf)) {
-      log_fn(LOG_WARNING,"socks4 addr too long.");
-      return -1;
-    }
-    log_fn(LOG_DEBUG,"Successfully read destip (%s)", tmpbuf);
-    socks_prot = socks4;
-  }
+      *port_out = ntohs(*(uint16_t*)(buf->buf+2));
+      destip = ntohl(*(uint32_t*)(buf->buf+4));
+      if(!*port_out || !destip) {
+        log_fn(LOG_WARNING,"socks4: Port or DestIP is zero.");
+        return -1;
+      }
+      if(destip >> 8) {
+        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) {
+          log_fn(LOG_WARNING,"socks4 addr (%d bytes) too long.", strlen(tmpbuf));
+          return -1;
+        }
+        log_fn(LOG_DEBUG,"socks4: successfully read destip (%s)", tmpbuf);
+        socks4_prot = socks4;
+      }
 
-  next = memchr(buf->buf+SOCKS4_NETWORK_LEN, 0, buf->datalen);
-  if(!next) {
-    log_fn(LOG_DEBUG,"Username not here yet.");
-    return 0;
-  }
+      next = memchr(buf->buf+SOCKS4_NETWORK_LEN, 0, buf->datalen);
+      if(!next) {
+        log_fn(LOG_DEBUG,"Username not here yet.");
+        return 0;
+      }
 
-  startaddr = next+1;
-  if(socks_prot == socks4a) {
-    next = memchr(startaddr, 0, buf->buf+buf->datalen-startaddr);
-    if(!next) {
-      log_fn(LOG_DEBUG,"Destaddr not here yet.");
-      return 0;
-    }
-    if(max_addrlen <= next-startaddr) {
-      log_fn(LOG_WARNING,"Destaddr too long.");
+      startaddr = next+1;
+      if(socks4_prot == socks4a) {
+        next = memchr(startaddr, 0, buf->buf+buf->datalen-startaddr);
+        if(!next) {
+          log_fn(LOG_DEBUG,"Destaddr not here yet.");
+          return 0;
+        }
+        if(max_addrlen <= next-startaddr) {
+          log_fn(LOG_WARNING,"Destaddr too long.");
+          return -1;
+        }
+      }
+      log_fn(LOG_DEBUG,"Everything is here. Success.");
+      strcpy(addr_out, socks4_prot == socks4 ? tmpbuf : startaddr);
+      buf->datalen -= (next-buf->buf+1); /* next points to the final \0 on inbuf */
+      memmove(buf->buf, next+1, buf->datalen);
+      return 1;
+
+    default: /* version is not socks4 or socks5 */
+      log_fn(LOG_WARNING,"Socks version %d not recognized.",*(buf->buf));
       return -1;
-    }
   }
-  log_fn(LOG_DEBUG,"Everything is here. Success.");
-  *port_out = port; 
-  strcpy(addr_out, socks_prot == socks4 ? tmpbuf : startaddr);
-  buf->datalen -= (next-buf->buf+1); /* next points to the final \0 on inbuf */
-  memmove(buf->buf, next+1, buf->datalen);
-//  log_fn(LOG_DEBUG,"buf_datalen is now %d:'%s'",*buf_datalen,buf);
-  return 1;
 }
 
 /*

Index: connection_edge.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection_edge.c,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -d -r1.29 -r1.30
--- connection_edge.c	2 Oct 2003 20:00:38 -0000	1.29
+++ connection_edge.c	4 Oct 2003 01:37:01 -0000	1.30
@@ -9,15 +9,11 @@
 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_socks_reply(connection_t *conn, char result);
+static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
+                                               int replylen, char success);
 
 static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
 
-#define SOCKS4_REQUEST_GRANTED          90
-#define SOCKS4_REQUEST_REJECT           91
-#define SOCKS4_REQUEST_IDENT_FAILED     92
-#define SOCKS4_REQUEST_IDENT_CONFLICT   93
-
 int connection_edge_process_inbuf(connection_t *conn) {
 
   assert(conn);
@@ -242,7 +238,7 @@
         break;
       }
       log_fn(LOG_INFO,"Connected! Notifying application.");
-      if(connection_ap_handshake_socks_reply(conn, SOCKS4_REQUEST_GRANTED) < 0) {
+      if(connection_ap_handshake_socks_reply(conn, NULL, 0, 1) < 0) {
 /*ENDCLOSE*/    conn->marked_for_close = 1;
       }
       break;
@@ -307,6 +303,9 @@
     case EXIT_CONN_STATE_OPEN:
       connection_stop_writing(conn);
       return connection_consider_sending_sendme(conn, conn->type);
+    case AP_CONN_STATE_SOCKS_WAIT:
+      connection_stop_writing(conn);
+      return 0;
     default:
       log_fn(LOG_WARNING,"BUG: called in unexpected state.");
       return -1;
@@ -445,23 +444,29 @@
 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;
+  int sockshere;
 
   assert(conn);
 
   log_fn(LOG_DEBUG,"entered.");
 
-  switch(fetch_from_buf_socks(conn->inbuf,
-                              destaddr, sizeof(destaddr), &destport)) {
-    case -1:
+  sockshere = fetch_from_buf_socks(conn->inbuf, &conn->socks_version, reply, &replylen,
+                                   destaddr, sizeof(destaddr), &destport);
+  if(sockshere == -1 || sockshere == 0) {
+    if(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);
+    } else if(sockshere == -1) { /* send normal reject */
       log_fn(LOG_WARNING,"Fetching socks handshake failed. Closing.");
-      connection_ap_handshake_socks_reply(conn, SOCKS4_REQUEST_REJECT);
-      return -1;
-    case 0:
-      log_fn(LOG_DEBUG,"Fetching socks handshake, not all here yet. Ignoring.");
-      return 0;
-    /* case 1, fall through */
-  }
+      connection_ap_handshake_socks_reply(conn, NULL, 0, 0);
+    } else {
+      log_fn(LOG_DEBUG,"socks handshake not all here yet.");
+    }
+    return sockshere;
+  } /* else socks handshake is done, continue processing */
 
   /* find the circuit that we should use, if there is one. */
   circ = circuit_get_newest_open();
@@ -521,26 +526,43 @@
   return 0;
 }
 
-static int connection_ap_handshake_socks_reply(connection_t *conn, char result) {
-  char buf[SOCKS4_NETWORK_LEN];
-
-  assert(conn);
-
-  /* an inlined socks4_pack() */
-  memset(buf,0,sizeof(buf));
-  buf[1] = result; /* command */
-  /* leave version, destport, destip zero */
+static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
+                                               int replylen, char success) {
+  char buf[256];
 
-  if(connection_write_to_buf(buf, sizeof(buf), conn) < 0)
-    return -1;
-  return connection_flush_buf(conn); /* try to flush it, in case we're about to close the conn */
+  if(replylen) { /* we already have a reply in mind */
+    connection_write_to_buf(reply, replylen, conn);
+    return connection_flush_buf(conn); /* try to flush it */
+  }
+  if(conn->socks_version == 4) {
+    memset(buf,0,SOCKS4_NETWORK_LEN);
+#define SOCKS4_GRANTED          90
+#define SOCKS4_REJECT           91
+    buf[1] = (success ? SOCKS4_GRANTED : SOCKS4_REJECT);
+    /* leave version, destport, destip zero */
+    connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, conn);
+    return connection_flush_buf(conn); /* try to flush it */
+  }
+  if(conn->socks_version == 5) {
+    buf[0] = 5; /* version 5 */
+#define SOCKS5_SUCCESS          0
+#define SOCKS5_GENERIC_ERROR    1
+    buf[1] = success ? SOCKS5_SUCCESS : SOCKS5_GENERIC_ERROR;
+    buf[2] = 0;
+    buf[3] = 1; /* ipv4 addr */
+    memset(buf+4,0,6); /* XXX set external addr/port to 0, see what breaks */
+    connection_write_to_buf(buf,10,conn);
+    return connection_flush_buf(conn); /* try to flush it */
+  }
+  assert(0);
 }
 
 /*ENDCLOSE*/ static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) {
   connection_t *n_stream;
   char *colon;
 
-  if(!memchr(cell->payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE,0,cell->length-RELAY_HEADER_SIZE-STREAM_ID_SIZE)) {
+  if(!memchr(cell->payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE,0,
+             cell->length-RELAY_HEADER_SIZE-STREAM_ID_SIZE)) {
     log_fn(LOG_WARNING,"relay begin cell has no \\0. Dropping.");
     return 0;
   }

Index: main.c
===================================================================
RCS file: /home/or/cvsroot/src/or/main.c,v
retrieving revision 1.122
retrieving revision 1.123
diff -u -d -r1.122 -r1.123
--- main.c	2 Oct 2003 20:00:38 -0000	1.122
+++ main.c	4 Oct 2003 01:37:01 -0000	1.123
@@ -678,13 +678,13 @@
     circuit_dump_by_conn(conn); /* dump info about all the circuits using this conn */
     printf("\n");
   }
-  printf("Cells processed: % 10lud padding\n"
-         "                 % 10lud create\n"
-         "                 % 10lud created\n"
-         "                 % 10lud relay\n"
-         "                        (% 10lud relayed)\n"
-         "                        (% 10lud delivered)\n"
-         "                 % 10lud destroy\n",
+  printf("Cells processed: %10lud padding\n"
+         "                 %10lud create\n"
+         "                 %10lud created\n"
+         "                 %10lud relay\n"
+         "                        (%10lud relayed)\n"
+         "                        (%10lud delivered)\n"
+         "                 %10lud destroy\n",
          stats_n_padding_cells_processed,
          stats_n_create_cells_processed,
          stats_n_created_cells_processed,

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.154
retrieving revision 1.155
diff -u -d -r1.154 -r1.155
--- or.h	2 Oct 2003 20:00:38 -0000	1.154
+++ or.h	4 Oct 2003 01:37:01 -0000	1.155
@@ -84,6 +84,7 @@
 
 #ifdef MS_WINDOWS
 #include <io.h>
+#include <process.h>
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #define snprintf _snprintf
@@ -301,6 +302,7 @@
                         */
 
 /* 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 */
@@ -465,7 +467,8 @@
 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,
+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);