r8894@Kushana: nickm | 2006-09-21 18:30:42 -0400

Specify and implement SOCKS5 interface for reverse hostname lookup.


svn:r8451
This commit is contained in:
Nick Mathewson 2006-09-22 00:43:55 +00:00
parent 9bc8d69dfc
commit 213658f117
7 changed files with 120 additions and 38 deletions

View File

@ -6,6 +6,8 @@ Changes in version 0.1.2.2-alpha - 2006-??-??
previously never implemented. This is only supported by eventdns;
servers now announce in their descriptors whether they support
eventdns.
- Specify and implement client-side SOCKS5 interface for reverse DNS
lookups; see doc/socks-extensions.txt for full information.
o Minor features:
- Check for name servers (like Earthlink's) that hijack failing DNS

View File

@ -32,15 +32,14 @@ def socks5Hello():
def socks5ParseHello(response):
if response != "\x05\x00":
raise ValueError("Bizarre socks5 response")
def socks5ResolveRequest(hostname):
def socks5ResolveRequest(hostname, atype=0x03, command=0xF0):
version = 5
command = 0xF0
rsv = 0
port = 0
atype = 0x03
reqheader = struct.pack("!BBBBB",version, command, rsv, atype, len(hostname))
portstr = struct.pack("!H",port)
return "%s%s%s"%(reqheader,hostname,portstr)
def socks5ParseResponse(r):
if len(r)<8:
return None
@ -49,18 +48,27 @@ def socks5ParseResponse(r):
assert rsv==0
if reply != 0x00:
return "ERROR",reply
assert atype in (0x01,0x04)
expected_len = 4 + ({1:4,4:16}[atype]) + 2
if len(r) < expected_len:
return None
elif len(r) > expected_len:
raise ValueError("Overlong socks5 reply!")
addr = r[4:-2]
if atype == 0x01:
return "%d.%d.%d.%d"%tuple(map(ord,addr))
assert atype in (0x01,0x03,0x04)
if atype != 0x03:
expected_len = 4 + ({1:4,4:16}[atype]) + 2
if len(r) < expected_len:
return None
elif len(r) > expected_len:
raise ValueError("Overlong socks5 reply!")
addr = r[4:-2]
if atype == 0x01:
return "%d.%d.%d.%d"%tuple(map(ord,addr))
else:
# not really the right way to format IPv6
return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr]))
else:
# not really the right way to format IPv6
return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr]))
nul = r.index('\0',4)
return r[4:nul]
def socks5ResolvePTRRequest(hostname):
return socks5ResolveRequest(socket.inet_aton(hostname),
atype=1, command = 0xF1)
def parseHostAndPort(h):
host, port = "localhost", 9050
@ -80,14 +88,18 @@ def parseHostAndPort(h):
return host, port
def resolve(hostname, sockshost, socksport, socksver=4):
def resolve(hostname, sockshost, socksport, socksver=4, reverse=0):
assert socksver in (4,5)
if socksver == 4:
fmt = socks4AResolveRequest
parse = socks4AParseResponse
else:
elif not reverse:
fmt = socks5ResolveRequest
parse = socks5ParseResponse
else:
fmt = socks5ResolvePTRRequest
parse = socks5ParseResponse
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((sockshost,socksport))
if socksver == 5:
@ -114,14 +126,25 @@ if __name__ == '__main__':
print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
sys.exit(0)
socksver = 4
if sys.argv[1] in ("-4", "-5"):
socksver = int(sys.argv[1][1])
del sys.argv[1]
if len(sys.argv) == 4:
reverse = 0
while sys.argv[1] == '-':
if sys.argv[1] in ("-4", "-5"):
socksver = int(sys.argv[1][1])
del sys.argv[1]
elif sys.argv[1] == '-x':
reverse = 1
del sys.argv[1]
elif sys.argv[1] == '--':
break
if len(sys.argv) >= 4:
print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
sys.exit(0)
if len(sys.argv) == 3:
sh,sp = parseHostAndPort(sys.argv[2])
else:
sh,sp = parseHostAndPort("")
resolve(sys.argv[1], sh, sp, socksver)
if reverse and socksver == 4:
socksver = 5
resolve(sys.argv[1], sh, sp, socksver, reverse)

