[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 Monday 02 July 2007 22:21:12 Robert Hogan wrote:
> On Wednesday 27 June 2007 17:20:06 Nick Mathewson wrote:
> <snip>
>
> > I think the approach you describe above is okay.  It shouldn't break
> > backward compatibility, since preexisting controllers will never issue
> > RESOLVE commands, and so will never get "650 RESOLVE" events.
>
> Hi Nick,
>
> Patch attached. It supports host and reverse lookups.
>
> Sample output:
>
> resolve hotmail.com
> 250 OK
> 650 RESOLVE hotmail.com:64.4.32.7
> resolve 64.4.32.7
> 250 OK
> 650 RESOLVE 64.4.32.7:lc1.bay0.hotmail.com
>
> Let me know what needs cleaning up/reworking.

For a start it didn't handle unknown hostnames, or any other error conditions. 
Nor did it respect the indentation in dnsserv.c. The attached does.

I haven't included a write-up for controlspec.txt.

-- 

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 10722)
+++ 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);
@@ -2212,6 +2215,38 @@
   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 */
+  args = smartlist_create();
+  smartlist_split_string(args, body, " ",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+  SMARTLIST_FOREACH(args, const char *, arg, {
+        evdns_server_control(arg);
+  });
+
+  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
+handle_control_resolve_response(const char *question,const char *answer)
+{
+  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 +2477,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/connection_edge.c
===================================================================
--- src/or/connection_edge.c	(revision 10722)
+++ src/or/connection_edge.c	(working copy)
@@ -1189,7 +1189,7 @@
             safe_str(socks->address),
             socks->port);
 
-  if (socks->command == SOCKS_COMMAND_RESOLVE &&
+  if ((SOCKS_COMMAND_IS_RESOLVE_A(socks->command)) &&
       !tor_inet_aton(socks->address, &addr_tmp) &&
       options->AutomapHostsOnResolve && options->AutomapHostsSuffixes) {
     SMARTLIST_FOREACH(options->AutomapHostsSuffixes, const char *, cp,
@@ -1208,7 +1208,7 @@
     }
   }
 
-  if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) {
+  if (SOCKS_COMMAND_IS_RESOLVE_PTR(socks->command)) {
     if (addressmap_rewrite_reverse(socks->address, sizeof(socks->address))) {
       char *result = tor_strdup(socks->address);
       /* remember _what_ is supposed to have been resolved. */
@@ -1299,7 +1299,7 @@
       return -1;
     }
 
-    if (socks->command == SOCKS_COMMAND_RESOLVE) {
+    if (SOCKS_COMMAND_IS_RESOLVE_A(socks->command)) {
       uint32_t answer;
       struct in_addr in;
       /* Reply to resolves immediately if we can. */
@@ -1355,7 +1355,7 @@
 
       /* help predict this next time */
       rep_hist_note_used_port(socks->port, time(NULL));
-    } else if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) {
+    } else if (SOCKS_COMMAND_IS_RESOLVE_PTR(socks->command)) {
       rep_hist_note_used_resolve(time(NULL)); /* help predict this next time */
     } else if (socks->command == SOCKS_COMMAND_CONNECT_DIR) {
       ; /* nothing */
@@ -1844,7 +1844,7 @@
     return -1;
   }
 
-  if (command == SOCKS_COMMAND_RESOLVE) {
+  if (SOCKS_COMMAND_IS_RESOLVE_A(command)) {
     string_addr = ap_conn->socks_request->address;
     payload_len = strlen(string_addr)+1;
     tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
@@ -2542,7 +2542,7 @@
       return 0;
   } else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
     /* Can't support reverse lookups without eventdns. */
-    if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR &&
+    if (SOCKS_COMMAND_IS_RESOLVE_PTR(conn->socks_request->command) &&
         exit->has_old_dnsworkers)
       return 0;
 
Index: src/or/or.h
===================================================================
--- src/or/or.h	(revision 10722)
+++ src/or/or.h	(working copy)
@@ -2095,6 +2095,11 @@
 #define SOCKS_COMMAND_RESOLVE       0xF0
 /** Please turn this IP address into an FQDN, privately. */
 #define SOCKS_COMMAND_RESOLVE_PTR   0xF1
+/** Please turn this FQDN into an IP address, privately. */
+#define SOCKS_COMMAND_RESOLVE_CONTROL       0xF4
+/** Please turn this IP address into an FQDN, privately. */
+#define SOCKS_COMMAND_RESOLVE_PTR_CONTROL   0xF5
+
 /** Please open an encrypted direct TCP connection to the directory port
  * of the Tor server specified by address:port. (In this case address:port
  * specifies the ORPort of the server.) */
@@ -2103,8 +2108,20 @@
 #define SOCKS_COMMAND_IS_CONNECT(c) ((c)==SOCKS_COMMAND_CONNECT || \
                                      (c)==SOCKS_COMMAND_CONNECT_DIR)
 #define SOCKS_COMMAND_IS_RESOLVE(c) ((c)==SOCKS_COMMAND_RESOLVE || \
-                                     (c)==SOCKS_COMMAND_RESOLVE_PTR)
+                                     (c)==SOCKS_COMMAND_RESOLVE_PTR || \
+                                     (c)==SOCKS_COMMAND_RESOLVE_CONTROL || \
+                                     (c)==SOCKS_COMMAND_RESOLVE_PTR_CONTROL)
 
+#define SOCKS_COMMAND_IS_RESOLVE_A(c) \
+                                    ((c)==SOCKS_COMMAND_RESOLVE || \
+                                     (c)==SOCKS_COMMAND_RESOLVE_CONTROL)
+#define SOCKS_COMMAND_IS_RESOLVE_PTR(c) \
+                                    ((c)==SOCKS_COMMAND_RESOLVE_PTR || \
+                                     (c)==SOCKS_COMMAND_RESOLVE_PTR_CONTROL)
+#define SOCKS_COMMAND_IS_RESOLVE_CONTROL(c) \
+                                    ((c)==SOCKS_COMMAND_RESOLVE_CONTROL || \
+                                     (c)==SOCKS_COMMAND_RESOLVE_PTR_CONTROL)
+
 /** State of a SOCKS request from a user to an OP.  Also used to encode other
  * information for non-socks user request (such as those on TransPort and
  * DNSPort) */
@@ -2605,6 +2622,7 @@
 int control_event_stream_status(edge_connection_t *conn,
                                 stream_status_event_t e,
                                 int reason);
+int handle_control_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);
@@ -2794,6 +2812,7 @@
 
 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,
