Back to index

tor  0.2.3.18-rc
Functions
dnsserv.c File Reference

Implements client-side DNS proxy server code. More...

#include "or.h"
#include "dnsserv.h"
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
#include "control.h"
#include "main.h"
#include "policies.h"
#include "eventdns.h"

Go to the source code of this file.

Functions

static void evdns_server_callback (struct evdns_server_request *req, void *data_)
 Helper function: called by evdns whenever the client sends a request to our DNSPort.
int dnsserv_launch_request (const char *name, int reverse)
 Helper function: called whenever the client sends a resolve request to our controller.
void dnsserv_reject_request (entry_connection_t *conn)
 If there is a pending request on conn that's waiting for an answer, send back an error and free the request.
static const char * evdns_get_orig_address (const struct evdns_server_request *req, int rtype, const char *addr)
 Look up the original name that corresponds to 'addr' in req.
void dnsserv_resolved (entry_connection_t *conn, int answer_type, size_t answer_len, const char *answer, int ttl)
 Tell the dns request waiting for an answer on conn that we have an answer of type answer_type (RESOLVE_TYPE_IPV4/IPV6/ERR), of length answer_len, in answer, with TTL ttl.
void dnsserv_configure_listener (connection_t *conn)
 Set up the evdns server port for the UDP socket on conn, which must be an AP_DNS_LISTENER.
void dnsserv_close_listener (connection_t *conn)
 Free the evdns server port for conn, which must be an AP_DNS_LISTENER.

Detailed Description

Implements client-side DNS proxy server code.

Note: this is the DNS Server code, not the Server DNS code. Confused? This code runs on client-side, and acts as a DNS server. The code in dns.c, on the other hand, runs on Tor servers, and acts as a DNS client.

Definition in file dnsserv.c.


Function Documentation

Free the evdns server port for conn, which must be an AP_DNS_LISTENER.

Definition at line 337 of file dnsserv.c.

{
  listener_connection_t *listener_conn;
  tor_assert(conn);
  tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);

  listener_conn = TO_LISTENER_CONN(conn);

  if (listener_conn->dns_server_port) {
    evdns_close_server_port(listener_conn->dns_server_port);
    listener_conn->dns_server_port = NULL;
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

Set up the evdns server port for the UDP socket on conn, which must be an AP_DNS_LISTENER.

Definition at line 321 of file dnsserv.c.

{
  listener_connection_t *listener_conn;
  tor_assert(conn);
  tor_assert(SOCKET_OK(conn->s));
  tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);

  listener_conn = TO_LISTENER_CONN(conn);
  listener_conn->dns_server_port =
    tor_evdns_add_server_port(conn->s, 0, evdns_server_callback,
                              listener_conn);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int dnsserv_launch_request ( const char *  name,
int  reverse 
)

Helper function: called whenever the client sends a resolve request to our controller.

We need to eventually answer the request req. Returns 0 if the controller will be getting (or has gotten) an event in response; -1 if we couldn't launch the request.

Definition at line 172 of file dnsserv.c.

{
  entry_connection_t *entry_conn;
  edge_connection_t *conn;
  char *q_name;

  /* Make a new dummy AP connection, and attach the request to it. */
  entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
  conn = ENTRY_TO_EDGE_CONN(entry_conn);
  conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;

  if (reverse)
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
  else
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE;

  conn->is_dns_request = 1;

  strlcpy(entry_conn->socks_request->address, name,
          sizeof(entry_conn->socks_request->address));

  entry_conn->socks_request->listener_type = CONN_TYPE_CONTROL_LISTENER;
  entry_conn->original_dest_address = tor_strdup(name);
  entry_conn->session_group = SESSION_GROUP_CONTROL_RESOLVE;
  entry_conn->nym_epoch = get_signewnym_epoch();
  entry_conn->isolation_flags = ISO_DEFAULT;

  if (connection_add(TO_CONN(conn))<0) {
    log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request");
    connection_free(TO_CONN(conn));
    return -1;
  }

  /* Now, unless a controller asked us to leave streams unattached,
  * throw the connection over to get rewritten (which will
  * answer it immediately if it's in the cache, or completely bogus, or
  * automapped), and then attached to a circuit. */
  log_info(LD_APP, "Passing request for %s to rewrite_and_attach.",
           escaped_safe_str_client(name));
  q_name = tor_strdup(name); /* q could be freed in rewrite_and_attach */
  connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL);
  /* Now, the connection is marked if it was bad. */

  log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.",
           escaped_safe_str_client(q_name));
  tor_free(q_name);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