View File

@ -109,7 +109,12 @@ d - Special-case localhost?
o Connect to resolve cells, server-side.
o Add element to routerinfo to note routers that aren't using eventdns,
so we can avoid sending them reverse DNS etc.
- Add client-side interface
. Add client-side interface
o SOCKS interface: specify
o SOCKS interface: implement
- Cache answers client-side
o Add to Tor-resolve.py
- Add to tor-resolve
- Performance improvements

View File

@ -46,6 +46,12 @@ Tor's extensions to the SOCKS protocol
(We support RESOLVE in SOCKS4 too, even though it is unnecessary.)
For SOCKS5 only, we support reverse resolution with a new command value,
"RESOLVE_PTR". In response to a "RESOLVE_PTR" SOCKS5 command with an IPv4
address as its target, Tor attempts to find the canonical hostname for that
IPv4 record, and returns it in the "server bound address" portion of the
reply. (This was not supported before Tor 0.1.2.2-alpha)
3. HTTP-resistance
Tor checks the first byte of each SOCKS request to see whether it looks

View File

@ -974,8 +974,9 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
return 0; /* not yet */
req->command = (unsigned char) *(buf->cur+1);
if (req->command != SOCKS_COMMAND_CONNECT &&
req->command != SOCKS_COMMAND_RESOLVE) {
/* not a connect or resolve? we don't support it. */
req->command != SOCKS_COMMAND_RESOLVE &&
req->command != SOCKS_COMMAND_RESOLVE_PTR) {
/* not a connect or resolve or a resolve_ptr? we don't support it. */
log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.",
req->command);
return -1;
@ -999,7 +1000,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
strlcpy(req->address,tmpbuf,sizeof(req->address));
req->port = ntohs(*(uint16_t*)(buf->cur+8));
buf_remove_from_front(buf, 10);
if (!addressmap_have_mapping(req->address) &&
if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
!addressmap_have_mapping(req->address) &&
!have_warned_about_unsafe_socks) {
log_warn(LD_APP,
"Your application (using socks5 on port %d) is giving "
@ -1025,6 +1027,11 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
"%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN);
return -1;
}
if (req->command == SOCKS_COMMAND_RESOLVE_PTR) {
log_warn(LD_APP, "socks5 received RESOLVE_PTR command with "
"hostname type. Rejecting.");
return -1;
}
memcpy(req->address,buf->cur+5,len);
req->address[len] = 0;
req->port = ntohs(get_uint16(buf->cur+5+len));
@ -1059,7 +1066,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
req->command = (unsigned char) *(buf->cur+1);
if (req->command != SOCKS_COMMAND_CONNECT &&
req->command != SOCKS_COMMAND_RESOLVE) {
/* not a connect or resolve? we don't support it. */
/* not a connect or resolve? we don't support it. (No resolve_ptr with
* socks4.) */
log_warn(LD_APP,"socks4: command %d not recognized. Rejecting.",
req->command);
return -1;

View File

@ -1120,7 +1120,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
return -1;
}
if (socks->command == SOCKS_COMMAND_RESOLVE) {
if (socks->command == SOCKS_COMMAND_RESOLVE) { // resolve_ptr XXXX NM
uint32_t answer;
struct in_addr in;
/* Reply to resolves immediately if we can. */
@ -1181,7 +1181,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
rend_cache_entry_t *entry;
int r;
if (socks->command == SOCKS_COMMAND_RESOLVE) {
if (socks->command != SOCKS_COMMAND_CONNECT) {
/* if it's a resolve request, fail it right now, rather than
* building all the circuits and then realizing it won't work. */
log_warn(LD_APP,
@ -1524,13 +1524,17 @@ int
connection_ap_handshake_send_resolve(edge_connection_t *ap_conn,
origin_circuit_t *circ)
{
int payload_len;
int payload_len, command;
const char *string_addr;
char inaddr_buf[32];
command = ap_conn->socks_request->command;
tor_assert(ap_conn->_base.type == CONN_TYPE_AP);
tor_assert(ap_conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
tor_assert(ap_conn->socks_request);
tor_assert(ap_conn->socks_request->command == SOCKS_COMMAND_RESOLVE);
tor_assert(command == SOCKS_COMMAND_RESOLVE ||
command == SOCKS_COMMAND_RESOLVE_PTR);
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL);
ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
@ -1540,9 +1544,27 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn,
return -1;
}
string_addr = ap_conn->socks_request->address;
payload_len = strlen(string_addr)+1;
tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
if (command == SOCKS_COMMAND_RESOLVE) {
string_addr = ap_conn->socks_request->address;
payload_len = strlen(string_addr)+1;
tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
} else {
struct in_addr in;
uint32_t a;
if (tor_inet_aton(ap_conn->socks_request->address, &in) == 0) {
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
return -1;
}
a = ntohl(in.s_addr);
tor_snprintf(inaddr_buf, sizeof(inaddr_buf), "%d.%d.%d.%d.in-addr.arpa",
(int)(uint8_t)((a )&0xff),
(int)(uint8_t)((a>>8 )&0xff),
(int)(uint8_t)((a>>16)&0xff),
(int)(uint8_t)((a>>24)&0xff));
string_addr = inaddr_buf;
payload_len = strlen(inaddr_buf)+1;
tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
}
log_debug(LD_APP,
"Sending relay cell to begin stream %d.", ap_conn->stream_id);
@ -1625,7 +1647,7 @@ connection_ap_make_bridge(char *address, uint16_t port)
}
/** Send an answer to an AP connection that has requested a DNS lookup
* via SOCKS. The type should be one of RESOLVED_TYPE_(IPV4|IPV6) or
* via SOCKS. The type should be one of RESOLVED_TYPE_(IPV4|IPV6|HOSTNAME) or
* -1 for unreachable; the answer should be in the format specified
* in the socks extensions document.
**/
@ -1636,7 +1658,7 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
const char *answer,
int ttl)
{
char buf[256];
char buf[384];
size_t replylen;
if (answer_type == RESOLVED_TYPE_IPV4) {
@ -1675,6 +1697,14 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
memcpy(buf+4, answer, 16); /* address */
set_uint16(buf+20, 0); /* port == 0. */
replylen = 22;
} else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
buf[1] = SOCKS5_SUCCEEDED;
buf[2] = 0; /* reserved */
buf[3] = 0x03; /* Domainname address type */
memcpy(buf+4, answer, answer_len); /* address */
buf[4+answer_len] = '\0';
set_uint16(buf+4+answer_len+1, 0); /* port == 0. */
replylen = 4+answer_len+1+2;
} else {
buf[1] = SOCKS5_HOST_UNREACHABLE;
memset(buf+2, 0, 8);
@ -1699,7 +1729,8 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
void
connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
size_t replylen,
socks5_reply_status_t status) {
socks5_reply_status_t status)
{
char buf[256];
tor_assert(conn->socks_request); /* make sure it's an AP stream */
@ -2072,7 +2103,7 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
}
}
if (conn->socks_request->command != SOCKS_COMMAND_RESOLVE) {
if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
struct in_addr in;
uint32_t addr = 0;
addr_policy_result_t r;
@ -2082,7 +2113,13 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
exit->exit_policy);
if (r == ADDR_POLICY_REJECTED || r == ADDR_POLICY_PROBABLY_REJECTED)
return 0;
} else {
} else { /* Some kind of a resolve. */
/* Can't support reverse lookups without eventdns. */
if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR &&
exit->has_old_dnsworkers)
return 0;
/* Don't send DNS requests to non-exit servers by default. */
if (policy_is_reject_star(exit->exit_policy))
return 0;

View File

@ -1612,6 +1612,7 @@ typedef struct {
#define MAX_SOCKS_ADDR_LEN 256
#define SOCKS_COMMAND_CONNECT 0x01
#define SOCKS_COMMAND_RESOLVE 0xF0
#define SOCKS_COMMAND_RESOLVE_PTR 0xF1
/** State of a SOCKS request from a user to an OP */
struct socks_request_t {
char socks_version; /**< Which version of SOCKS did the client use? */