summaryrefslogtreecommitdiff
path: root/src/or/dnsserv.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2007-05-24 20:31:30 +0000
committerNick Mathewson <nickm@torproject.org>2007-05-24 20:31:30 +0000
commit703bf1962068a2beb8754f9bb772efd7eb3c100d (patch)
tree4fa9db1792fad7b4bcc36f2ff466f9033549eaca /src/or/dnsserv.c
parent17830bc03ba26dc90258f436fbd9786cf769027c (diff)
downloadtor-703bf1962068a2beb8754f9bb772efd7eb3c100d.tar.gz
tor-703bf1962068a2beb8754f9bb772efd7eb3c100d.zip
r12942@catbus: nickm | 2007-05-24 16:31:22 -0400
Well, that was easier than I thought it would be. Tor is now a DNS proxy as well as a socks proxy. Probably some bugs remain, but since it A) has managed to resolve one address for me successfully, and B) will not affect anybody who leaves DNSPort unset, it feel like a good time to commit. svn:r10317
Diffstat (limited to 'src/or/dnsserv.c')
-rw-r--r--src/or/dnsserv.c177
1 files changed, 177 insertions, 0 deletions
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;
+}
+