[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: TODO: Add a way to request DNS resolves from the controller



On Thursday 05 July 2007 18:38:13 Nick Mathewson wrote:
>   - As written, if there's more than one controller that uses the
>     RESOLVE command, all of them are informed whenever _any_ of them
>     gets a "650 RESOLVE".  (This is the big one, the rest are cosmetic
>     and easy to fix.)

I could only think of one way to fix this and it wasn't very pretty. Included 
it anyway. It also removes the ability to have concurrent resolve requests on 
the same controller, e.g. 'resolve hotmail.com google.com' will only do 
hotmail.com.

>
>   - Using a new socks command isn't right here: this isn't new
>     functionality.  Instead, it would probably be best to add a bit to
>     the edge_connection_t struct.
>

Fixed.

>   - evdns_server_control() is almost entirely cut-and-pated code.
>     That's bad.  Common code should be in a function.
>

Didn't do anything here. It's copy and pasted from all over the place, so 
couldn't see a way of saving anything more than a few lines. Will be 
interested to see how it should be done.

>   - The evdns_* prefix is reserved for functions in evdns.c.
>
>   - The handle_control_* prefix is for handling control commands from
>     a controller.
>
Fixed.

Thanks,
Rob

-- 

Browse Anonymously Anywhere	- http://anonymityanywhere.com
TorK	- KDE Anonymity Manager	- http://tork.sf.net
KlamAV	- KDE Anti-Virus 	- http://www.klamav.net

Index: src/or/control.c
===================================================================
--- src/or/control.c	(revision 10779)
+++ src/or/control.c	(working copy)
@@ -43,7 +43,8 @@
 #define EVENT_STATUS_GENERAL   0x0012
 #define EVENT_GUARD            0x0013
 #define EVENT_STREAM_BANDWIDTH_USED   0x0014
-#define _EVENT_MAX             0x0014
+#define EVENT_RESOLVE_RESPONSE   0x0015
+#define _EVENT_MAX             0x0015
 /* If _EVENT_MAX ever hits 0x0020, we need to make the mask wider. */
 
 /** Bitfield: The bit 1&lt;&lt;e is set if <b>any</b> open control
@@ -133,6 +134,8 @@
 static int handle_control_closecircuit(control_connection_t *conn,
                                        uint32_t len,
                                        const char *body);
+static int handle_control_resolve(control_connection_t *conn, uint32_t len,
+                            const char *body);
 static int handle_control_usefeature(control_connection_t *conn,
                                      uint32_t len,
                                      const char *body);
@@ -495,6 +498,15 @@
           continue;
       }
       if (control_conn->event_mask & (1<<event)) {
+        /* Is this conn interested in this particular resolve response?*/
+        if (event == EVENT_RESOLVE_RESPONSE){
+          if (!control_conn->resolve_cmd_pending)
+            continue;
+          if (strncasecmp(control_conn->resolve_cmd_pending, msg,
+                         strlen(control_conn->resolve_cmd_pending)))
+            continue;
+          tor_free(control_conn->resolve_cmd_pending);
+        }
         int is_err = 0;
         connection_write_to_buf(msg, strlen(msg), TO_CONN(control_conn));
         if (event == EVENT_ERR_MSG)
@@ -2212,6 +2224,51 @@
   return 0;
 }
 
+static int
+handle_control_resolve(control_connection_t *conn, uint32_t len,
+                            const char *body)
+{
+  smartlist_t *args;
+  (void) len; /* body is nul-terminated; it's safe to ignore the length */
+  char msg[512];
+
+  if (conn->resolve_cmd_pending){
+    connection_printf_to_buf(conn, "251 Resolve Already In Progress\r\n");
+    return 0;
+  }
+
+  args = smartlist_create();
+  smartlist_split_string(args, body, " ",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+  SMARTLIST_FOREACH(args, const char *, arg, {
+        tor_snprintf(msg, sizeof(msg),"650 RESOLVE %s", arg);
+        conn->resolve_cmd_pending = tor_strdup(msg);
+        dnsserv_server_control(arg);
+        break;
+  });
+
+  conn->event_mask |= (1 << EVENT_RESOLVE_RESPONSE);
+  control_update_global_event_mask();
+
+  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+  smartlist_free(args);
+
+  send_control_done(conn);
+  return 0;
+}
+
+int
+control_event_resolve_response(const char *question,const char *answer)
+{
+  if (!EVENT_IS_INTERESTING(EVENT_RESOLVE_RESPONSE))
+    return 0;
+
+  send_control_event(EVENT_RESOLVE_RESPONSE, ALL_NAMES,
+                            "650 RESOLVE %s:%s\r\n",
+                            question, answer);
+  return 0;
+}
+
 /** Called when we get a USEFEATURE command: parse the feature list, and
  * set up the control_connection's options properly. */
 static int
@@ -2442,6 +2499,9 @@
   } else if (!strcasecmp(conn->incoming_cmd, "USEFEATURE")) {
     if (handle_control_usefeature(conn, data_len, args))
       return -1;
+  } else if (!strcasecmp(conn->incoming_cmd, "RESOLVE")) {
+    if (handle_control_resolve(conn, data_len, args))
+      return -1;
   } else {
     connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n",
                              conn->incoming_cmd);
