summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/or/Makefile.am4
-rw-r--r--src/or/config.c8
-rw-r--r--src/or/connection.c43
-rw-r--r--src/or/connection_edge.c8
-rw-r--r--src/or/dnsserv.c177
-rw-r--r--src/or/or.h25
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);