diff --git a/ChangeLog b/ChangeLog index c867738432..7e0fe2325f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -46,6 +46,7 @@ Changes in version 0.2.1.9-alpha - 2008-12-2? - Optimize out calls to time(NULL) that occur for every IO operation, or for every cell. On systems where time() is a slow syscalls, this will be slightly helpful. + - Exit servers can now answer resolve requests for ip6.arpa addresses. o Minor features (controller): - New CONSENSUS_ARRIVED event to note when a new consensus has diff --git a/src/common/address.c b/src/common/address.c index 9c7da1af2a..efbc79c2be 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -344,6 +344,139 @@ tor_addr_to_str(char *dest, const tor_addr_t *addr, int len, int decorate) return ptr; } +/** Parse an .in-addr.arpa or .ip6.arpa address from address. Return 0 + * if this is not an .in-addr.arpa address or an .ip6.arpa address. Return -1 + * if this is an ill-formed .in-addr.arpa address or an .ip6.arpa address. + * Also return -1 if family is not AF_UNSPEC, and the parsed address + * family does not match family. On success, return 1, and store the + * result, if any, into result, if provided. + * + * If accept_regular is set and the address is in neither recognized + * reverse lookup hostname format, try parsing the address as a regular + * IPv4 or IPv6 address too. + */ +int +tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address, + int family, int accept_regular) +{ + if (!strcasecmpend(address, ".in-addr.arpa")) { + /* We have an in-addr.arpa address. */ + char buf[INET_NTOA_BUF_LEN]; + size_t len; + struct in_addr inaddr; + if (family == AF_INET6) + return -1; + + len = strlen(address) - strlen(".in-addr.arpa"); + if (len >= INET_NTOA_BUF_LEN) + return -1; /* Too long. */ + + memcpy(buf, address, len); + buf[len] = '\0'; + if (tor_inet_aton(buf, &inaddr) == 0) + return -1; /* malformed. */ + + /* reverse the bytes */ + inaddr.s_addr = (((inaddr.s_addr & 0x000000fful) << 24) + |((inaddr.s_addr & 0x0000ff00ul) << 8) + |((inaddr.s_addr & 0x00ff0000ul) >> 8) + |((inaddr.s_addr & 0xff000000ul) >> 24)); + + if (result) { + memset(result, 0, sizeof(tor_addr_t)); + result->family = AF_INET; + result->addr.in_addr.s_addr = inaddr.s_addr; + } + return 1; + } + + if (!strcasecmpend(address, ".ip6.arpa")) { + const char *cp; + int i; + int n0, n1; + struct in6_addr in6; + + if (family == AF_INET) + return -1; + + cp = address; + for (i = 0; i < 16; ++i) { + n0 = hex_decode_digit(*cp++); /* The low-order nybble appears first. */ + if (*cp++ != '.') return -1; /* Then a dot. */ + n1 = hex_decode_digit(*cp++); /* The high-order nybble appears first. */ + if (*cp++ != '.') return -1; /* Then another dot. */ + if (n0<0 || n1 < 0) /* Both nybbles must be hex. */ + return -1; + + /* We don't check the length of the string in here. But that's okay, + * since we already know that the string ends with ".ip6.arpa", and + * there is no way to frameshift .ip6.arpa so it fits into the pattern + * of hexdigit, period, hexdigit, period that we enforce above. + */ + + /* Assign from low-byte to high-byte. */ + in6.s6_addr[15-i] = n0 | (n1 << 4); + } + if (strcasecmp(cp, "ip6.arpa")) + return -1; + + if (result) { + result->family = AF_INET6; + memcpy(&result->addr.in6_addr, &in6, sizeof(in6)); + } + return 1; + } + + if (accept_regular) { + tor_addr_t tmp; + int r = tor_addr_from_str(&tmp, address); + if (r < 0) + return 0; + if (r != family && family != AF_UNSPEC) + return -1; + + if (result) + memcpy(result, &tmp, sizeof(tor_addr_t)); + + return 1; + } + + return 0; +} + +/** Convert addr to an in-addr.arpa name or a .ip6.arpa name, and store + * the result in the outlen-byte buffer at out. Return 0 on + * success, -1 on failure. */ +int +tor_addr_to_reverse_lookup_name(char *out, size_t outlen, + const tor_addr_t *addr) +{ + if (addr->family == AF_INET) { + uint32_t a = tor_addr_to_ipv4h(addr); + + return tor_snprintf(out, outlen, "%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)); + } else if (addr->family == AF_INET6) { + int i; + char *cp = out; + if (outlen < REVERSE_LOOKUP_NAME_BUF_LEN) + return -1; + for (i = 15; i >= 0; --i) { + uint8_t byte = addr->addr.in6_addr.s6_addr[i]; + *cp++ = "0123456789abcdef"[byte & 0x0f]; + *cp++ = '.'; + *cp++ = "0123456789abcdef"[byte >> 4]; + *cp++ = '.'; + } + memcpy(cp, "ip6.arpa", 9); /* 8 characters plus nul */ + return 0; + } + return -1; +} + /** Parse a string s containing an IPv4/IPv6 address, and possibly * a mask and port or port range. Store the parsed address in * addr_out, a mask (if any) in mask_out, and port(s) (if any) @@ -831,6 +964,13 @@ tor_dup_addr(const tor_addr_t *addr) return tor_strdup(buf); } +/** Copy the address in src to dest */ +void +tor_addr_assign(tor_addr_t *dest, const tor_addr_t *src) +{ + memcpy(dest, src, sizeof(tor_addr_t)); +} + /** Return a string representing the address addr. This string is * statically allocated, and must not be freed. Each call to * fmt_addr invalidates the last result of the function. This diff --git a/src/common/address.h b/src/common/address.h index 5f9e01910a..fa2ac1b5bc 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -90,6 +90,7 @@ tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u) int tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr_out); char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC; +void tor_addr_assign(tor_addr_t *dest, const tor_addr_t *src); const char *fmt_addr(const tor_addr_t *addr); int get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr); @@ -113,6 +114,15 @@ int tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2, unsigned int tor_addr_hash(const tor_addr_t *addr); int tor_addr_is_v4(const tor_addr_t *addr); int tor_addr_is_internal(const tor_addr_t *ip, int for_listening) ATTR_PURE; + +/** Longest length that can be required for a reverse lookup name. */ +/* 32 nybbles, 32 dots, 8 characters of "ip6.arpa", 1 NUL: 73 characters. */ +#define REVERSE_LOOKUP_NAME_BUF_LEN 73 +int tor_addr_to_reverse_lookup_name(char *out, size_t outlen, + const tor_addr_t *addr); +int tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address, + int family, int accept_regular); + int tor_addr_port_parse(const char *s, tor_addr_t *addr_out, uint16_t *port_out); int tor_addr_parse_mask_ports(const char *s, diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 620db73a62..9b192b5565 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1436,17 +1436,10 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn, if (options->ClientDNSRejectInternalAddresses) { /* Don't let people try to do a reverse lookup on 10.0.0.1. */ tor_addr_t addr; - struct in_addr in; int ok; - if (!strcasecmpend(socks->address, ".in-addr.arpa")) - ok = !parse_inaddr_arpa_address(socks->address, &in); - else - ok = tor_inet_aton(socks->address, &in); - /*XXXX021 make this a function. */ - addr.family = AF_INET; - memcpy(&addr.addr.in_addr, &in, sizeof(struct in_addr)); - - if (ok && tor_addr_is_internal(&addr, 0)) { + ok = tor_addr_parse_reverse_lookup_name( + &addr, socks->address, AF_UNSPEC, 1); + if (ok == 1 && tor_addr_is_internal(&addr, 0)) { connection_ap_handshake_socks_resolved(conn, RESOLVED_TYPE_ERROR, 0, NULL, -1, TIME_MAX); connection_mark_unattached_ap(conn, @@ -2130,7 +2123,7 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn) { int payload_len, command; const char *string_addr; - char inaddr_buf[32]; + char inaddr_buf[REVERSE_LOOKUP_NAME_BUF_LEN]; origin_circuit_t *circ; tor_assert(ap_conn->on_circuit); circ = TO_ORIGIN_CIRCUIT(ap_conn->on_circuit); @@ -2155,36 +2148,29 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn) payload_len = (int)strlen(string_addr)+1; tor_assert(payload_len <= RELAY_PAYLOAD_SIZE); } else { - struct in_addr in; - uint32_t a; - size_t len = strlen(ap_conn->socks_request->address); - char c = 0; - /* XXXX This logic is a little ugly: we check for an in-addr.arpa ending - * on the address. If we have one, the address is already in the right - * order, so we'll leave it alone later. Otherwise, we reverse it and - * turn it into an in-addr.arpa address. */ - if (!strcasecmpend(ap_conn->socks_request->address, ".in-addr.arpa")) { - /* Temporarily truncate the address, so we can give it to inet_aton. */ - c = ap_conn->socks_request->address[len-13]; - ap_conn->socks_request->address[len-13] = '\0'; - } - if (tor_inet_aton(ap_conn->socks_request->address, &in) == 0) { + /* command == SOCKS_COMMAND_RESOLVE_PTR */ + const char *a = ap_conn->socks_request->address; + tor_addr_t addr; + int r; + + /* We're doing a reverse lookup. The input could be an IP address, or + * could be an .in-addr.arpa or .ip6.arpa address */ + r = tor_addr_parse_reverse_lookup_name(&addr, a, AF_INET, 1); + if (r <= 0) { + log_warn(LD_APP, "Rejecting ill-formed reverse lookup of %s", + safe_str(a)); connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL); return -1; } - if (c) { - /* this path happens on DNS. Can we unify? XXXX021 */ - ap_conn->socks_request->address[len-13] = c; - strlcpy(inaddr_buf, ap_conn->socks_request->address, sizeof(inaddr_buf)); - } else { - /* this path happens on tor-resolve. Can we unify? XXXX021 */ - 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)); + + r = tor_addr_to_reverse_lookup_name(inaddr_buf, sizeof(inaddr_buf), &addr); + if (r < 0) { + log_warn(LD_BUG, "Couldn't generate reverse lookup hostname of %s", + safe_str(a)); + connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL); + return -1; } + string_addr = inaddr_buf; payload_len = (int)strlen(inaddr_buf)+1; tor_assert(payload_len <= RELAY_PAYLOAD_SIZE); diff --git a/src/or/dns.c b/src/or/dns.c index a83c48981d..191fd068c1 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -494,49 +494,6 @@ send_resolved_hostname_cell(edge_connection_t *conn, const char *hostname) // log_notice(LD_EXIT, "Sent"); } -/** Given a lower-case address, check to see whether it's a - * 1.2.3.4.in-addr.arpa address used for reverse lookups. If so, - * parse it and place the address in in if present. Return 1 on success; - * 0 if the address is not in in-addr.arpa format, and -1 if the address is - * malformed. */ -/* XXXX021 move this to address.c; unify with logic in connection_edge.c */ -int -parse_inaddr_arpa_address(const char *address, struct in_addr *in) -{ - char buf[INET_NTOA_BUF_LEN]; - char *cp; - size_t len; - struct in_addr inaddr; - - cp = strstr(address, ".in-addr.arpa"); - if (!cp || *(cp+strlen(".in-addr.arpa"))) - return 0; /* not an .in-addr.arpa address */ - - len = cp - address; - - if (len >= INET_NTOA_BUF_LEN) - return -1; /* Too long. */ - - memcpy(buf, address, len); - buf[len] = '\0'; - if (tor_inet_aton(buf, &inaddr) == 0) - return -1; /* malformed. */ - - if (in) { - uint32_t a; - /* reverse the bytes */ - a = (uint32_t) ( ((inaddr.s_addr & 0x000000fful) << 24) - |((inaddr.s_addr & 0x0000ff00ul) << 8) - |((inaddr.s_addr & 0x00ff0000ul) >> 8) - |((inaddr.s_addr & 0xff000000ul) >> 24)); - inaddr.s_addr = a; - - memcpy(in, &inaddr, sizeof(inaddr)); - } - - return 1; -} - /** See if we have a cache entry for exitconn-\>address. if so, * if resolve valid, put it into exitconn-\>addr and return 1. * If resolve failed, free exitconn and return -1. @@ -646,7 +603,7 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, cached_resolve_t search; pending_connection_t *pending_connection; routerinfo_t *me; - struct in_addr in; + tor_addr_t addr; time_t now = time(NULL); uint8_t is_reverse = 0; int r; @@ -657,8 +614,8 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, /* first check if exitconn->_base.address is an IP. If so, we already * know the answer. */ - if (tor_inet_aton(exitconn->_base.address, &in) != 0) { - tor_addr_from_ipv4n(&exitconn->_base.addr, in.s_addr); + if (tor_addr_from_str(&addr, exitconn->_base.address)<0) { + tor_addr_assign(&exitconn->_base.addr, &addr); exitconn->address_ttl = DEFAULT_DNS_TTL; return 1; } @@ -685,11 +642,12 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, * .in-addr.arpa address but this isn't a resolve request, kill the * connection. */ - if ((r = parse_inaddr_arpa_address(exitconn->_base.address, &in)) != 0) { + if ((r = tor_addr_parse_reverse_lookup_name(&addr, exitconn->_base.address, + AF_UNSPEC, 0)) != 0) { if (r == 1) { is_reverse = 1; - if (is_internal_IP(ntohl(in.s_addr), 0)) /* internal address */ - return -1; + if (tor_addr_is_internal(&addr, 0)) /* internal address? */ + return -1; } if (!is_reverse || !is_resolve) { @@ -1298,7 +1256,7 @@ static int launch_resolve(edge_connection_t *exitconn) { char *addr = tor_strdup(exitconn->_base.address); - struct in_addr in; + tor_addr_t a; int r; int options = get_options()->ServerDNSSearchDomains ? 0 : DNS_QUERY_NO_SEARCH; @@ -1311,7 +1269,8 @@ launch_resolve(edge_connection_t *exitconn) } } - r = parse_inaddr_arpa_address(exitconn->_base.address, &in); + r = tor_addr_parse_reverse_lookup_name( + &a, exitconn->_base.address, AF_UNSPEC, 0); if (r == 0) { log_info(LD_EXIT, "Launching eventdns request for %s", escaped_safe_str(exitconn->_base.address)); @@ -1320,8 +1279,12 @@ launch_resolve(edge_connection_t *exitconn) } else if (r == 1) { log_info(LD_EXIT, "Launching eventdns reverse request for %s", escaped_safe_str(exitconn->_base.address)); - r = evdns_resolve_reverse(&in, DNS_QUERY_NO_SEARCH, - evdns_callback, addr); + if (tor_addr_family(&a) == AF_INET) + r = evdns_resolve_reverse(tor_addr_to_in(&a), DNS_QUERY_NO_SEARCH, + evdns_callback, addr); + else + r = evdns_resolve_reverse_ipv6(tor_addr_to_in6(&a), DNS_QUERY_NO_SEARCH, + evdns_callback, addr); } else if (r == -1) { log_warn(LD_BUG, "Somehow a malformed in-addr.arpa address reached here."); } diff --git a/src/or/eventdns.c b/src/or/eventdns.c index 2b28b2bf94..cd486caaa1 100644 --- a/src/or/eventdns.c +++ b/src/or/eventdns.c @@ -2534,7 +2534,7 @@ int evdns_resolve_ipv6(const char *name, int flags, } } -int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) { +int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) { char buf[32]; struct request *req; u32 a; @@ -2552,7 +2552,7 @@ int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type cal return 0; } -int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) { +int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) { /* 32 nybbles, 32 periods, "ip6.arpa", NUL. */ char buf[73]; char *cp; diff --git a/src/or/eventdns.h b/src/or/eventdns.h index d1c34ade7c..df5b936923 100644 --- a/src/or/eventdns.h +++ b/src/or/eventdns.h @@ -268,8 +268,8 @@ int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr); struct in_addr; struct in6_addr; -int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); -int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); +int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); +int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); int evdns_set_option(const char *option, const char *val, int flags); int evdns_resolv_conf_parse(int flags, const char *); #ifdef MS_WINDOWS diff --git a/src/or/or.h b/src/or/or.h index b766083f44..9093127ee3 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3541,7 +3541,6 @@ int dns_resolve(edge_connection_t *exitconn); void dns_launch_correctness_checks(void); int dns_seems_to_be_broken(void); void dns_reset_correctness_checks(void); -int parse_inaddr_arpa_address(const char *address, struct in_addr *in); /********************************* dnsserv.c ************************/ diff --git a/src/or/test.c b/src/or/test.c index 8b07ee536a..28bf46f952 100644 --- a/src/or/test.c +++ b/src/or/test.c @@ -1621,6 +1621,70 @@ test_util_ip6_helpers(void) p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1); test_streq(p1, "18.0.0.1"); + /* Test tor_addr_parse_reverse_lookup_name */ + i = tor_addr_parse_reverse_lookup_name(&t1, "Foobar.baz", AF_UNSPEC, 0); + test_eq(0, i); + i = tor_addr_parse_reverse_lookup_name(&t1, "Foobar.baz", AF_UNSPEC, 1); + test_eq(0, i); + i = tor_addr_parse_reverse_lookup_name(&t1, "1.0.168.192.in-addr.arpa", + AF_UNSPEC, 1); + test_eq(1, i); + test_eq(tor_addr_family(&t1), AF_INET); + p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1); + test_streq(p1, "192.168.0.1"); + i = tor_addr_parse_reverse_lookup_name(&t1, "192.168.0.99", AF_UNSPEC, 0); + test_eq(0, i); + i = tor_addr_parse_reverse_lookup_name(&t1, "192.168.0.99", AF_UNSPEC, 1); + test_eq(1, i); + p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1); + test_streq(p1, "192.168.0.99"); + memset(&t1, 0, sizeof(t1)); + i = tor_addr_parse_reverse_lookup_name(&t1, + "0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f." + "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9." + "ip6.ARPA", + AF_UNSPEC, 0); + test_eq(1, i); + p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1); + test_streq(p1, "[9dee:effe:ebe1:beef:fedc:ba98:7654:3210]"); + /* Failing cases. */ + i = tor_addr_parse_reverse_lookup_name(&t1, + "6.7.8.9.a.b.c.d.e.f." + "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9." + "ip6.ARPA", + AF_UNSPEC, 0); + test_eq(i, -1); + i = tor_addr_parse_reverse_lookup_name(&t1, + "6.7.8.9.a.b.c.d.e.f.a.b.c.d.e.f.0." + "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9." + "ip6.ARPA", + AF_UNSPEC, 0); + test_eq(i, -1); + i = tor_addr_parse_reverse_lookup_name(&t1, + "6.7.8.9.a.b.c.d.e.f.X.0.0.0.0.9." + "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9." + "ip6.ARPA", + AF_UNSPEC, 0); + test_eq(i, -1); + i = tor_addr_parse_reverse_lookup_name(&t1, "32.1.1.in-addr.arpa", + AF_UNSPEC, 0); + /* test_eq(i, -1); XXXX021 Apparently '32.1.1' is a valid aton address. */ + i = tor_addr_parse_reverse_lookup_name(&t1, ".in-addr.arpa", + AF_UNSPEC, 0); + test_eq(i, -1); + i = tor_addr_parse_reverse_lookup_name(&t1, "1.2.3.4.5.in-addr.arpa", + AF_UNSPEC, 0); + test_eq(i, -1); + i = tor_addr_parse_reverse_lookup_name(&t1, "1.2.3.4.5.in-addr.arpa", + AF_INET6, 0); + test_eq(i, -1); + i = tor_addr_parse_reverse_lookup_name(&t1, + "6.7.8.9.a.b.c.d.e.f.a.b.c.d.e.0." + "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9." + "ip6.ARPA", + AF_INET, 0); + test_eq(i, -1); + /* test tor_addr_parse_mask_ports */ test_addr_mask_ports_parse("[::f]/17:47-95", AF_INET6, 0, 0, 0, 0x0000000f, 17, 47, 95);