@@ -2801,6 +2820,8 @@
                       int ttl);
 void dnsserv_reject_request(edge_connection_t *conn);
 
+void evdns_server_control(const char *name);
+
 /********************************* hibernate.c **********************/
 
 int accounting_parse_options(or_options_t *options, int validate_only);
Index: src/or/buffers.c
===================================================================
--- src/or/buffers.c	(revision 10722)
+++ src/or/buffers.c	(working copy)
@@ -1157,7 +1157,9 @@
       if (req->command != SOCKS_COMMAND_CONNECT &&
           req->command != SOCKS_COMMAND_CONNECT_DIR &&
           req->command != SOCKS_COMMAND_RESOLVE &&
-          req->command != SOCKS_COMMAND_RESOLVE_PTR) {
+          req->command != SOCKS_COMMAND_RESOLVE_CONTROL &&
+          req->command != SOCKS_COMMAND_RESOLVE_PTR &&
+          req->command != SOCKS_COMMAND_RESOLVE_PTR_CONTROL) {
         /* not a connect or resolve or a resolve_ptr? we don't support it. */
         log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.",
                  req->command);
@@ -1182,7 +1184,7 @@
           strlcpy(req->address,tmpbuf,sizeof(req->address));
           req->port = ntohs(*(uint16_t*)(buf->cur+8));
           buf_remove_from_front(buf, 10);
-          if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
+          if (!SOCKS_COMMAND_IS_RESOLVE_PTR(req->command) &&
               !addressmap_have_mapping(req->address) &&
               !have_warned_about_unsafe_socks) {
             log_warn(LD_APP,
@@ -1203,7 +1205,7 @@
           return 1;
         case 3: /* fqdn */
           log_debug(LD_APP,"socks5: fqdn address type");
-          if (req->command == SOCKS_COMMAND_RESOLVE_PTR) {
+          if (SOCKS_COMMAND_IS_RESOLVE_PTR(req->command)) {
             log_warn(LD_APP, "socks5 received RESOLVE_PTR command with "
                      "hostname type. Rejecting.");
             return -1;
@@ -1251,6 +1253,7 @@
       req->command = (unsigned char) *(buf->cur+1);
       if (req->command != SOCKS_COMMAND_CONNECT &&
           req->command != SOCKS_COMMAND_CONNECT_DIR &&
+          req->command != SOCKS_COMMAND_RESOLVE_CONTROL &&
           req->command != SOCKS_COMMAND_RESOLVE) {
         /* not a connect or resolve? we don't support it. (No resolve_ptr with
          * socks4.) */
@@ -1261,7 +1264,8 @@
 
       req->port = ntohs(*(uint16_t*)(buf->cur+2));
       destip = ntohl(*(uint32_t*)(buf->mem+4));
-      if ((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) {
+      if ((!req->port && !SOCKS_COMMAND_IS_RESOLVE_A(req->command))
+           || !destip) {
         log_warn(LD_APP,"socks4: Port or DestIP is zero. Rejecting.");
         return -1;
       }
Index: src/or/dnsserv.c
===================================================================
--- src/or/dnsserv.c	(revision 10722)
+++ src/or/dnsserv.c	(working copy)
@@ -145,6 +145,78 @@
   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
+evdns_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_CONTROL;
+  else
+    conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR_CONTROL;
+
+  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;
+
+  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 +253,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 (SOCKS_COMMAND_IS_RESOLVE_CONTROL(conn->socks_request->command))
+      handle_control_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);
+             SOCKS_COMMAND_IS_RESOLVE_A(conn->socks_request->command)) {
+    if (SOCKS_COMMAND_IS_RESOLVE_CONTROL(conn->socks_request->command))
+      handle_control_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,
+             (SOCKS_COMMAND_IS_RESOLVE_PTR(conn->socks_request->command))) {
+    if (SOCKS_COMMAND_IS_RESOLVE_CONTROL(conn->socks_request->command))
+      handle_control_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 (SOCKS_COMMAND_IS_RESOLVE_CONTROL(conn->socks_request->command))
+      handle_control_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 (SOCKS_COMMAND_IS_RESOLVE_CONTROL(conn->socks_request->command))
+      handle_control_resolve_response(req->questions[0]->name,
+                                      "Temporary Error");
+    else
+      err = DNS_ERR_SERVERFAILED;
   }
 
-  evdns_server_request_respond(req, err);
+  if (!SOCKS_COMMAND_IS_RESOLVE_CONTROL(conn->socks_request->command))
+    evdns_server_request_respond(req, err);
+
   conn->dns_server_request = NULL;
 }