Browse Source

r8875@Kushana: nickm | 2006-09-21 16:46:28 -0400
Resolve bug 330: detect ISPs that want to hijack failing DNS requests and basically domain-squat the entire internet.


svn:r8440

Nick Mathewson 18 years ago
parent
commit
e4a9b4de4e
7 changed files with 142 additions and 6 deletions
  1. 5 1
      ChangeLog
  2. 2 2
      doc/TODO
  3. 9 0
      doc/tor.1.in
  4. 1 0
      src/or/config.c
  5. 107 3
      src/or/dns.c
  6. 15 0
      src/or/main.c
  7. 3 0
      src/or/or.h

+ 5 - 1
ChangeLog

@@ -1,6 +1,5 @@
 Changes in version 0.1.2.2-alpha - 2006-??-??
 Changes in version 0.1.2.2-alpha - 2006-??-??
   o Major features:
   o Major features:
-
     - Add server-side support for "reverse" DNS lookups (using PTR
     - Add server-side support for "reverse" DNS lookups (using PTR
       records so clients can determine the canonical hostname for a given
       records so clients can determine the canonical hostname for a given
       IPv4 address).  This has been specified for a long time, but was
       IPv4 address).  This has been specified for a long time, but was
@@ -8,6 +7,11 @@ Changes in version 0.1.2.2-alpha - 2006-??-??
       servers now announce in their descriptors whether they support
       servers now announce in their descriptors whether they support
       eventdns.
       eventdns.
 
 
+  o Minor features:
+    - Check for name servers (like Earthlink's) that hijack failing DNS
+      requests and replace the 'no such server' answer with a "helpful"
+      redirect to an advertising-driven search portal.  [Resolves bug 330.]
+
   o Security Fixes, minor
   o Security Fixes, minor
     - If a client asked for a server by name, and we didn't have a
     - If a client asked for a server by name, and we didn't have a
       descriptor for a named server with that name, we might return an old
       descriptor for a named server with that name, we might return an old

+ 2 - 2
doc/TODO

@@ -87,9 +87,9 @@ d     - Write limiting; separate token bucket for write
         - Write-limit directory responses (need to research)
         - Write-limit directory responses (need to research)
 
 
 N - DNS improvements
 N - DNS improvements
-    - Option to deal with broken DNS of the "ggoogle.com? Ah, you meant
+    o Option to deal with broken DNS of the "ggoogle.com? Ah, you meant
       ads.me.com!" variety.
       ads.me.com!" variety.
-d     - Autodetect whether DNS is broken in this way.
+      o Autodetect whether DNS is broken in this way.
     - Don't ask reject *:* nodes for DNS unless client wants you to.
     - Don't ask reject *:* nodes for DNS unless client wants you to.
     . Asynchronous DNS
     . Asynchronous DNS
       o Document and rename SearchDomains, ResolvConf options
       o Document and rename SearchDomains, ResolvConf options

+ 9 - 0
doc/tor.1.in

@@ -647,6 +647,15 @@ domain.  For example, if this system is configured to believe it is in
 connected to "www.example.com".
 connected to "www.example.com".
 This option only effects name lookup for addresses requested by clients.
 This option only effects name lookup for addresses requested by clients.
 (Defaults to "0".)
 (Defaults to "0".)
+.LP
+.TP
+\fBServerDNSDetectHijacking \fR\fB0\fR|\fB1\fR\fP
+When this option is set to 1, we will test periodically to determine whether
+our local nameservers have been configured to hijack failing DNS requests
+(usually to an advertising site).  If they are, we will attempt to correct
+this.  This option only effects name lookup for addresses requested by
+clients; and only takes effect if Tor was built with eventdns support.
+(Defaults to "1".)
 
 
 .SH DIRECTORY SERVER OPTIONS
 .SH DIRECTORY SERVER OPTIONS
 .PP
 .PP

+ 1 - 0
src/or/config.c

@@ -222,6 +222,7 @@ static config_var_t _option_vars[] = {
   VAR("RunTesting",          BOOL,     RunTesting,           "0"),
   VAR("RunTesting",          BOOL,     RunTesting,           "0"),
   VAR("SafeLogging",         BOOL,     SafeLogging,          "1"),
   VAR("SafeLogging",         BOOL,     SafeLogging,          "1"),
   VAR("SafeSocks",           BOOL,     SafeSocks,            "0"),
   VAR("SafeSocks",           BOOL,     SafeSocks,            "0"),
+  VAR("ServerDNSDetectHijacking",BOOL,   ServerDNSDetectHijacking,"1"),
   VAR("ServerDNSResolvConfFile", STRING, ServerDNSResolvConfFile, NULL),
   VAR("ServerDNSResolvConfFile", STRING, ServerDNSResolvConfFile, NULL),
   VAR("ServerDNSSearchDomains",  BOOL,   ServerDNSSearchDomains,  "0"),
   VAR("ServerDNSSearchDomains",  BOOL,   ServerDNSSearchDomains,  "0"),
   VAR("ShutdownWaitLength",  INTERVAL, ShutdownWaitLength,   "30 seconds"),
   VAR("ShutdownWaitLength",  INTERVAL, ShutdownWaitLength,   "30 seconds"),

+ 107 - 3
src/or/dns.c

@@ -122,6 +122,7 @@ static int spawn_dnsworker(void);
 static int spawn_enough_dnsworkers(void);
 static int spawn_enough_dnsworkers(void);
 #else
 #else
 static int configure_nameservers(int force);
 static int configure_nameservers(int force);
+static int answer_is_wildcarded(const char *ip);
 #endif
 #endif
 #ifdef DEBUG_DNS_CACHE
 #ifdef DEBUG_DNS_CACHE
 static void _assert_cache_ok(void);
 static void _assert_cache_ok(void);
@@ -1330,6 +1331,11 @@ spawn_enough_dnsworkers(void)
 
 
   return 0;
   return 0;
 }
 }
