Browse Source

Allow servers with no hostname or IP address to learn their IP address
by asking the directory authorities. This code only kicks in when you
would normally have exited with a "no address" error.

This design is flawed, though, since the X-Your-Address-Is header is not
authenticated, and doing it this way introduces too many new attacks. The
right answer is to give IP address hints inside the HELLO cell; much of
this code can be reused when we switch.


svn:r6774

Roger Dingledine 19 years ago
parent
commit
9db7b2c068
4 changed files with 99 additions and 30 deletions
  1. 1 1
      src/or/config.c
  2. 17 10
      src/or/directory.c
  3. 2 0
      src/or/or.h
  4. 79 19
      src/or/router.c

+ 1 - 1
src/or/config.c

@@ -2129,7 +2129,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
     options->PublishServerDescriptor = 0;
   }
 
-  if (server_mode(options)) {
+  if (authdir_mode(options)) {
     /* confirm that our address isn't broken, so we can complain now */
     uint32_t tmp;
     if (resolve_my_address(LOG_WARN, options, &tmp, NULL) < 0)

+ 17 - 10
src/or/directory.c

@@ -58,6 +58,8 @@ static void note_request(const char *key, size_t bytes);
  * before deciding that one of us has the wrong time? */
 #define ALLOW_DIRECTORY_TIME_SKEW (30*60)
 
+#define X_ADDRESS_HEADER "X-Your-Address-Is: "
+
 /********* END VARIABLES ************/
 
 /** Return true iff the directory purpose 'purpose' must use an
@@ -386,6 +388,7 @@ directory_initiate_command(const char *address, uint32_t addr,
 
   /* give it an initial state */
   conn->state = DIR_CONN_STATE_CONNECTING;
+  conn->dirconn_direct = (private_connection == 0);
 
   if (!private_connection) {
     /* then we want to connect directly */
@@ -658,9 +661,8 @@ http_get_header(const char *headers, const char *which)
 }
 
 /** If <b>headers</b> indicates that a proxy was involved, then rewrite
- * <b>conn</b>-\>address to describe our best guess of the addresses
- * involved in this HTTP request. The format is either "1.2.3.4" or
- * "1.2.3.4 (forwarded for 5.6.7.8)". */
+ * <b>conn</b>-\>address to describe our best guess of the address that
+ * originated this HTTP request. */
 static void
 http_set_address_origin(const char *headers, connection_t *conn)
 {
@@ -670,13 +672,9 @@ http_set_address_origin(const char *headers, connection_t *conn)
   if (!fwd)
     fwd = http_get_header(headers, "X-Forwarded-For: ");
   if (fwd) {
-    size_t len = strlen(fwd)+strlen(conn->address)+32;
-    char *result = tor_malloc(len);
-    tor_snprintf(result, len, "%s (forwarded for %s)", conn->address,
-                 escaped(fwd));
-    tor_free(fwd);
     tor_free(conn->address);
-    conn->address = result;
+    conn->address = tor_strdup(escaped(fwd));
+    tor_free(fwd);
   }
 }
 
@@ -851,6 +849,15 @@ connection_dir_client_reached_eof(connection_t *conn)
             "Received response from directory server '%s:%d': %d %s",
             conn->address, conn->port, status_code, escaped(reason));
 
+  /* now check if it's got any hints for us about our IP address. */
+  if (server_mode(get_options())) {
+    char *guess = http_get_header(headers, X_ADDRESS_HEADER);
+    if (guess) {
+      router_new_address_suggestion(guess);
+      tor_free(guess);
+    }
+  }
+
   if (date_header > 0) {
     now = time(NULL);
     delta = now-date_header;
@@ -1264,7 +1271,7 @@ write_http_response_header(connection_t *conn, ssize_t length,
   cp = tmp;
   tor_snprintf(cp, sizeof(tmp),
                "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Type: %s\r\n"
-               "X-Your-Address-Is: %s\r\n",
+               X_ADDRESS_HEADER "%s\r\n",
                date, type, conn->address);
   cp += strlen(tmp);
   if (encoding) {

+ 2 - 0
src/or/or.h

@@ -691,6 +691,7 @@ struct connection_t {
 /* Used only by Dir connections */
   char *requested_resource; /**< Which 'resource' did we ask the directory
                              * for? */
+  unsigned int dirconn_direct:1; /**< Is this dirconn direct, or via Tor? */
 /* Used only for server sides of some dir connections. */
   enum {
     DIR_SPOOL_NONE=0, DIR_SPOOL_SERVER_BY_DIGEST, DIR_SPOOL_SERVER_BY_FP,
@@ -2274,6 +2275,7 @@ void mark_my_descriptor_dirty_if_older_than(time_t when);
 void mark_my_descriptor_dirty(void);
 void check_descriptor_bandwidth_changed(time_t now);
 void check_descriptor_ipaddress_changed(time_t now);
+void router_new_address_suggestion(const char *suggestion);
 int router_compare_to_my_exit_policy(connection_t *conn);
 routerinfo_t *router_get_my_routerinfo(void);
 const char *router_get_my_descriptor(void);

+ 79 - 19
src/or/router.c

@@ -736,9 +736,11 @@ router_get_my_descriptor(void)
 /*DOCDOC*/
 static smartlist_t *warned_nonexistent_family = NULL;
 
+static int router_guess_address_from_dir_headers(uint32_t *guess);
+
 /** If <b>force</b> is true, or our descriptor is out-of-date, rebuild
  * a fresh routerinfo and signed server descriptor for this OR.
- * Return 0 on success, -1 on error.
+ * Return 0 on success, -1 on temporary error.
  */
 int
 router_rebuild_descriptor(int force)
@@ -752,9 +754,14 @@ router_rebuild_descriptor(int force)
   if (desc_clean_since && !force)
     return 0;
 
-  if (resolve_my_address(LOG_WARN, options, &addr, NULL) < 0) {
-    log_warn(LD_CONFIG,"options->Address didn't resolve into an IP.");
-    return -1;
+  if (resolve_my_address(LOG_INFO, options, &addr, NULL) < 0) {
+    log_info(LD_CONFIG, "Could not determine our address locally. "
+             "Checking if directory headers provide any hints.");
+    if (router_guess_address_from_dir_headers(&addr) < 0) {
+      log_info(LD_CONFIG, "No hints from directory headers either. "
+               "Will try again later.");
+      return -1;
+    }
   }
 
   ri = tor_malloc_zero(sizeof(routerinfo_t));
@@ -894,6 +901,26 @@ check_descriptor_bandwidth_changed(time_t now)
   }
 }
 
+static void
+log_addr_has_changed(uint32_t prev, uint32_t cur)
+{
+  char addrbuf_prev[INET_NTOA_BUF_LEN];
+  char addrbuf_cur[INET_NTOA_BUF_LEN];
+  struct in_addr in_prev;
+  struct in_addr in_cur;
+
+  in_prev.s_addr = htonl(prev);
+  tor_inet_ntoa(&in_prev, addrbuf_prev, sizeof(addrbuf_prev));
+
+  in_cur.s_addr = htonl(cur);
+  tor_inet_ntoa(&in_cur, addrbuf_cur, sizeof(addrbuf_cur));
+
+  log_info(LD_GENERAL,
+           "Our IP Address has changed from %s to %s; "
+           "rebuilding descriptor.",
+           addrbuf_prev, addrbuf_cur);
+}
+
 /** Check whether our own address as defined by the Address configuration
  * has changed. This is for routers that get their address from a service
  * like dyndns. If our address has changed, mark our descriptor dirty. */
@@ -908,29 +935,62 @@ check_descriptor_ipaddress_changed(time_t now)
     return;
 
   prev = desc_routerinfo->addr;
-  if (resolve_my_address(LOG_WARN, options, &cur, NULL) < 0) {
-    log_warn(LD_CONFIG,"options->Address didn't resolve into an IP.");
+  if (resolve_my_address(LOG_INFO, options, &cur, NULL) < 0) {
+    log_info(LD_CONFIG,"options->Address didn't resolve into an IP.");
     return;
   }
 
   if (prev != cur) {
-    char addrbuf_prev[INET_NTOA_BUF_LEN];
-    char addrbuf_cur[INET_NTOA_BUF_LEN];
-    struct in_addr in_prev;
-    struct in_addr in_cur;
+    log_addr_has_changed(prev, cur);
+    mark_my_descriptor_dirty();
+    /* the above call is probably redundant, since resolve_my_address()
+     * probably already noticed and marked it dirty. */
+  }
+}
+
+static uint32_t last_guessed_ip = 0;
 
-    in_prev.s_addr = htonl(prev);
-    tor_inet_ntoa(&in_prev, addrbuf_prev, sizeof(addrbuf_prev));
+/** A directory authority told us our IP address is <b>suggestion</b>.
+ * If this address is different from the one we think we are now, and
+ * if our computer doesn't actually know its IP address, then switch. */
+void
+router_new_address_suggestion(const char *suggestion)
+{
+  uint32_t addr, cur;
+  struct in_addr in;
 
-    in_cur.s_addr = htonl(cur);
-    tor_inet_ntoa(&in_cur, addrbuf_cur, sizeof(addrbuf_cur));
+  /* first, learn what the IP address actually is */
+  if (!tor_inet_aton(suggestion, &in)) {
+    log_debug(LD_DIR, "Malformed X-Your-Address-Is header. Ignoring.");
+    return;
+  }
+  addr = ntohl(in.s_addr);
 
-    log_info(LD_GENERAL,
-             "Our IP Address has changed from %s to %s; "
-             "rebuilding descriptor.",
-             addrbuf_prev, addrbuf_cur);
-    mark_my_descriptor_dirty();
+  if (resolve_my_address(LOG_INFO, get_options(), &cur, NULL) >= 0) {
+    /* We're all set -- we already know our address. Great. */
+    last_guessed_ip = cur; /* store it in case we need it later */
+    return;
+  }
+
+  if (last_guessed_ip != addr) {
+    log_addr_has_changed(last_guessed_ip, addr);
+    server_has_changed_ip();
+    last_guessed_ip = addr; /* router_rebuild_descriptor() will fetch it */
+  }
+}
+
+/** We failed to resolve our address locally, but we'd like to build
+ * a descriptor and publish / test reachability. If we have a guess
+ * about our address based on directory headers, answer it and return
+ * 0; else return -1. */
+static int
+router_guess_address_from_dir_headers(uint32_t *guess)
+{
+  if (last_guessed_ip) {
+    *guess = last_guessed_ip;
+    return 0;
   }
+  return -1;
 }
 
 /** Set <b>platform</b> (max length <b>len</b>) to a NUL-terminated short