Index: src/or/or.h
===================================================================
--- src/or/or.h	(revision 10779)
+++ src/or/or.h	(working copy)
@@ -914,6 +914,9 @@
    * request that we're going to try to answer.  */
   struct evdns_server_request *dns_server_request;
 
+  /** Is this a DNS request from a controller? */
+  unsigned int control_resolve_request:1;
+
 } edge_connection_t;
 
 /** Subtype of connection_t for an "directory connection" -- that is, an HTTP
@@ -970,6 +973,7 @@
   uint32_t incoming_cmd_len;
   uint32_t incoming_cmd_cur_len;
   char *incoming_cmd;
+  char *resolve_cmd_pending;
   /* Used only by control v0 connections */
   uint16_t incoming_cmd_type;
 } control_connection_t;
@@ -2605,6 +2609,7 @@
 int control_event_stream_status(edge_connection_t *conn,
                                 stream_status_event_t e,
                                 int reason);
+int control_event_resolve_response(const char *question, const char *answer);
 int control_tls_error_to_reason(int e);
 int control_event_or_conn_status(or_connection_t *conn,
                                  or_conn_status_event_t e, int reason);
@@ -2801,6 +2806,8 @@
                       int ttl);
 void dnsserv_reject_request(edge_connection_t *conn);
 
+void dnsserv_server_control(const char *name);
+
 /********************************* hibernate.c **********************/
 
 int accounting_parse_options(or_options_t *options, int validate_only);
Index: src/or/dnsserv.c
===================================================================
--- src/or/dnsserv.c	(revision 10779)
+++ src/or/dnsserv.c	(working copy)
@@ -145,6 +145,79 @@
   tor_free(q_name);
 }
 
+/* Helper function: called whenever the client sends a resolve request to our
+ * controller.  We need to eventually answer the request <b>req</b>.
+ */
+void
+dnsserv_server_control(const char *name)
+{
+  edge_connection_t *conn;
+  struct evdns_server_request *server_req;
+  struct in_addr in;
+  char *q_name;
+  int i;
+  int is_ip_address;
+
+  /* Make a new dummy AP connection, and attach the request to it. */
+  conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP, AF_INET));
+  conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
+
+  is_ip_address = tor_inet_aton(name, &in);
+
+  if (!is_ip_address)
+    conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
+  else
+    conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
+
+  strlcpy(conn->socks_request->address, name,
+          sizeof(conn->socks_request->address));
+
+  server_req = malloc(sizeof(struct evdns_server_request));
+  if (server_req == NULL) return;
+  memset(server_req, 0, sizeof(struct evdns_server_request));
+
+  server_req->flags = 0;
+  server_req->nquestions = 0;
+
+  server_req->questions = malloc(sizeof(struct evdns_server_question *) * 1);
+  if (server_req->questions == NULL)
+    return;
+
+  for ( i = 0; i < 1; ++i) {
+    struct evdns_server_question *q;
+    int namelen;
+    namelen = strlen(name);
+    q = malloc(sizeof(struct evdns_server_question) + namelen);
+    if (!q)
+        return;
+    if (!is_ip_address)
+      q->type = EVDNS_TYPE_A;
+    else
+      q->type = EVDNS_TYPE_PTR;
+    q->class = EVDNS_CLASS_INET;
+    memcpy(q->name, name, namelen+1);
+    server_req->questions[server_req->nquestions++] = q;
+  }
+
+  conn->dns_server_request = server_req;
+  conn->control_resolve_request = 1;
+
+  connection_add(TO_CONN(conn));
+
+  /* Now, 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(name));
+  q_name = tor_strdup(name); /* q could be freed in rewrite_and_attach */
+  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.",
+           escaped_safe_str(q_name));
+  tor_free(q_name);
+}
+
 /** If there is a pending request on <b>conn</b> that's waiting for an answer,
  * send back an error and free the request. */
 void
@@ -181,27 +254,49 @@
    * 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;
+    if (conn->control_resolve_request)
+      control_event_resolve_response(req->questions[0]->name,
+                                      "IPv6 not implemented");
+    else {
+      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);
+    if (conn->control_resolve_request)
+      control_event_resolve_response(req->questions[0]->name,
+                                      tor_dup_addr(ntohl(get_uint32(answer))));
+    else
+      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,
+    if (conn->control_resolve_request)
+      control_event_resolve_response(req->questions[0]->name, answer);
+    else {
+      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);
+      tor_free(ans);
+    }
   } else if (answer_type == RESOLVED_TYPE_ERROR) {
-    err = DNS_ERR_NOTEXIST;
+    if (conn->control_resolve_request)
+      control_event_resolve_response(req->questions[0]->name, "Unknown Host");
+    else
+      err = DNS_ERR_NOTEXIST;
   } else { /* answer_type == RESOLVED_TYPE_ERROR_TRANSIENT */
-    err = DNS_ERR_SERVERFAILED;
+    if (conn->control_resolve_request)
+      control_event_resolve_response(req->questions[0]->name,
+                                      "Temporary Error");
+    else
+      err = DNS_ERR_SERVERFAILED;
   }
 
-  evdns_server_request_respond(req, err);
+  if (!conn->control_resolve_request)
+    evdns_server_request_respond(req, err);
+
   conn->dns_server_request = NULL;
 }