If there is a pending request on conn that's waiting for an answer, send back an error and free the request.

Definition at line 224 of file dnsserv.c.

Here is the call graph for this function:

Here is the caller graph for this function:

void dnsserv_resolved ( entry_connection_t conn,
int  answer_type,
size_t  answer_len,
const char *  answer,
int  ttl 
)

Tell the dns request waiting for an answer on conn that we have an answer of type answer_type (RESOLVE_TYPE_IPV4/IPV6/ERR), of length answer_len, in answer, with TTL ttl.

Doesn't do any caching; that's handled elsewhere.

Definition at line 270 of file dnsserv.c.

{
  struct evdns_server_request *req = conn->dns_server_request;
  const char *name;
  int err = DNS_ERR_NONE;
  if (!req)
    return;
  name = evdns_get_orig_address(req, answer_type,
                                conn->socks_request->address);

  /* XXXX Re-do; this is dumb. */
  if (ttl < 60)
    ttl = 60;

  /* The evdns interface is: add a bunch of reply items (corresponding to one
   * or more of the questions in the request); then, call
   * evdns_server_request_respond. */
  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,
                                     name,
                                     1, answer, ttl);
  } else if (answer_type == RESOLVED_TYPE_HOSTNAME &&
             answer_len < 256 &&
             conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR) {
    char *ans = tor_strndup(answer, answer_len);
    evdns_server_request_add_ptr_reply(req, NULL,
                                       name,
                                       ans, ttl);
    tor_free(ans);
  } else if (answer_type == RESOLVED_TYPE_ERROR) {
    err = DNS_ERR_NOTEXIST;
  } else { /* answer_type == RESOLVED_TYPE_ERROR_TRANSIENT */
    err = DNS_ERR_SERVERFAILED;
  }

  evdns_server_request_respond(req, err);

  conn->dns_server_request = NULL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static const char* evdns_get_orig_address ( const struct evdns_server_request req,
int  rtype,
const char *  addr 
) [static]

Look up the original name that corresponds to 'addr' in req.

We use this to preserve case in order to facilitate people using 0x20-hacks to avoid DNS poisoning.

Definition at line 237 of file dnsserv.c.

{
  int i, type;

  switch (rtype) {
  case RESOLVED_TYPE_IPV4:
    type = EVDNS_TYPE_A;
    break;
  case RESOLVED_TYPE_HOSTNAME:
    type = EVDNS_TYPE_PTR;
    break;
  case RESOLVED_TYPE_IPV6:
    type = EVDNS_TYPE_AAAA;
    break;
  default:
    tor_fragile_assert();
    return addr;
  }

  for (i = 0; i < req->nquestions; ++i) {
    const struct evdns_server_question *q = req->questions[i];
    if (q->type == type && !strcasecmp(q->name, addr))
      return q->name;
  }
  return addr;
}

Here is the caller graph for this function:

static void evdns_server_callback ( struct evdns_server_request req,
void *  data_ 
) [static]

Helper function: called by evdns whenever the client sends a request to our DNSPort.

We need to eventually answer the request req.

Definition at line 32 of file dnsserv.c.

