aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2006-09-22 00:43:55 +0000
committerNick Mathewson <nickm@torproject.org>2006-09-22 00:43:55 +0000
commit213658f117f88eaeb21ffd61451155f451f67604 (patch)
tree31f4c5d408f6372cc08d1be2731f4c2ab10eced9
parent9bc8d69dfc4ddda5a9c8478b1f1e04490845ded0 (diff)
downloadtor-213658f117f88eaeb21ffd61451155f451f67604.tar
tor-213658f117f88eaeb21ffd61451155f451f67604.tar.gz
r8894@Kushana: nickm | 2006-09-21 18:30:42 -0400
Specify and implement SOCKS5 interface for reverse hostname lookup. svn:r8451
-rw-r--r--ChangeLog2
-rwxr-xr-xcontrib/tor-resolve.py65
-rw-r--r--doc/TODO7
-rw-r--r--doc/socks-extensions.txt6
-rw-r--r--src/or/buffers.c16
-rw-r--r--src/or/connection_edge.c61
-rw-r--r--src/or/or.h1
7 files changed, 120 insertions, 38 deletions
diff --git a/ChangeLog b/ChangeLog
index 34597857f..db03be8fd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/contrib/tor-resolve.py b/contrib/tor-resolve.py
index dd44255bc..3557c2aa9 100755
--- a/contrib/tor-resolve.py
+++ b/contrib/tor-resolve.py
@@ -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)
diff --git a/doc/TODO b/doc/TODO
index f5c8f0dc8..d82da2c3f 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -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
diff --git a/doc/socks-extensions.txt b/doc/socks-extensions.txt
index 7022af14f..8040a8b03 100644
--- a/doc/socks-extensions.txt
+++ b/doc/socks-extensions.txt
@@ -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
diff --git a/src/or/buffers.c b/src/or/buffers.c
index b6e775da8..290a81c8b 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -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;
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 3ed8aa9f2..8d6c9273b 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -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;
diff --git a/src/or/or.h b/src/or/or.h
index 217cb260f..f8852b2e1 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -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? */