mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
socks5 now works
(or at least, we can talk to mozilla.) svn:r536
This commit is contained in:
parent
750b238aea
commit
a6bab569ab
160
src/or/buffers.c
160
src/or/buffers.c
@ -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? */
|
||||
if(buf->datalen < 2) /* version and another byte */
|
||||
return 0;
|
||||
switch(*(buf->buf)) { /* which version of socks? */
|
||||
|
||||
case 5: /* socks5 */
|
||||
|
||||
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);
|
||||
|
||||
*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 */
|
||||
|
||||
*socks_version = 4;
|
||||
if(buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */
|
||||
return 0; /* not yet */
|
||||
|
||||
/* 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));
|
||||
|
||||
if(socks4_info.version != 4) {
|
||||
log_fn(LOG_WARNING,"Unrecognized version %d.",socks4_info.version);
|
||||
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.command != 1) { /* not a connect? we don't support it. */
|
||||
log_fn(LOG_WARNING,"command %d not '1'.",socks4_info.command);
|
||||
*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;
|
||||
}
|
||||
|
||||
port = socks4_info.destport;
|
||||
if(!port) {
|
||||
log_fn(LOG_WARNING,"Port is zero.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!socks4_info.destip) {
|
||||
log_fn(LOG_WARNING,"DestIP is zero.");
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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];
|
||||
static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
|
||||
int replylen, char success) {
|
||||
char buf[256];
|
||||
|
||||
assert(conn);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
@ -678,13 +678,13 @@ static void dumpstats(void) { /* dump stats to stdout */
|
||||
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,
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user