diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/Makefile.am | 4 | ||||
-rw-r--r-- | src/or/config.c | 8 | ||||
-rw-r--r-- | src/or/connection.c | 43 | ||||
-rw-r--r-- | src/or/connection_edge.c | 8 | ||||
-rw-r--r-- | src/or/dnsserv.c | 177 | ||||
-rw-r--r-- | src/or/or.h | 25 |
6 files changed, 253 insertions, 12 deletions
diff --git a/src/or/Makefile.am b/src/or/Makefile.am index 744ea35b67..2b35ef75c7 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -7,7 +7,7 @@ bin_PROGRAMS = tor tor_SOURCES = buffers.c circuitbuild.c circuitlist.c \ circuituse.c command.c config.c \ connection.c connection_edge.c connection_or.c control.c \ - cpuworker.c directory.c dirserv.c dns.c hibernate.c main.c \ + cpuworker.c directory.c dirserv.c dns.c dnsserv.c hibernate.c main.c \ onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \ rendservice.c rephist.c router.c routerlist.c routerparse.c \ eventdns.c \ @@ -23,7 +23,7 @@ tor_LDADD = ../common/libor.a ../common/libor-crypto.a \ test_SOURCES = buffers.c circuitbuild.c circuitlist.c \ circuituse.c command.c config.c \ connection.c connection_edge.c connection_or.c control.c \ - cpuworker.c directory.c dirserv.c dns.c hibernate.c main.c \ + cpuworker.c directory.c dirserv.c dns.c dnsserv.c hibernate.c main.c \ onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \ rendservice.c rephist.c router.c routerlist.c routerparse.c \ eventdns.c \ diff --git a/src/or/config.c b/src/or/config.c index 05e32d71cf..8cfd5087fc 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -155,6 +155,8 @@ static config_var_t _option_vars[] = { VAR("DirPort", UINT, DirPort, "0"), OBSOLETE("DirPostPeriod"), VAR("DirServer", LINELIST, DirServers, NULL), + VAR("DNSPort", UINT, DNSPort, "0"), + VAR("DNSListenAddress", LINELIST, DNSListenAddress, NULL), VAR("DownloadExtraInfo", BOOL, DownloadExtraInfo, "0"), VAR("EnforceDistinctSubnets", BOOL, EnforceDistinctSubnets,"1"), VAR("EntryNodes", STRING, EntryNodes, NULL), @@ -2415,6 +2417,9 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->DirPort == 0 && options->DirListenAddress != NULL) REJECT("DirPort must be defined if DirListenAddress is defined."); + if (options->DNSPort == 0 && options->DNSListenAddress != NULL) + REJECT("DirPort must be defined if DirListenAddress is defined."); + if (options->ControlPort == 0 && options->ControlListenAddress != NULL) REJECT("ControlPort must be defined if ControlListenAddress is defined."); @@ -2523,6 +2528,9 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->SocksPort < 0 || options->SocksPort > 65535) REJECT("SocksPort option out of bounds."); + if (options->DNSPort < 0 || options->DNSPort > 65535) + REJECT("DNSPort option out of bounds."); + if (options->TransPort < 0 || options->TransPort > 65535) REJECT("TransPort option out of bounds."); diff --git a/src/or/connection.c b/src/or/connection.c index 5ca2744778..daafc7d7c0 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -48,6 +48,7 @@ conn_type_to_string(int type) case CONN_TYPE_AP_TRANS_LISTENER: return "Transparent pf/netfilter listener"; case CONN_TYPE_AP_NATD_LISTENER: return "Transparent natd listener"; + case CONN_TYPE_AP_DNS_LISTENER: return "DNS listener"; case CONN_TYPE_AP: return "Socks"; case CONN_TYPE_DIR_LISTENER: return "Directory listener"; case CONN_TYPE_DIR: return "Directory"; @@ -74,6 +75,7 @@ conn_state_to_string(int type, int state) case CONN_TYPE_AP_LISTENER: case CONN_TYPE_AP_TRANS_LISTENER: case CONN_TYPE_AP_NATD_LISTENER: + case CONN_TYPE_AP_DNS_LISTENER: case CONN_TYPE_DIR_LISTENER: case CONN_TYPE_CONTROL_LISTENER: if (state == LISTENER_STATE_READY) @@ -240,6 +242,9 @@ connection_unregister_events(connection_t *conn) log_warn(LD_BUG, "Error removing write event for %d", conn->s); tor_free(conn->write_event); } + if (conn->dns_server_port) { + dnsserv_close_listener(conn); + } } /** Deallocate memory used by <b>conn</b>. Deallocate its buffers if @@ -491,6 +496,12 @@ connection_about_to_close_connection(connection_t *conn) " set end_reason.", conn->marked_for_close_file, conn->marked_for_close); } + if (edge_conn->dns_server_request) { + log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having" + " replied to DNS request.", + conn->marked_for_close_file, conn->marked_for_close); + dnsserv_reject_request(edge_conn); + } control_event_stream_status(edge_conn, STREAM_EVENT_CLOSED, edge_conn->end_reason); circ = circuit_get_by_edge_conn(edge_conn); @@ -632,6 +643,7 @@ connection_create_listener(const char *listenaddress, uint16_t listenport, #ifndef MS_WINDOWS int one=1; #endif + int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER); memset(&listenaddr,0,sizeof(struct sockaddr_in)); if (parse_addr_port(LOG_WARN, listenaddress, &address, &addr, &usePort)<0) { @@ -658,7 +670,9 @@ connection_create_listener(const char *listenaddress, uint16_t listenport, return NULL; } - s = tor_open_socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + s = tor_open_socket(PF_INET, + is_tcp ? SOCK_STREAM : SOCK_DGRAM, + is_tcp ? IPPROTO_TCP: IPPROTO_UDP); if (s < 0) { log_warn(LD_NET,"Socket creation failed."); goto err; @@ -683,11 +697,13 @@ connection_create_listener(const char *listenaddress, uint16_t listenport, goto err; } - if (listen(s,SOMAXCONN) < 0) { - log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, - tor_socket_strerror(tor_socket_errno(s))); - tor_close_socket(s); - goto err; + if (is_tcp) { + if (listen(s,SOMAXCONN) < 0) { + log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, + tor_socket_strerror(tor_socket_errno(s))); + tor_close_socket(s); + goto err; + } } set_socket_nonblocking(s); @@ -708,7 +724,12 @@ connection_create_listener(const char *listenaddress, uint16_t listenport, conn_type_to_string(type), usePort); conn->state = LISTENER_STATE_READY; - connection_start_reading(conn); + if (is_tcp) { + connection_start_reading(conn); + } else { + tor_assert(type == CONN_TYPE_AP_DNS_LISTENER); + dnsserv_configure_listener(conn); + } return conn; @@ -1125,6 +1146,10 @@ retry_all_listeners(int force, smartlist_t *replaced_conns, options->NatdPort, "127.0.0.1", force, replaced_conns, new_conns, 0)<0) return -1; + if (retry_listeners(CONN_TYPE_AP_DNS_LISTENER, options->DNSListenAddress, + options->DNSPort, "127.0.0.1", force, + replaced_conns, new_conns, 0)<0) + return -1; if (retry_listeners(CONN_TYPE_CONTROL_LISTENER, options->ControlListenAddress, options->ControlPort, "127.0.0.1", force, @@ -1535,6 +1560,10 @@ connection_handle_read(connection_t *conn) return connection_handle_listener_read(conn, CONN_TYPE_DIR); case CONN_TYPE_CONTROL_LISTENER: return connection_handle_listener_read(conn, CONN_TYPE_CONTROL); + case CONN_TYPE_AP_DNS_LISTENER: + /* This should never happen; eventdns.c handles the reads here. */ + tor_fragile_assert(); + return 0; } loop_again: diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index a910013d25..e2b4f1b021 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -30,7 +30,6 @@ static smartlist_t *redirect_exit_list = NULL; static int connection_ap_handshake_process_socks(edge_connection_t *conn); static int connection_ap_process_natd(edge_connection_t *conn); static int connection_exit_connect_dir(edge_connection_t *exitconn); -static int hostname_is_noconnect_address(const char *address); /** An AP stream has failed/finished. If it hasn't already sent back * a socks reply, send one now (based on endreason). Also set @@ -1920,6 +1919,11 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn, char buf[384]; size_t replylen; + if (conn->dns_server_request) { + dnsserv_resolved(conn, answer_type, answer_len, answer, ttl); + return; + } + if (ttl >= 0) { if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { uint32_t a = ntohl(get_uint32(answer)); @@ -2551,7 +2555,7 @@ failed: /** Check if the address is of the form "y.noconnect" */ -static int +int hostname_is_noconnect_address(const char *address) { return ! strcasecmpend(address, ".noconnect"); diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c new file mode 100644 index 0000000000..489dda73e8 --- /dev/null +++ b/src/or/dnsserv.c @@ -0,0 +1,177 @@ +/* Copyright 2007 Roger Dingledine, Nick Mathewson */ +/* See LICENSE for licensing information */ +/* $Id$ */ +const char dnsserv_c_id[] = + "$Id$"; + +/** + * \file dnservs.c + * \brief Implements client-side DNS proxy server code. + **/ + +#include "or.h" +#include "eventdns.h" + +static void +evdns_server_callback(struct evdns_server_request *req, void *_data) +{ + edge_connection_t *conn; + int i = 0; + struct evdns_server_question *q = NULL; + struct sockaddr_storage addr; + struct sockaddr *sa; + int addrlen; + uint32_t ipaddr; + int err = DNS_ERR_NONE; + + tor_assert(req); + tor_assert(_data == NULL); + log_info(LD_APP, "Got a new DNS request!"); + + if ((addrlen = evdns_server_request_get_requesting_addr(req, + (struct sockaddr*)&addr, sizeof(addr))) < 0) { + log_warn(LD_APP, "Couldn't get requesting address."); + evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); + return; + } + sa = (struct sockaddr*) &addr; + if (sa->sa_family != AF_INET) { + /* XXXX020 Handle IPV6 */ + log_warn(LD_APP, "Requesting address wasn't ipv4."); + evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); + return; + } else { + struct sockaddr_in *sin = (struct sockaddr_in*)&addr; + ipaddr = ntohl(sin->sin_addr.s_addr); + } + if (!socks_policy_permits_address(ipaddr)) { + log_warn(LD_APP, "Rejecting DNS request from disallowed IP."); + evdns_server_request_respond(req, DNS_ERR_REFUSED); + return; + } + if (req->nquestions == 0) { + log_info(LD_APP, "No questions in DNS request; sending back nil reply."); + evdns_server_request_respond(req, 0); + return; + } + if (req->nquestions > 1) { + log_info(LD_APP, "Got a DNS request with more than one question; I only " + "handle one question at a time for now. Skipping the extras."); + } + for (i = 0; i < req->nquestions; ++i) { + if (req->questions[i]->class != EVDNS_CLASS_INET) + continue; + switch (req->questions[i]->type) { + case EVDNS_TYPE_A: + case EVDNS_TYPE_PTR: + q = req->questions[i]; + default: + break; + } + } + if (!q) { + log_info(LD_APP, "None of the questions we got were ones we're willing " + "to support. Sending error."); + evdns_server_request_respond(req, DNS_ERR_NOTIMPL); + return; + } + if (q->type == EVDNS_TYPE_A) { + if (hostname_is_noconnect_address(q->name)) { + err = DNS_ERR_REFUSED; + } + } else { + tor_assert(q->type == EVDNS_TYPE_PTR); + } + if (err == DNS_ERR_NONE && strlen(q->name) > MAX_SOCKS_ADDR_LEN-1) + err = DNS_ERR_FORMAT; + + if (err != DNS_ERR_NONE) { + evdns_server_request_respond(req, err); + return; + } + + /* XXXX020 Handle .onion and .exit. */ + /* XXXX020 Send a stream event to the controller. */ + + conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP)); + if (q->type == EVDNS_TYPE_A) + conn->socks_request->command = SOCKS_COMMAND_RESOLVE; + else + conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR; + + strlcpy(conn->socks_request->address, q->name, + sizeof(conn->socks_request->address)); + + conn->dns_server_request = req; + + /* XXXX need to set state ?? */ + + log_info(LD_APP, "Passing request for %s to rewrite_and_attach.", q->name); + connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL); + /* Now the connection is marked if it was bad. */ + + log_info(LD_APP, "Passed request for %s to rewrite_and_attach.", q->name); +} + +void +dnsserv_reject_request(edge_connection_t *conn) +{ + evdns_server_request_respond(conn->dns_server_request, DNS_ERR_SERVERFAILED); + conn->dns_server_request = NULL; +} + +void +dnsserv_resolved(edge_connection_t *conn, + int answer_type, + size_t answer_len, + const char *answer, + int ttl) +{ + struct evdns_server_request *req = conn->dns_server_request; + int err = DNS_ERR_NONE; + if (!req) + return; + + /* XXXX Re-do. */ + if (ttl < 60) + ttl = 60; + + if (answer_type == RESOLVED_TYPE_IPV6) { + log_info(LD_APP, "Got an IPv6 answer; that's not implemented."); + err = DNS_ERR_NOTIMPL; + } else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4 && + conn->socks_request->command == SOCKS_COMMAND_RESOLVE) { + evdns_server_request_add_a_reply(req, + conn->socks_request->address, + 1, (char*)answer, ttl); + } else if (answer_type == RESOLVED_TYPE_HOSTNAME && + conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR) { + char *ans = tor_strndup(answer, answer_len); + evdns_server_request_add_ptr_reply(req, NULL, + conn->socks_request->address, + (char*)answer, ttl); + tor_free(ans); + } else { + err = DNS_ERR_SERVERFAILED; + } + + evdns_server_request_respond(req, err); + conn->dns_server_request = NULL; +} + +void +dnsserv_configure_listener(connection_t *conn) +{ + tor_assert(conn); + tor_assert(conn->s); + + evdns_add_server_port(conn->s, 0, evdns_server_callback, NULL); +} + +void +dnsserv_close_listener(connection_t *conn) +{ + evdns_close_server_port(conn->dns_server_port); + conn->dns_server_port = NULL; +} + diff --git a/src/or/or.h b/src/or/or.h index 51c75eddeb..a61e5efc78 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -237,7 +237,9 @@ typedef enum { /** Type for sockets listening for transparent connections redirected by * natd. */ #define CONN_TYPE_AP_NATD_LISTENER 14 -#define _CONN_TYPE_MAX 14 +/** Type for sockets listening for DNS requests. */ +#define CONN_TYPE_AP_DNS_LISTENER 15 +#define _CONN_TYPE_MAX 15 #define CONN_IS_EDGE(x) \ ((x)->type == CONN_TYPE_EXIT || (x)->type == CONN_TYPE_AP) @@ -813,6 +815,9 @@ typedef struct connection_t { * read_event should be made active with libevent. */ unsigned int active_on_link:1; + /* XXXX020 move this into a subtype!!! */ + struct evdns_server_port *dns_server_port; + } connection_t; /** Subtype of connection_t for an "OR connection" -- that is, one that speaks @@ -905,6 +910,9 @@ typedef struct edge_connection_t { * already retried several times. */ uint8_t num_socks_retries; + /** DOCDOC */ + struct evdns_server_request *dns_server_request; + } edge_connection_t; /** Subtype of connection_t for an "directory connection" -- that is, an HTTP @@ -1728,6 +1736,8 @@ typedef struct { config_line_t *TransListenAddress; /** Addresses to bind for listening for transparent natd connections */ config_line_t *NatdListenAddress; + /** Addresses to bind for listening for SOCKS connections. */ + config_line_t *DNSListenAddress; /** Addresses to bind for listening for OR connections. */ config_line_t *ORListenAddress; /** Addresses to bind for listening for directory connections. */ @@ -1752,6 +1762,7 @@ typedef struct { int NatdPort; /**< Port to listen on for transparent natd connections. */ int ControlPort; /**< Port to listen on for control connections. */ int DirPort; /**< Port to listen on for directory connections. */ + int DNSPort; /**< Port to listen on for DNS requests. */ int AssumeReachable; /**< Whether to publish our descriptor regardless. */ int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */ int V1AuthoritativeDir; /**< Boolean: is this an authoritative directory @@ -2403,6 +2414,7 @@ void addressmap_get_mappings(smartlist_t *sl, time_t min_expires, int connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn, origin_circuit_t *circ, crypt_path_t *cpath); +int hostname_is_noconnect_address(const char *address); void set_exit_redirects(smartlist_t *lst); typedef enum hostname_type_t { @@ -2655,6 +2667,17 @@ void dns_launch_correctness_checks(void); int dns_seems_to_be_broken(void); void dns_reset_correctness_checks(void); +/********************************* dnsserv.c ************************/ + +void dnsserv_configure_listener(connection_t *conn); +void dnsserv_close_listener(connection_t *conn); +void dnsserv_resolved(edge_connection_t *conn, + int answer_type, + size_t answer_len, + const char *answer, + int ttl); +void dnsserv_reject_request(edge_connection_t *conn); + /********************************* hibernate.c **********************/ int accounting_parse_options(or_options_t *options, int validate_only); |