{
  const listener_connection_t *listener = data_;
  entry_connection_t *entry_conn;
  edge_connection_t *conn;
  int i = 0;
  struct evdns_server_question *q = NULL;
  struct sockaddr_storage addr;
  struct sockaddr *sa;
  int addrlen;
  tor_addr_t tor_addr;
  uint16_t port;
  int err = DNS_ERR_NONE;
  char *q_name;

  tor_assert(req);

  log_info(LD_APP, "Got a new DNS request!");

  req->flags |= 0x80; /* set RA */

  /* First, check whether the requesting address matches our SOCKSPolicy. */
  if ((addrlen = evdns_server_request_get_requesting_addr(req,
                      (struct sockaddr*)&addr, (socklen_t)sizeof(addr))) < 0) {
    log_warn(LD_APP, "Couldn't get requesting address.");
    evdns_server_request_respond(req, DNS_ERR_SERVERFAILED);
    return;
  }
  (void) addrlen;
  sa = (struct sockaddr*) &addr;
  if (tor_addr_from_sockaddr(&tor_addr, sa, &port)<0) {
    log_warn(LD_APP, "Requesting address wasn't recognized.");
    evdns_server_request_respond(req, DNS_ERR_SERVERFAILED);
    return;
  }

  if (!socks_policy_permits_address(&tor_addr)) {
    log_warn(LD_APP, "Rejecting DNS request from disallowed IP.");
    evdns_server_request_respond(req, DNS_ERR_REFUSED);
    return;
  }

  /* Now, let's find the first actual question of a type we can answer in this
   * DNS request.  It makes us a little noncompliant to act like this; we
   * should fix that eventually if it turns out to make a difference for
   * anybody. */
  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]->dns_question_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 NOTIMPL.");
    evdns_server_request_respond(req, DNS_ERR_NOTIMPL);
    return;
  }
  if (q->type != EVDNS_TYPE_A) {
    tor_assert(q->type == EVDNS_TYPE_PTR);
  }

  /* Make sure the name isn't too long: This should be impossible, I think. */
  if (err == DNS_ERR_NONE && strlen(q->name) > MAX_SOCKS_ADDR_LEN-1)
    err = DNS_ERR_FORMAT;

  if (err != DNS_ERR_NONE) {
    /* We got an error?  Then send back an answer immediately; we're done. */
    evdns_server_request_respond(req, err);
    return;
  }

  /* Make a new dummy AP connection, and attach the request to it. */
  entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
  conn = ENTRY_TO_EDGE_CONN(entry_conn);
  TO_CONN(conn)->state = AP_CONN_STATE_RESOLVE_WAIT;
  conn->is_dns_request = 1;

  tor_addr_copy(&TO_CONN(conn)->addr, &tor_addr);
  TO_CONN(conn)->port = port;
  TO_CONN(conn)->address = tor_dup_addr(&tor_addr);

  if (q->type == EVDNS_TYPE_A)
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
  else
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;

  strlcpy(entry_conn->socks_request->address, q->name,
          sizeof(entry_conn->socks_request->address));

  entry_conn->socks_request->listener_type = listener->_base.type;
  entry_conn->dns_server_request = req;
  entry_conn->isolation_flags = listener->isolation_flags;
  entry_conn->session_group = listener->session_group;
  entry_conn->nym_epoch = get_signewnym_epoch();

  if (connection_add(ENTRY_TO_CONN(entry_conn)) < 0) {
    log_warn(LD_APP, "Couldn't register dummy connection for DNS request");
    evdns_server_request_respond(req, DNS_ERR_SERVERFAILED);
    connection_free(ENTRY_TO_CONN(entry_conn));
    return;
  }

  control_event_stream_status(entry_conn, STREAM_EVENT_NEW, 0);

  /* Now, unless a controller asked us to leave streams unattached,
  * throw the connection over to get rewritten (which will
  * answer it immediately if it's in the cache, or completely bogus, or
  * automapped), and then attached to a circuit. */
  log_info(LD_APP, "Passing request for %s to rewrite_and_attach.",
           escaped_safe_str_client(q->name));
  q_name = tor_strdup(q->name); /* q could be freed in rewrite_and_attach */
  connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL);
  /* Now, the connection is marked if it was bad. */

  log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.",
           escaped_safe_str_client(q_name));
  tor_free(q_name);
}

Here is the call graph for this function:

Here is the caller graph for this function: