Commit a6bab569 authored by Roger Dingledine's avatar Roger Dingledine
Browse files

socks5 now works

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


svn:r536
parent 750b238a
Loading
Loading
Loading
Loading
+146 −74
Original line number Diff line number Diff line
@@ -317,66 +317,135 @@ int fetch_from_buf_http(buf_t *buf,
  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);
      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);
        *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;
      }

  port = socks4_info.destport;
  if(!port) {
    log_fn(LOG_WARNING,"Port is zero.");
      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 */

       *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.");
      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);
      *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(max_addrlen <= strlen(tmpbuf)) {
      log_fn(LOG_WARNING,"socks4 addr too long.");
        if(strlen(tmpbuf)+1 > max_addrlen) {
          log_fn(LOG_WARNING,"socks4 addr (%d bytes) too long.", strlen(tmpbuf));
          return -1;
        }
    log_fn(LOG_DEBUG,"Successfully read destip (%s)", tmpbuf);
    socks_prot = socks4;
        log_fn(LOG_DEBUG,"socks4: successfully read destip (%s)", tmpbuf);
        socks4_prot = socks4;
      }

      next = memchr(buf->buf+SOCKS4_NETWORK_LEN, 0, buf->datalen);
@@ -386,7 +455,7 @@ int fetch_from_buf_socks(buf_t *buf,
      }

      startaddr = next+1;
  if(socks_prot == socks4a) {
      if(socks4_prot == socks4a) {
        next = memchr(startaddr, 0, buf->buf+buf->datalen-startaddr);
        if(!next) {
          log_fn(LOG_DEBUG,"Destaddr not here yet.");
@@ -398,12 +467,15 @@ int fetch_from_buf_socks(buf_t *buf,
        }
      }
      log_fn(LOG_DEBUG,"Everything is here. Success.");
  *port_out = port; 
  strcpy(addr_out, socks_prot == socks4 ? tmpbuf : startaddr);
      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);
//  log_fn(LOG_DEBUG,"buf_datalen is now %d:'%s'",*buf_datalen,buf);
      return 1;

    default: /* version is not socks4 or socks5 */
      log_fn(LOG_WARNING,"Socks version %d not recognized.",*(buf->buf));
      return -1;
  }
}

/*
+52 −30
Original line number Diff line number Diff line
@@ -9,15 +9,11 @@ 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_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 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection
        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 @@ int connection_edge_finished_flushing(connection_t *conn) {
    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 @@ int connection_consider_sending_sendme(connection_t *conn, int edge_type) {
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 @@ static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *
  return 0;
}

static int connection_ap_handshake_socks_reply(connection_t *conn, char result) {
  char buf[SOCKS4_NETWORK_LEN];

  assert(conn);
static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
                                               int replylen, char success) {
  char buf[256];

  /* an inlined socks4_pack() */
  memset(buf,0,sizeof(buf));
  buf[1] = result; /* command */
  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 */

  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 */
    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;
  }
+4 −1
Original line number Diff line number Diff line
@@ -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 @@ struct connection_t {
                        */

/* 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(char *string, int string_len, buf_t *buf);
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);

+7 −7

File changed.

Contains only whitespace changes.