+
+void
+dns_launch_wildcard_checks(void)
+{
+}
 #else /* !USE_EVENTDNS */
 #else /* !USE_EVENTDNS */
 
 
 /** Eventdns helper: return true iff the eventdns result <b>err</b> is
 /** Eventdns helper: return true iff the eventdns result <b>err</b> is
@@ -1470,9 +1476,19 @@ eventdns_callback(int result, char type, int count, int ttl, void *addresses,
       status = DNS_RESOLVE_SUCCEEDED;
       status = DNS_RESOLVE_SUCCEEDED;
       tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
       tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
       escaped_address = esc_for_log(string_address);
       escaped_address = esc_for_log(string_address);
-      log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
-                safe_str(escaped_address),
-                escaped_safe_str(answer_buf));
+
+      if (answer_is_wildcarded(answer_buf)) {
+        log_debug(LD_EXIT, "eventdns said that %s resolves to ISP-hijacked "
+                  "address %s; treating as a failure.",
+                  safe_str(escaped_address),
+                  escaped_safe_str(answer_buf));
+        addr = 0;
+        status = DNS_RESOLVE_FAILED_PERMANENT;
+      } else {
+        log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
+                  safe_str(escaped_address),
+                  escaped_safe_str(answer_buf));
+      }
       tor_free(escaped_address);
       tor_free(escaped_address);
     } else if (type == DNS_PTR && count) {
     } else if (type == DNS_PTR && count) {
       char *escaped_address;
       char *escaped_address;
@@ -1546,6 +1562,94 @@ launch_resolve(edge_connection_t *exitconn)
   }
   }
   return r ? -1 : 0;
   return r ? -1 : 0;
 }
 }
+
+/** If present, a list of dotted-quad IP addresses that our nameserver
+ * apparently wants to return in response to requests for nonexistent domains.
+ */
+static smartlist_t *dns_wildcard_list = NULL;
+
+/** Callback function when we get an answer (possibly failing) for a request
+ * for a (hopefully) nonexistent domain. */
+static void
+eventdns_wildcard_check_callback(int result, char type, int count, int ttl,
+                                 void *addresses, void *arg)
+{
+  static int notice_given = 0;
+  if (result == DNS_ERR_NONE && type == DNS_IPv4_A && count) {
+    uint32_t *addrs = addresses;
+    int i;
+    char *string_address = arg;
+    if (!dns_wildcard_list) dns_wildcard_list = smartlist_create();
+    for (i = 0; i < count; ++i) {
+      char answer_buf[INET_NTOA_BUF_LEN+1];
+      struct in_addr in;
+      in.s_addr = addrs[i];
+      tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
+      if (!smartlist_string_isin(dns_wildcard_list, answer_buf))
+        smartlist_add(dns_wildcard_list, tor_strdup(answer_buf));
+    }
+    log(notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
+        "Your DNS provider gave an answer for \"%s\", which "
+        "is not supposed to exist.  Apparently they are hijacking "
+        "DNS failures. Trying to correct for this.  We've noticed %d bad "
+        "addresses so far.", string_address, smartlist_len(dns_wildcard_list));
+    notice_given = 1;
+  }
+  tor_free(arg);
+}
+
+/** Launch a single request for a nonexistent hostname consisting of
+ * <b>len</b> random (plausible) characters followed by <b>suffix</b> */
+static void
+launch_wildcard_check(int len, const char *suffix)
+{
+  char random_bytes[16], name[64], *addr;
+  size_t n = (len+1)/2;
+  int r;
+
+  tor_assert(n <= sizeof(random_bytes));
+
+  if (crypto_rand(random_bytes, n) < 0)
+    return;
+  base32_encode(name, sizeof(name), random_bytes, n);
+  name[len] = '\0';
+  strlcat(name, suffix, sizeof(name));
+
+  addr = tor_strdup(name);
+  r = eventdns_resolve_ipv4(name, DNS_QUERY_NO_SEARCH,
+                            eventdns_wildcard_check_callback, addr);
+  if (r)
+    tor_free(addr);
+}
+
+#define N_WILDCARD_CHECKS 2
+
+/** Launch DNS requests for a few nonexistent hostnames, and see if we can
+ * catch our nameserver trying to hijack them and map them to a stupid "I
+ * couldn't find ggoogle.com but maybe you'd like to buy these lovely
+ * encyclopedias" page. */
+void
+dns_launch_wildcard_checks(void)
+{
+  int i;
+  if (!get_options()->ServerDNSDetectHijacking)
+    return;
+  log_info(LD_EXIT, "Launching checks to see whether our nameservers like "
+           "to hijack DNS failures.");
+  for (i = 0; i < N_WILDCARD_CHECKS; ++i) {
+    /* RFC2606 reserves these */
+    launch_wildcard_check(8, ".invalid");
+    launch_wildcard_check(8, ".test");
+  }
+}
+
+/** Return true iff we have noticed that the dotted-quad <b>ip</b> has been
+ * returned in response to a request for a nonexistent hostname. */
+static int
+answer_is_wildcarded(const char *ip)
+{
+  return dns_wildcard_list && smartlist_string_isin(dns_wildcard_list, ip);
+}
 #endif /* USE_EVENTDNS */
 #endif /* USE_EVENTDNS */
 
 
 /** Exit with an assertion if <b>resolve</b> is corrupt. */
 /** Exit with an assertion if <b>resolve</b> is corrupt. */

+ 15 - 0
src/or/main.c

@@ -722,6 +722,7 @@ run_scheduled_events(time_t now)
   static time_t time_to_try_getting_descriptors = 0;
   static time_t time_to_try_getting_descriptors = 0;
   static time_t time_to_reset_descriptor_failures = 0;
   static time_t time_to_reset_descriptor_failures = 0;
   static time_t time_to_add_entropy = 0;
   static time_t time_to_add_entropy = 0;
+  static time_t time_to_check_for_wildcarded_dns = 0;
   or_options_t *options = get_options();
   or_options_t *options = get_options();
   int i;
   int i;
   int have_dir_info;
   int have_dir_info;
@@ -923,6 +924,20 @@ run_scheduled_events(time_t now)
    * we'll pass it to poll/select and bad things will happen.
    * we'll pass it to poll/select and bad things will happen.
    */
    */
   close_closeable_connections();
   close_closeable_connections();
+
+  /** 9. and if we're a server, check whether our DNS is telling stories to
+   * us. */
+  if (server_mode(options) && time_to_check_for_wildcarded_dns < now) {
+    if (!time_to_check_for_wildcarded_dns) {
+      time_to_check_for_wildcarded_dns = now + 60 + crypto_rand_int(120);
+    } else {
+      dns_launch_wildcard_checks();
+      time_to_check_for_wildcarded_dns = now + 12*3600 +
+        crypto_rand_int(12*3600);
+    }
+  }
+
+
 }
 }
 
 
 static struct event *timeout_event = NULL;
 static struct event *timeout_event = NULL;

+ 3 - 0
src/or/or.h

@@ -1574,6 +1574,8 @@ typedef struct {
   int ServerDNSSearchDomains; /**< Boolean: If set, we don't force exit
   int ServerDNSSearchDomains; /**< Boolean: If set, we don't force exit
                       * addresses to be FQDNs, but rather search for them in
                       * addresses to be FQDNs, but rather search for them in
                       * the local domains. */
                       * the local domains. */
+  int ServerDNSDetectHijacking; /**< Boolean: If true, check for DNS failure
+                                 * hijacking */
   char *ServerDNSResolvConfFile; /**< If provided, we configure our internal
   char *ServerDNSResolvConfFile; /**< If provided, we configure our internal
                      * resolver from the file here rather than from
                      * resolver from the file here rather than from
                      * /etc/resolv.conf (unix) or the registry (windows) */
                      * /etc/resolv.conf (unix) or the registry (windows) */
@@ -2158,6 +2160,7 @@ void assert_connection_edge_not_dns_pending(edge_connection_t *conn);
 void assert_all_pending_dns_resolves_ok(void);
 void assert_all_pending_dns_resolves_ok(void);
 void dns_cancel_pending_resolve(const char *question);
 void dns_cancel_pending_resolve(const char *question);
 int dns_resolve(edge_connection_t *exitconn);
 int dns_resolve(edge_connection_t *exitconn);
+void dns_launch_wildcard_checks(void);
 
 
 /********************************* hibernate.c **********************/
 /********************************* hibernate.c **********************/