mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-28 14:23:30 +01:00
Call new SOCKS code from parse_socks, to parse multiple packets in row
This commit is contained in:
parent
27333b2298
commit
63c478c1c4
@ -21,7 +21,7 @@ static void socks_request_set_socks5_error(socks_request_t *req,
|
||||
socks5_reply_status_t reason);
|
||||
|
||||
static int parse_socks(const char *data, size_t datalen, socks_request_t *req,
|
||||
int log_sockstype, int safe_socks, ssize_t *drain_out,
|
||||
int log_sockstype, int safe_socks, size_t *drain_out,
|
||||
size_t *want_length_out);
|
||||
static int parse_socks_client(const uint8_t *data, size_t datalen,
|
||||
int state, char **reason,
|
||||
@ -88,13 +88,19 @@ socks_request_free_(socks_request_t *req)
|
||||
|
||||
static int
|
||||
parse_socks4_request(const uint8_t *raw_data, socks_request_t *req,
|
||||
size_t datalen, int *is_socks4a)
|
||||
size_t datalen, int *is_socks4a, size_t *drain_out)
|
||||
{
|
||||
// http://ss5.sourceforge.net/socks4.protocol.txt
|
||||
// http://ss5.sourceforge.net/socks4A.protocol.txt
|
||||
int res = 1;
|
||||
tor_addr_t destaddr;
|
||||
|
||||
tor_assert(is_socks4a);
|
||||
tor_assert(drain_out);
|
||||
|
||||
*is_socks4a = 0;
|
||||
*drain_out = 0;
|
||||
|
||||
req->socks_version = 4;
|
||||
|
||||
socks4_client_request_t *trunnel_req;
|
||||
@ -115,6 +121,9 @@ parse_socks4_request(const uint8_t *raw_data, socks_request_t *req,
|
||||
goto end;
|
||||
}
|
||||
|
||||
tor_assert(parsed >= 0);
|
||||
*drain_out = (size_t)parsed;
|
||||
|
||||
uint8_t command = socks4_client_request_get_command(trunnel_req);
|
||||
req->command = command;
|
||||
|
||||
@ -172,6 +181,7 @@ parse_socks4_request(const uint8_t *raw_data, socks_request_t *req,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
end:
|
||||
socks4_client_request_free(trunnel_req);
|
||||
|
||||
@ -261,6 +271,9 @@ parse_socks5_methods_request(const uint8_t *raw_data, socks_request_t *req,
|
||||
goto end;
|
||||
}
|
||||
|
||||
tor_assert(parsed >= 0);
|
||||
*drain_out = (size_t)parsed;
|
||||
|
||||
size_t n_methods = (size_t)socks5_client_version_get_n_methods(trunnel_req);
|
||||
if (n_methods == 0) {
|
||||
res = -1;
|
||||
@ -282,7 +295,6 @@ parse_socks5_methods_request(const uint8_t *raw_data, socks_request_t *req,
|
||||
}
|
||||
|
||||
end:
|
||||
*drain_out = (size_t)parsed;
|
||||
socks5_client_version_free(trunnel_req);
|
||||
|
||||
return res;
|
||||
@ -342,49 +354,13 @@ process_socks5_methods_request(socks_request_t *req, int have_user_pass,
|
||||
return res;
|
||||
}
|
||||
|
||||
/** There is a (possibly incomplete) socks handshake on <b>buf</b>, 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
|
||||
* MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf,
|
||||
* assign to <b>req</b>, 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 <b>req->reply</b>
|
||||
* and set <b>req->replylen</b>, else leave <b>req->replylen</b> alone.
|
||||
*
|
||||
* If <b>log_sockstype</b> is non-zero, then do a notice-level log of whether
|
||||
* the connection is possibly leaking DNS requests locally or not.
|
||||
*
|
||||
* If <b>safe_socks</b> is true, then reject unsafe socks protocols.
|
||||
*
|
||||
* If returning 0 or -1, <b>req->address</b> and <b>req->port</b> are
|
||||
* undefined.
|
||||
*/
|
||||
int
|
||||
fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
||||
int log_sockstype, int safe_socks)
|
||||
static int
|
||||
handle_socks_message(const uint8_t *raw_data, size_t datalen, socks_request_t *req,
|
||||
int log_sockstype, int safe_socks, size_t *drain_out)
|
||||
{
|
||||
int res = 0;
|
||||
size_t datalen = buf_datalen(buf);
|
||||
uint8_t *raw_data;
|
||||
uint8_t *raw_ptr;
|
||||
uint8_t socks_version;
|
||||
|
||||
raw_data = tor_malloc(datalen);
|
||||
memset(raw_data, 0, datalen);
|
||||
|
||||
buf_peek(buf, (char *)raw_data, datalen);
|
||||
|
||||
raw_ptr = raw_data;
|
||||
|
||||
socks_version = (uint8_t)raw_data[0];
|
||||
uint8_t socks_version = raw_data[0];
|
||||
|
||||
if (socks_version == 4) {
|
||||
if (datalen < SOCKS4_NETWORK_LEN) {
|
||||
@ -395,7 +371,7 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
||||
int is_socks4a = 0;
|
||||
int parse_status =
|
||||
parse_socks4_request((const uint8_t *)raw_data, req, datalen,
|
||||
&is_socks4a);
|
||||
&is_socks4a, drain_out);
|
||||
|
||||
if (parse_status != 1) {
|
||||
res = parse_status;
|
||||
@ -411,7 +387,6 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
||||
goto end;
|
||||
}
|
||||
|
||||
buf_clear(buf);
|
||||
res = 1;
|
||||
goto end;
|
||||
} else if (socks_version == 5) {
|
||||
@ -447,15 +422,47 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
||||
goto end;
|
||||
}
|
||||
|
||||
buf_drain(buf, n_drain); // TODO: do it like this for SOCKS4/4a as well
|
||||
raw_ptr += n_drain;
|
||||
datalen -= n_drain;
|
||||
res = 0;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t n_drain;
|
||||
end:
|
||||
return res;
|
||||
}
|
||||
|
||||
/** There is a (possibly incomplete) socks handshake on <b>buf</b>, 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
|
||||
* MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf,
|
||||
* assign to <b>req</b>, 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 <b>req->reply</b>
|
||||
* and set <b>req->replylen</b>, else leave <b>req->replylen</b> alone.
|
||||
*
|
||||
* If <b>log_sockstype</b> is non-zero, then do a notice-level log of whether
|
||||
* the connection is possibly leaking DNS requests locally or not.
|
||||
*
|
||||
* If <b>safe_socks</b> is true, then reject unsafe socks protocols.
|
||||
*
|
||||
* If returning 0 or -1, <b>req->address</b> and <b>req->port</b> are
|
||||
* undefined.
|
||||
*/
|
||||
int
|
||||
fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
||||
int log_sockstype, int safe_socks)
|
||||
{
|
||||
int res = 0;
|
||||
size_t datalen = buf_datalen(buf);
|
||||
size_t n_drain;
|
||||
size_t want_length = 128;
|
||||
const char *head = NULL;
|
||||
|
||||
@ -464,25 +471,27 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
||||
goto end;
|
||||
}
|
||||
|
||||
buf_pullup(buf, datalen, &head, &datalen); // XXX
|
||||
|
||||
do {
|
||||
n_drain = 0;
|
||||
buf_pullup(buf, want_length, &head, &datalen);
|
||||
//buf_pullup(buf, want_length, &head, &datalen);
|
||||
tor_assert(head && datalen >= 2);
|
||||
want_length = 0;
|
||||
|
||||
res = parse_socks(head, datalen, req, log_sockstype,
|
||||
safe_socks, &n_drain, &want_length);
|
||||
|
||||
if (n_drain < 0)
|
||||
if (res == -1)
|
||||
buf_clear(buf);
|
||||
else if (n_drain > 0)
|
||||
buf_drain(buf, n_drain);
|
||||
|
||||
datalen = buf_datalen(buf);
|
||||
} while (res == 0 && head && want_length < buf_datalen(buf) &&
|
||||
buf_datalen(buf) >= 2);
|
||||
|
||||
end:
|
||||
tor_free(raw_data);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -536,7 +545,7 @@ static const char SOCKS_PROXY_IS_NOT_AN_HTTP_PROXY_MSG[] =
|
||||
* we'd like to see in the input buffer, if they're available. */
|
||||
static int
|
||||
parse_socks(const char *data, size_t datalen, socks_request_t *req,
|
||||
int log_sockstype, int safe_socks, ssize_t *drain_out,
|
||||
int log_sockstype, int safe_socks, size_t *drain_out,
|
||||
size_t *want_length_out)
|
||||
{
|
||||
unsigned int len;
|
||||
@ -551,6 +560,15 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
|
||||
return 0;
|
||||
}
|
||||
|
||||
socksver = get_uint8(data);
|
||||
|
||||
if ((socksver == 5 && req->socks_version != 5) ||
|
||||
socksver == 4) {
|
||||
*want_length_out = 128; // TODO remove this arg later
|
||||
return handle_socks_message((const uint8_t *)data, datalen, req,
|
||||
log_sockstype, safe_socks, drain_out);
|
||||
}
|
||||
|
||||
if (req->socks_version == 5 && !req->got_auth) {
|
||||
/* See if we have received authentication. Strictly speaking, we should
|
||||
also check whether we actually negotiated username/password
|
||||
@ -597,8 +615,6 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
|
||||
}
|
||||
}
|
||||
|
||||
socksver = *data;
|
||||
|
||||
switch (socksver) { /* which version of socks? */
|
||||
case 5: /* socks5 */
|
||||
|
||||
@ -783,6 +799,9 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
tor_assert_unreached();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Inspect a reply from SOCKS server stored in <b>buf</b> according
|
||||
|
Loading…
Reference in New Issue
Block a user