[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [obfsproxy/master] Added support for more SOCKS5 replies.
commit 891596713a8868b1d05b3fbfefc8b8674feb8b7a
Author: George Kadianakis <desnacked@xxxxxxxxx>
Date: Tue May 24 05:18:04 2011 +0200
Added support for more SOCKS5 replies.
---
src/network.c | 4 +-
src/socks.c | 121 +++++++++++++++++++++++++++++++++------------
src/socks.h | 12 +++-
src/test/unittest_socks.c | 37 ++++++++++++--
4 files changed, 134 insertions(+), 40 deletions(-)
diff --git a/src/network.c b/src/network.c
index 7b017b5..835dff1 100644
--- a/src/network.c
+++ b/src/network.c
@@ -358,7 +358,7 @@ output_event_cb(struct bufferevent *bev, short what, void *arg)
bufferevent_enable(conn->input, EV_WRITE);
bufferevent_disable(conn->input, EV_READ);
socks_send_reply(conn->socks_state, bufferevent_get_output(conn->input),
- SOCKS_FAILED);
+ EVUTIL_SOCKET_ERROR());
bufferevent_setcb(conn->input, NULL,
close_conn_on_flush, output_event_cb, conn);
return;
@@ -400,7 +400,7 @@ output_event_cb(struct bufferevent *bev, short what, void *arg)
socks_state_set_address(conn->socks_state, sa);
}
socks_send_reply(conn->socks_state, bufferevent_get_output(conn->input),
- SOCKS_SUCCESS);
+ 0);
/* we sent a socks reply. We can finally move over to being a regular
input bufferevent. */
socks_state_free(conn->socks_state);
diff --git a/src/socks.c b/src/socks.c
index 5d73965..a388363 100644
--- a/src/socks.c
+++ b/src/socks.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+#include <errno.h>
#define SOCKS_PRIVATE
#include "socks.h"
@@ -34,7 +35,7 @@
"Method Negotiation Packet" is handled by: socks5_handle_negotiation()
"Method Negotiation Reply" is done by: socks5_do_negotiation()
"Client request" is handled by: socks5_handle_request()
- "Server reply" is done by: socks5_send_reply
+ "Server reply" is done by: socks5_send_reply()
*/
static int socks5_do_negotiation(struct evbuffer *dest,
@@ -59,20 +60,53 @@ socks_state_free(socks_state_t *s)
memset(s,0x0b, sizeof(socks_state_t));
free(s);
}
+
/**
- This function handles connection requests by authenticated SOCKS
- clients.
- Considering a request packet from 'source', it evaluates it and pushes
- the appropriate reply to 'dest'.
- If the request was correct and can be fulfilled, it connects 'output'
- to the location the client specified to actually set up the proxying.
-
- XXX: You will notice some "sizeof(req)-1" that initially make no
- sense, but because of handle_socks() removing the version byte,
- there are only 3 bytes (==sizeof(req)-1) between the start
- of 'source' and addrlen. I don't know if replacing it with plain "3"
- would make more sense.
+ This function receives an errno(3) 'error' and returns the reply
+ code that should be sent to the SOCKS client.
+ If 'error' is 0 it means that no errors were encountered, and
+ SOCKS{4,5}_SUCCESS is returned.
+*/
+static int
+socks_errno_to_reply(socks_state_t *state, int error)
+{
+ if (state->version == SOCKS4_VERSION) {
+ if (!error)
+ return SOCKS4_SUCCESS;
+ else
+ return SOCKS4_FAILED;
+ } else if (state->version == SOCKS5_VERSION) {
+ if (!error)
+ return SOCKS5_SUCCESS;
+ else {
+ switch (error) {
+ case ENETUNREACH:
+ return SOCKS5_FAILED_NETUNREACH;
+ case EHOSTUNREACH:
+ return SOCKS5_FAILED_HOSTUNREACH;
+ case ECONNREFUSED:
+ return SOCKS5_FAILED_REFUSED;
+ default:
+ return SOCKS5_FAILED_GENERAL;
+ }
+ }
+ }
+ return -1;
+}
+/**
+ Takes a command request from 'source', it evaluates it and if it's
+ legit it parses into 'parsereq'.
+
+ It returns '1' if everything went fine.
+ It returns '0' if we need more data from the client.
+ It returns '-1' if we didn't like something.
+ It returns '-2' if the client asked for something else than CONNECT.
+ If that's the case we should send a reply back to the client
+ telling him that we don't support it.
+
+ Disclaimer: This is just a temporary documentation.
+
Client Request (Client -> Server)
*/
int
@@ -100,10 +134,12 @@ socks5_handle_request(struct evbuffer *source, struct parsereq *parsereq)
/* p[0] = Version
p[1] = Command field
p[2] = Reserved field */
- if (p[0] != SOCKS5_VERSION || p[1] != SOCKS5_CMD_CONNECT || p[2] != 0x00) {
- printf("socks: Only CONNECT supported. Sowwy!\n");
+ if (p[0] != SOCKS5_VERSION || p[2] != 0x00) {
+ printf("socks: Corrupted packet. Discarding.\n");
goto err;
}
+ if (p[1] != SOCKS5_CMD_CONNECT)
+ return -2; /* We must send reply to the client. */
unsigned int addrlen,af,extralen=0;
/* p[3] is Address type field */
@@ -183,7 +219,6 @@ int
socks5_send_reply(struct evbuffer *reply_dest, socks_state_t *state,
int status)
{
- /* This is the buffer that contains our reply to the client. */
uchar p[4];
uchar addr[16];
const char *extra = NULL;
@@ -193,19 +228,31 @@ socks5_send_reply(struct evbuffer *reply_dest, socks_state_t *state,
Either way, we should send something back to the client */
p[0] = SOCKS5_VERSION; /* Version field */
/* Reply field */
- p[1] = (status == SOCKS_SUCCESS) ? SOCKS5_SUCCESS : SOCKS5_FAILED;
+ p[1] = status;
p[2] = 0; /* Reserved */
- if (state->parsereq.af == AF_UNSPEC) {
- addrlen = 1;
- addr[0] = strlen(state->parsereq.addr);
- extra = state->parsereq.addr;
- p[3] = SOCKS5_ATYP_FQDN;
+
+ /* If status is SOCKS5_FAILED_UNSUPPORTED, it means that we failed
+ before populating state->parsereq. We will just feel the rest
+ of the reply packet with zeroes and ship it off.
+ */
+ if (status == SOCKS5_FAILED_UNSUPPORTED) {
+ addrlen = 4;
+ p[3] = SOCKS5_ATYP_IPV4;
+ memset(addr,'\x00',4);
+ port = 0;
} else {
- addrlen = (state->parsereq.af == AF_INET) ? 4 : 16;
- p[3] = (state->parsereq.af == AF_INET) ? SOCKS5_ATYP_IPV4 : SOCKS5_ATYP_IPV6;
- evutil_inet_pton(state->parsereq.af, state->parsereq.addr, addr);
+ if (state->parsereq.af == AF_UNSPEC) {
+ addrlen = 1;
+ addr[0] = strlen(state->parsereq.addr);
+ extra = state->parsereq.addr;
+ p[3] = SOCKS5_ATYP_FQDN;
+ } else {
+ addrlen = (state->parsereq.af == AF_INET) ? 4 : 16;
+ p[3] = (state->parsereq.af == AF_INET) ? SOCKS5_ATYP_IPV4 : SOCKS5_ATYP_IPV6;
+ evutil_inet_pton(state->parsereq.af, state->parsereq.addr, addr);
+ }
+ port = htons(state->parsereq.port);
}
- port = htons(state->parsereq.port);
evbuffer_add(reply_dest, p, 4);
evbuffer_add(reply_dest, addr, addrlen);
@@ -377,7 +424,7 @@ socks4_send_reply(struct evbuffer *dest, socks_state_t *state, int status)
/* Nul byte */
msg[0] = 0;
/* convert to socks4 status */
- msg[1] = (status == SOCKS_SUCCESS) ? SOCKS4_SUCCESS : SOCKS4_FAILED;
+ msg[1] = status;
memcpy(msg+2, &portnum, 2);
/* ASN: What should we do here in the case of an FQDN request? */
memcpy(msg+4, &in.s_addr, 4);
@@ -450,7 +497,11 @@ handle_socks(struct evbuffer *source, struct evbuffer *dest,
r = socks5_handle_request(source,&socks_state->parsereq);
if (r == -1)
goto broken;
- else if (r == 1)
+ else if (r == -2) {
+ socks5_send_reply(dest,socks_state,
+ SOCKS5_FAILED_UNSUPPORTED);
+ goto broken;
+ } else if (r == 1)
socks_state->state = ST_HAVE_ADDR;
return r;
}
@@ -513,12 +564,20 @@ socks_state_set_address(socks_state_t *state, const struct sockaddr *sa)
return 0;
}
+/**
+ 'error' is 0 if no errors were encountered during the SOCKS
+ operation (normally a CONNECT with no errors means that the
+ connect() was successful).
+ If 'error' is not 0, it means that an error was encountered and
+ error carries the errno(3) of the error.
+*/
int
-socks_send_reply(socks_state_t *state, struct evbuffer *dest, int status)
+socks_send_reply(socks_state_t *state, struct evbuffer *dest, int error)
{
- if (state->version == 5)
+ int status = socks_errno_to_reply(state, error);
+ if (state->version == SOCKS5_VERSION)
return socks5_send_reply(dest, state, status);
- else if (state->version == 4)
+ else if (state->version == SOCKS4_VERSION)
return socks4_send_reply(dest, state, status);
else
return -1;
diff --git a/src/socks.h b/src/socks.h
index 589cd9c..01a76ff 100644
--- a/src/socks.h
+++ b/src/socks.h
@@ -27,13 +27,19 @@ int socks_state_get_address(const socks_state_t *state,
const char **addr_out,
int *port_out);
int socks_state_set_address(socks_state_t *state, const struct sockaddr *sa);
-int socks_send_reply(socks_state_t *state, struct evbuffer *dest, int status);
+int socks_send_reply(socks_state_t *state, struct evbuffer *dest, int error);
#define SOCKS_SUCCESS 0x0
#define SOCKS_FAILED 0x01
-#define SOCKS5_SUCCESS 0x0
-#define SOCKS5_FAILED 0x01
+#define SOCKS5_SUCCESS 0x0
+#define SOCKS5_FAILED_GENERAL 0x01
+#define SOCKS5_FAILED_NOTALLOWED 0x02
+#define SOCKS5_FAILED_NETUNREACH 0x03
+#define SOCKS5_FAILED_HOSTUNREACH 0x04
+#define SOCKS5_FAILED_REFUSED 0x05
+#define SOCKS5_FAILED_TTLEXPIRED 0x06
+#define SOCKS5_FAILED_UNSUPPORTED 0x07
#ifdef SOCKS_PRIVATE
diff --git a/src/test/unittest_socks.c b/src/test/unittest_socks.c
index 8f06d51..8839eff 100644
--- a/src/test/unittest_socks.c
+++ b/src/test/unittest_socks.c
@@ -266,6 +266,21 @@ test_socks_socks5_request(void *data)
buffer_len = evbuffer_get_length(source);
tt_int_op(0, ==, evbuffer_drain(source, buffer_len));
+ /* Eigth test:
+ Everything is dreamy... if only the requested command was CONNECT... */
+ uchar req8[9];
+ req8[0] = 3;
+ req8[1] = 0;
+ req8[2] = 1;
+ memcpy(req8+3,&addr_ipv4,4);
+ memcpy(req8+7,&port,2);
+
+ evbuffer_add(source, "\x05", 1);
+ evbuffer_add(source, req8, 9);
+ /* '-2' means that we don't support the requested command. */
+ tt_int_op(-2, ==, socks5_handle_request(source,&pr1));
+
+
end:
if (state)
socks_state_free(state);
@@ -301,7 +316,7 @@ test_socks_socks5_request_reply(void *data)
We ask the server to send us a reply on an IPv4 request with
succesful status. */
tt_int_op(1, ==, socks5_send_reply(reply_dest,
- state, SOCKS_SUCCESS));
+ state, SOCKS5_SUCCESS));
uchar rep1[255];
evbuffer_remove(reply_dest,rep1,255); /* yes, this is dirty */
@@ -348,7 +363,7 @@ test_socks_socks5_request_reply(void *data)
strcpy(state->parsereq.addr, fqdn);
tt_int_op(1, ==, socks5_send_reply(reply_dest,
- state, SOCKS_FAILED));
+ state, SOCKS5_FAILED_GENERAL));
uchar rep3[255];
evbuffer_remove(reply_dest,rep3,255);
@@ -360,6 +375,20 @@ test_socks_socks5_request_reply(void *data)
/* check port */
tt_int_op(0, ==, memcmp(rep3+5+strlen(fqdn),"\x1c\xbd",2));
+ /* Fourth test:
+ We ask the server while having an empty parsereq and with a
+ SOCKS5_FAILED_UNSUPPORTED status. */
+ memset(&state->parsereq,'\x00',sizeof(struct parsereq));
+
+ tt_int_op(1, ==, socks5_send_reply(reply_dest,
+ state, SOCKS5_FAILED_UNSUPPORTED));
+ uchar rep4[255];
+ evbuffer_remove(reply_dest,rep4,255);
+
+ tt_assert(rep4[3] == SOCKS5_ATYP_IPV4);
+ tt_int_op(0, ==, memcmp(rep4+4,"\x00\x00\x00\x00",4));
+ tt_int_op(0, ==, memcmp(rep4+4+4, "\x00\x00", 2));
+
end:
if (state)
socks_state_free(state);
@@ -534,7 +563,7 @@ test_socks_socks4_request_reply(void *data)
We ask the server to send us a reply on an IPv4 request with
succesful status. */
tt_int_op(1, ==, socks4_send_reply(reply_dest,
- state, SOCKS_SUCCESS));
+ state, SOCKS4_SUCCESS));
uchar rep1[255];
evbuffer_remove(reply_dest,rep1,255); /* yes, this is dirty */
@@ -558,7 +587,7 @@ test_socks_socks4_request_reply(void *data)
strcpy(state->parsereq.addr, fqdn);
tt_int_op(1, ==, socks4_send_reply(reply_dest,
- state, SOCKS_FAILED));
+ state, SOCKS4_FAILED));
uchar rep2[255];
evbuffer_remove(reply_dest,rep2,255);
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits