Explorar o código

Write necessary backends for online directory generation. I think.

svn:r503
Nick Mathewson %!s(int64=20) %!d(string=hai) anos
pai
achega
0e0169d6fa
Modificáronse 8 ficheiros con 509 adicións e 291 borrados
  1. 2 2
      src/or/Makefile.am
  2. 2 0
      src/or/connection.c
  3. 1 0
      src/or/connection_or.c
  4. 7 29
      src/or/directory.c
  5. 387 0
      src/or/dirserv.c
  6. 29 118
      src/or/main.c
  7. 23 8
      src/or/or.h
  8. 58 134
      src/or/routers.c

+ 2 - 2
src/or/Makefile.am

@@ -5,14 +5,14 @@ noinst_PROGRAMS = test
 bin_PROGRAMS = tor
 
 tor_SOURCES = buffers.c circuit.c command.c connection.c \
-             connection_or.c config.c \
+             connection_or.c config.c dirserv.c \
              onion.c routers.c directory.c dns.c connection_edge.c \
              cpuworker.c main.c tor_main.c
 
 tor_LDADD = ../common/libor.a
 
 test_SOURCES = buffers.c circuit.c command.c connection.c \
-             connection_or.c config.c \
+             connection_or.c config.c dirserv.c \
              onion.c routers.c directory.c dns.c connection_edge.c \
              cpuworker.c main.c test.c
 

+ 2 - 0
src/or/connection.c

@@ -110,6 +110,8 @@ void connection_free(connection_t *conn) {
     crypto_free_pk_env(conn->link_pkey);
   if (conn->identity_pkey)
     crypto_free_pk_env(conn->identity_pkey);
+  if (conn->nickname) 
+    free(conn->nickname);
 
   if(conn->s > 0) {
     log_fn(LOG_INFO,"closing fd %d.",conn->s);

+ 1 - 0
src/or/connection_or.c

@@ -81,6 +81,7 @@ void connection_or_init_conn_from_router(connection_t *conn, routerinfo_t *route
   conn->onion_pkey = crypto_pk_dup_key(router->onion_pkey);
   conn->link_pkey = crypto_pk_dup_key(router->link_pkey);
   conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey);
+  conn->nickname = strdup(router->nickname);
   if(conn->address)
     free(conn->address);
   conn->address = strdup(router->address);

+ 7 - 29
src/or/directory.c

@@ -4,22 +4,17 @@
 
 #include "or.h"
 
-#define MAX_DIR_SIZE 50000 /* XXX, big enough? */
-
 static int directory_send_command(connection_t *conn, int command);
-static void directory_rebuild(void);
 static int directory_handle_command(connection_t *conn);
 
 /********* START VARIABLES **********/
 
 extern or_options_t options; /* command-line and config-file options */
 
-static char the_directory[MAX_DIR_SIZE+1];
-static int directorylen=0;
-static int directory_dirty=1;
-
 static char fetchstring[] = "GET / HTTP/1.0\r\n\r\n";
 static char answerstring[] = "HTTP/1.0 200 OK\r\n\r\n";
+static char the_directory[MAX_DIR_SIZE+1];
+static int directorylen=0;
 
 /********* END VARIABLES ************/
 
@@ -115,25 +110,6 @@ static int directory_send_command(connection_t *conn, int command) {
   return 0;
 }
 
-void directory_set_dirty(void) {
-  directory_dirty = 1;
-}
-
-static void directory_rebuild(void) {
-  if(directory_dirty) {
-    if (dump_signed_directory_to_string(the_directory, MAX_DIR_SIZE,
-                                        get_identity_key())) {
-      log(LOG_WARNING, "Error creating directory");
-      return;
-    }
-    directorylen = strlen(the_directory);
-    log(LOG_INFO,"New directory (size %d):\n%s",directorylen,the_directory);
-    directory_dirty = 0;
-  } else {
-    log(LOG_INFO,"Directory still clean, reusing.");
-  }
-}
-
 int connection_dir_process_inbuf(connection_t *conn) {
 
   assert(conn && conn->type == CONN_TYPE_DIR);
@@ -191,6 +167,8 @@ int connection_dir_process_inbuf(connection_t *conn) {
 static int directory_handle_command(connection_t *conn) {
   char headers[1024];
   char body[50000]; /* XXX */
+  size_t dl;
+  const char *cp;
 
   assert(conn && conn->type == CONN_TYPE_DIR);
 
@@ -209,16 +187,16 @@ static int directory_handle_command(connection_t *conn) {
   if(!strncasecmp(headers,"GET",3)) {
     /* XXX should check url and http version */
 
-    directory_rebuild(); /* rebuild it now, iff it's dirty */
+    dl = dirserv_get_directory(&cp);
 
-    if(directorylen == 0) {
+    if(dl == 0) {
       log_fn(LOG_WARNING,"My directory is empty. Closing.");
       return -1;
     }
 
     log_fn(LOG_DEBUG,"Dumping directory to client."); 
     if((connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) ||
-       (connection_write_to_buf(the_directory, directorylen, conn) < 0)) {
+       (connection_write_to_buf(cp, dl, conn) < 0)) {
       log_fn(LOG_WARNING,"Failed to write answerstring+directory to outbuf.");
       return -1;
     }

+ 387 - 0
src/or/dirserv.c

@@ -0,0 +1,387 @@
+/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
+/* See LICENSE for licensing information */
+/* $Id$ */
+
+#include "or.h"
+
+static int the_directory_is_dirty = 1;
+static char *the_directory = NULL;
+static int the_directory_len = -1;
+
+/*
+ *    Fingerprint handling code.
+ */
+typedef struct fingerprint_entry_t {
+  char *nickname;
+  char *fingerprint;
+} fingerprint_entry_t;
+
+static fingerprint_entry_t fingerprint_list[MAX_ROUTERS_IN_DIR];
+static int n_fingerprints = 0;
+
+/* return 0 on success, -1 on failure */
+int 
+dirserv_parse_fingerprint_file(const char *fname)
+{
+  FILE *file;
+#define BUF_LEN (FINGERPRINT_LEN+MAX_NICKNAME_LEN+20)
+  char buf[BUF_LEN+1];
+  char *cp, *nickname, *fingerprint;
+  fingerprint_entry_t fingerprint_list_tmp[MAX_ROUTERS_IN_DIR];
+  int n_fingerprints_tmp = 0;
+  int lineno=0;
+  int i;
+  if (!(file = fopen(fname, "r"))) {
+    log(LOG_WARNING, "Cannot open fingerprint file %s", fname);
+    goto err;
+  }
+  while (1) {
+    cp = fgets(buf, BUF_LEN, file);
+    ++lineno;
+    if (!cp) {
+      if (feof(file))
+        break;
+      else {
+        log(LOG_WARNING, "Error reading from fingerprint file");
+        goto err;
+      }
+    }
+    buf[BUF_LEN]='\0';
+    cp = buf;
+    while (isspace(*cp))
+      ++cp;
+    if (*cp == '#' || *cp == '\0') 
+      continue;
+    nickname = cp;
+    cp = strchr(cp, ' ');
+    if (!cp) {
+      log(LOG_WARNING, "Bad line %d of fingerprint file", lineno);
+      goto err;
+    }
+    *cp++ = '\0';
+    while (isspace(*cp))
+      ++cp;
+    if (strlen(cp) < FINGERPRINT_LEN) {
+      log(LOG_WARNING, "Bad line %d of fingerprint file", lineno);
+      goto err;
+    }
+    fingerprint = cp;
+    cp[FINGERPRINT_LEN] = '\0';
+    if (strlen(nickname) > MAX_NICKNAME_LEN) {
+      log(LOG_WARNING, "Nickname too long on line %d of fingerprint file",
+          lineno);
+      goto err;
+    }
+    if (!crypto_pk_check_fingerprint_syntax(fingerprint)) {
+      log(LOG_WARNING, "Invalid fingerprint on line %d of fingerprint file",
+          lineno);
+      goto err;
+    }
+    for (i = 0; i < n_fingerprints_tmp; ++i) {
+      if (0==strcasecmp(fingerprint_list_tmp[i].nickname, nickname)) {
+        log(LOG_WARNING, "Duplicate nickname on line %d of fingerprint file", lineno);
+        goto err;
+      }
+    }
+    fingerprint_list_tmp[n_fingerprints_tmp].nickname = strdup(nickname);
+    fingerprint_list_tmp[n_fingerprints_tmp].fingerprint = strdup(fingerprint);
+    ++n_fingerprints_tmp;
+  }
+  /* replace the global fingerprints list. */
+  dirserv_free_fingerprint_list();
+  memcpy(fingerprint_list, fingerprint_list_tmp, 
+         sizeof(fingerprint_entry_t)*n_fingerprints_tmp);
+  n_fingerprints = n_fingerprints_tmp;
+  return 0; 
+
+ err:
+  for (i = 0; i < n_fingerprints_tmp; ++i) {
+    free(fingerprint_list_tmp[i].nickname);
+    free(fingerprint_list_tmp[i].fingerprint);
+  }
+  return -1;
+#undef BUF_LEN
+}    
+
+/* return 1 if router's identity and nickname match. */
+int
+dirserv_router_fingerprint_is_known(const routerinfo_t *router)
+{
+  int i;
+  fingerprint_entry_t *ent =NULL;
+  char fp[FINGERPRINT_LEN+1];
+
+  for (i=0;i<n_fingerprints;++i) {
+    if (!strcasecmp(router->nickname,fingerprint_list[i].nickname)) {
+      ent = &fingerprint_list[i];
+      break;
+    }
+  }
+  
+  if (!ent) {
+    /* No such server known */
+    return 0;
+  }
+  if (crypto_pk_get_fingerprint(router->identity_pkey, fp)) {
+    /* XXX Error computing fingerprint: log */
+    return 0;
+  }
+  if (0==strcasecmp(ent->fingerprint, fp)) {
+    /* Right fingerprint. */
+    return 1;
+  } else {
+    /* Wrong fingerprint. */
+    return 0;
+  }
+}
+
+void 
+dirserv_free_fingerprint_list()
+{
+  int i;
+  for (i = 0; i < n_fingerprints; ++i) {
+    free(fingerprint_list[i].nickname);
+    free(fingerprint_list[i].fingerprint);
+  }
+  n_fingerprints = 0;
+}
+
+/*
+ *    Descriptor list
+ */
+typedef struct descriptor_entry_t {
+  char *nickname;
+  time_t published;
+  size_t desc_len;
+  char *descriptor;
+} descriptor_entry_t;
+
+static descriptor_entry_t *descriptor_list[MAX_ROUTERS_IN_DIR];
+static int n_descriptors = 0;
+
+static void free_descriptor_entry(descriptor_entry_t *desc)
+{
+  if (desc->descriptor)
+    free(desc->descriptor);
+  if (desc->nickname)
+    free(desc->nickname);
+  free(desc);
+}
+
+void 
+dirserv_free_descriptors()
+{
+  int i;
+  for (i = 0; i < n_descriptors; ++i) {
+    free_descriptor_entry(descriptor_list[i]);
+  }
+  n_descriptors = 0;
+}
+
+/* Return 0 if descriptor added; -1 if descriptor rejected.  Updates *desc
+ * to point after the descriptor if the descriptor is OK.
+ */
+int
+dirserv_add_descriptor(const char **desc)
+{
+  descriptor_entry_t **desc_ent_ptr;
+  routerinfo_t *ri = NULL;
+  int i;
+  char *start, *end;
+  char *desc_tmp = NULL;
+  size_t desc_len;
+
+  start = strstr(*desc, "router ");
+  if (!start) {
+    log(LOG_WARNING, "no descriptor found.");
+    goto err;
+  }
+  end = strstr(start+6, "\nrouter ");
+  if (end) {
+    ++end; /* Include NL. */
+  } else {
+    end = start+strlen(start);
+  }
+  desc_len = end-start;
+  desc_tmp = tor_malloc(desc_len+1);
+  strncpy(desc_tmp, start, desc_len);
+  desc_tmp[desc_len]='\0';
+
+  /* Check: is the descriptor syntactically valid? */
+  ri = router_get_entry_from_string(&desc_tmp);
+  if (!ri) {
+    log(LOG_WARNING, "Couldn't parse descriptor");
+    goto err;
+  }
+  free(desc_tmp); desc_tmp = NULL;
+  /* Okay.  Now check whether the fingerprint is recognized. */
+  if (!dirserv_router_fingerprint_is_known(ri)) {
+    log(LOG_WARNING, "Identity is unrecognized for descriptor");
+    goto err;
+  }
+  /* Do we already have an entry for this router? */
+  desc_ent_ptr = NULL;
+  for (i = 0; i < n_descriptors; ++i) {
+    if (!strcasecmp(ri->nickname, descriptor_list[i]->nickname)) {
+      desc_ent_ptr = &descriptor_list[i];
+      break;
+    }
+  }
+  if (desc_ent_ptr) {
+    /* if so, decide whether to update it. */
+    if ((*desc_ent_ptr)->published > ri->published_on) {
+      /* We already have a newer descriptor */
+      goto err;
+    }
+    /* We don't have a newer one; we'll update this one. */
+    free_descriptor_entry(*desc_ent_ptr);
+  } else {
+    /* Add this at the end. */
+    desc_ent_ptr = &descriptor_list[n_descriptors++];
+  }
+  
+  (*desc_ent_ptr) = tor_malloc(sizeof(descriptor_entry_t));
+  (*desc_ent_ptr)->nickname = ri->nickname;
+  (*desc_ent_ptr)->published = ri->published_on;
+  (*desc_ent_ptr)->desc_len = desc_len;
+  (*desc_ent_ptr)->descriptor = tor_malloc(desc_len+1);
+  strncpy((*desc_ent_ptr)->descriptor, start, desc_len);
+  (*desc_ent_ptr)->descriptor[desc_len] = '\0';
+  *desc = end;
+  the_directory_is_dirty = 1;
+  
+  routerinfo_free(ri);
+  return 0;
+ err:
+  if (desc_tmp)
+    free(desc_tmp);
+  if (ri)
+    routerinfo_free(ri);
+  
+  return -1;
+}
+
+void 
+directory_set_dirty()
+{
+  the_directory_is_dirty = 1;
+}
+
+int 
+dirserv_init_from_directory_string(const char *dir)
+{
+  const char *cp = dir;
+  while(1) {
+    cp = strstr(cp, "\nrouter ");
+    if (!cp) break;
+    ++cp;
+    if (dirserv_add_descriptor(&cp)) {
+      return -1;
+    }
+    --cp; /*Back up to newline.*/
+  }
+  return 0;
+}
+
+int
+dirserv_dump_directory_to_string(char *s, int maxlen,
+                                 crypto_pk_env_t *private_key)
+{
+  char *cp, *eos;
+  char digest[20];
+  char signature[128];
+  char published[33];
+  time_t published_on;
+  int i;
+  eos = s+maxlen;
+
+  if (list_running_servers(&cp))
+    return -1;
+  published_on = time(NULL);
+  strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on));
+  snprintf(s, maxlen,
+           "signed-directory\n"
+           "published %s\n"
+           "recommended-software "RECOMMENDED_SOFTWARE_VERSIONS"\n"
+           "running-routers %s\n", published, cp);
+  free(cp);
+  i = strlen(s);
+  cp = s+i;
+  
+  for (i = 0; i < n_descriptors; ++i) {
+    strncat(cp, descriptor_list[i]->descriptor, descriptor_list[i]->desc_len);
+    cp += descriptor_list[i]->desc_len;
+    assert(!cp);
+  }
+  /* These multiple strlen calls are inefficient, but dwarfed by the RSA
+     signature.
+  */
+  i = strlen(s);
+  strncat(s, "directory-signature\n", maxlen-i);
+  i = strlen(s);
+  cp = s + i;
+  
+  if (crypto_SHA_digest(s, i, digest)) {
+    log_fn(LOG_WARNING,"couldn't compute digest");
+    return -1;
+  }
+  if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) {
+    log_fn(LOG_WARNING,"couldn't sign digest");
+    return -1;
+  }
+  
+  strncpy(cp, 
+          "-----BEGIN SIGNATURE-----\n", maxlen-i);
+          
+  i = strlen(s);
+  cp = s+i;
+  if (base64_encode(cp, maxlen-i, signature, 128) < 0) {
+    log_fn(LOG_WARNING," couldn't base64-encode signature");
+    return -1;
+  }
+
+  i = strlen(s);
+  cp = s+i;
+  strncat(cp, "-----END SIGNATURE-----\n", maxlen-i);
+  i = strlen(s);
+  if (i == maxlen) {
+    log_fn(LOG_WARNING,"tried to exceed string length.");
+    return -1;
+  }
+
+  return 0;
+}
+
+size_t dirserv_get_directory(const char **directory)
+{
+  char *new_directory;
+  if (the_directory_is_dirty) {
+    new_directory = tor_malloc(MAX_DIR_SIZE);
+    if (dirserv_dump_directory_to_string(new_directory, MAX_DIR_SIZE,
+                                         get_identity_key())) {
+      log(LOG_WARNING, "Error creating directory.");
+      free(new_directory);
+      return 0;
+    }
+    if (the_directory)
+      free(the_directory);
+    the_directory = new_directory;
+    the_directory_len = strlen(the_directory);
+    log_fn(LOG_INFO,"New directory (size %d):\n%s",the_directory_len,
+           the_directory);
+    the_directory_is_dirty = 0;
+    /* Now read the directory we just made in order to update our own
+     * router lists.  This does more signature checking than is strictly
+     * necessary, but safe is better than sorry. */
+    new_directory = strdup(*directory);
+    if (router_get_dir_from_string(new_directory, get_identity_key())) {
+      log_fn(LOG_ERR, "We just generated a directory we can't parse. Dying.");
+      exit(0);
+    }
+    free(new_directory);
+  } else {
+    log(LOG_INFO,"Directory still clean, reusing.");
+  }
+  *directory = the_directory;
+  return the_directory_len;
+}

+ 29 - 118
src/or/main.c

@@ -741,11 +741,12 @@ int dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
   strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&router->published_on));
   
   result = snprintf(s, maxlen, 
-                    "router %s %d %d %d %d\n"
+                    "router %s %s %d %d %d %d\n"
                     "published %s\n"
                     "onion-key\n%s"
                     "link-key\n%s"
                     "signing-key\n%s",
+    router->nickname,                   
     router->address,
     router->or_port,
     router->ap_port,
@@ -805,126 +806,36 @@ int dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
   return written+1;
 }
 
-static int 
-build_directory(directory_t *dir) {
-  routerinfo_t **routers = NULL;
+int 
+list_running_servers(char **nicknames_out)
+{
+  char *nickname_lst[MAX_ROUTERS_IN_DIR];
   connection_t *conn;
-  routerinfo_t *router;
-  int i, n = 0;
-
-  routers = (routerinfo_t **)tor_malloc(sizeof(routerinfo_t*) * (nfds+1));
-  if (my_routerinfo) {
-    log(LOG_INFO, "build_directory(): adding self (%s:%d)", 
-        my_routerinfo->address, my_routerinfo->or_port);
-    routers[n++] = my_routerinfo;
-  }
-  for(i = 0; i<nfds; ++i) {
+  char *cp;
+  int n = 0, i;
+  int length;
+  *nicknames_out = NULL;
+  if (my_routerinfo)
+    nickname_lst[n++] = my_routerinfo->nickname;
+  for (i = 0; i<nfds; ++i) {
     conn = connection_array[i];
-
-    if(conn->type != CONN_TYPE_OR)
-      continue; /* we only want to list ORs */
-    if(conn->state != OR_CONN_STATE_OPEN)
-      continue; /* we only want to list ones that successfully handshaked */
-    router = router_get_by_addr_port(conn->addr,conn->port);
-    if(!router) {
-      /* XXX this legitimately happens when conn is an OP. How to detect this? */
-      log(LOG_INFO,"build_directory(): couldn't find router %d:%d!",
-          conn->addr,conn->port);
-      continue;
-    }
-    log(LOG_INFO, "build_directory(): adding router (%s:%d)",
-        router->address, router->or_port);
-    routers[n++] = router;
+    if (conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN)
+      continue; /* only list successfully handshaked OR's. */
+    nickname_lst[n++] = conn->nickname;
+  }
+  length = n + 1; /* spaces + EOS + 1. */
+  for (i = 0; i<n; ++i) {
+    length += strlen(nickname_lst[i]);
+  }
+  *nicknames_out = tor_malloc(length);
+  cp = *nicknames_out;
+  for (i = 0; i<n; ++i) {
+    if (i)
+      strcat(cp, " ");
+    strcat(cp, nickname_lst[i]);
+    while (*cp) 
+      ++cp;
   }
-  dir->routers = routers;
-  dir->n_routers = n;
-  return 0;
-}
-
-int
-dump_signed_directory_to_string(char *s, int maxlen,
-                                crypto_pk_env_t *private_key)
-{
-  directory_t dir;
-  if (build_directory(&dir)) {
-    log(LOG_WARNING,"dump_signed_directory_to_string(): build_directory failed.");
-    return -1;
-  }
-  return dump_signed_directory_to_string_impl(s, maxlen, &dir, private_key);
-}
-
-int
-dump_signed_directory_to_string_impl(char *s, int maxlen, directory_t *dir,
-                                     crypto_pk_env_t *private_key)
-{
-  char *cp, *eos;
-  char digest[20];
-  char signature[128];
-  int i, written;
-  routerinfo_t *router;
-  eos = s+maxlen;
-  strncpy(s, 
-          "signed-directory\n"
-          "recommended-software "
-          RECOMMENDED_SOFTWARE_VERSIONS
-          "\n"
-          , maxlen);
-  
-  i = strlen(s);
-  cp = s+i;
-  for (i = 0; i < dir->n_routers; ++i) {
-    router = dir->routers[i];
-    /* XXX This is wrong; we shouldn't sign routers, but rather propagate
-     * XXX the original router blocks, unaltered.
-     */
-    written = dump_router_to_string(cp, eos-cp, router, private_key);
-
-    if(written < 0) { 
-      log(LOG_WARNING,"dump_signed_directory_to_string(): tried to exceed string length.");
-      cp[maxlen-1] = 0; /* make sure it's null terminated */
-      free(dir->routers);
-      return -1;
-    }
-    cp += written;
-  }
-  free(dir->routers); /* not needed anymore */
-
-  /* These multiple strlen calls are inefficient, but dwarfed by the RSA
-     signature.
-  */
-  i = strlen(s);
-  strncat(s, "directory-signature\n", maxlen-i);
-  i = strlen(s);
-  cp = s + i;
-  
-  if (crypto_SHA_digest(s, i, digest)) {
-    log(LOG_WARNING,"dump_signed_directory_to_string(): couldn't compute digest");
-    return -1;
-  }
-  if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) {
-    log(LOG_WARNING,"dump_signed_directory_to_string(): couldn't sign digest");
-    return -1;
-  }
-  
-  strncpy(cp, 
-          "-----BEGIN SIGNATURE-----\n", maxlen-i);
-          
-  i = strlen(s);
-  cp = s+i;
-  if (base64_encode(cp, maxlen-i, signature, 128) < 0) {
-    log_fn(LOG_WARNING," couldn't base64-encode signature");
-    return -1;
-  }
-
-  i = strlen(s);
-  cp = s+i;
-  strncat(cp, "-----END SIGNATURE-----\n", maxlen-i);
-  i = strlen(s);
-  if (i == maxlen) {
-    log(LOG_WARNING,"dump_signed_directory_to_string(): tried to exceed string length.");
-    return -1;
-  }
-
   return 0;
 }
 

+ 23 - 8
src/or/or.h

@@ -102,6 +102,7 @@
 #define MAX_BUF_SIZE (640*1024)
 #define DEFAULT_BANDWIDTH_OP (1024 * 1000)
 #define MAX_NICKNAME_LEN 32
+#define MAX_DIR_SIZE 50000 /* XXX, big enough? */
 
 #define ACI_TYPE_LOWER 0
 #define ACI_TYPE_HIGHER 1
@@ -214,6 +215,8 @@
 
 /* legal characters in a filename */
 #define CONFIG_LEGAL_FILENAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/"
+/* legal characters in a nickname */
+#define LEGAL_NICKNAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 
 /* structure of a socks client operation */
 typedef struct {
@@ -282,6 +285,7 @@ struct connection_t {
   crypto_pk_env_t *onion_pkey; /* public RSA key for the other side's onions */
   crypto_pk_env_t *link_pkey; /* public RSA key for the other side's TLS */
   crypto_pk_env_t *identity_pkey; /* public RSA key for the other side's signing */
+  char *nickname;
 
 /* Used only by OR connections: */
   tor_tls *tls;
@@ -335,6 +339,8 @@ typedef struct {
   crypto_pk_env_t *link_pkey;  /* public RSA key for TLS */
   crypto_pk_env_t *identity_pkey;  /* public RSA key for signing */
  
+  int is_running;
+
   /* link info */
   uint32_t bandwidth;
   struct exit_policy_t *exit_policy;
@@ -345,6 +351,7 @@ typedef struct {
   routerinfo_t **routers;
   int n_routers;
   char *software_versions;
+  time_t published_on;
 } directory_t;
 
 struct crypt_path_t { 
@@ -582,7 +589,6 @@ int assign_to_cpuworker(connection_t *cpuworker, unsigned char question_type,
 /********************************* directory.c ***************************/
 
 void directory_initiate_command(routerinfo_t *router, int command);
-void directory_set_dirty(void);
 int connection_dir_process_inbuf(connection_t *conn);
 int connection_dir_finished_flushing(connection_t *conn);
 
@@ -618,12 +624,8 @@ void connection_start_reading(connection_t *conn);
 void connection_stop_writing(connection_t *conn);
 void connection_start_writing(connection_t *conn);
 
-int dump_signed_directory_to_string(char *s, int maxlen, 
-                                    crypto_pk_env_t *private_key);
-/* Exported for debugging */
-int dump_signed_directory_to_string_impl(char *s, int maxlen, 
-                                         directory_t *dir, 
-                                         crypto_pk_env_t *private_key); 
+int list_running_servers(char **nicknames_out);
+
 const char *router_get_my_descriptor(void);
 
 int main(int argc, char *argv[]);
@@ -676,7 +678,7 @@ int router_get_router_hash(char *s, char *digest);
 /* Reads a list of known routers, unsigned. */
 int router_get_list_from_string(char *s);
 /* Exported for debugging */
-int router_get_list_from_string_impl(char **s, directory_t **dest);
+int router_get_list_from_string_impl(char **s, directory_t **dest, int n_good_nicknames, const char *good_nickname_lst[]);
 /* Reads a signed directory. */
 int router_get_dir_from_string(char *s, crypto_pk_env_t *pkey);
 /* Exported or debugging */
@@ -686,6 +688,19 @@ routerinfo_t *router_get_entry_from_string(char **s);
 int router_compare_to_exit_policy(connection_t *conn);
 void routerinfo_free(routerinfo_t *router);
 
+/********************************* dirserv.c ***************************/
+int dirserv_parse_fingerprint_file(const char *fname);
+int dirserv_router_fingerprint_is_known(const routerinfo_t *router);
+void dirserv_free_fingerprint_list();
+int dirserv_add_descriptor(const char **desc);
+int dirserv_init_from_directory_string(const char *dir);
+void dirserv_free_descriptors();
+int dirserv_dump_directory_to_string(char *s, int maxlen,
+                                     crypto_pk_env_t *private_key);
+void directory_set_dirty();
+size_t dirserv_get_directory(const char **cp);
+
+
 #endif
 
 /*

+ 58 - 134
src/or/routers.c

@@ -319,6 +319,7 @@ typedef enum {
   K_LINK_KEY,
   K_ROUTER_SIGNATURE,
   K_PUBLISHED,
+  K_RUNNING_ROUTERS,
   _SIGNATURE, 
   _PUBLIC_KEY, 
   _ERR, 
@@ -339,10 +340,11 @@ static struct token_table_ent token_table[] = {
   { "link-key", K_LINK_KEY },
   { "router-signature", K_ROUTER_SIGNATURE },
   { "published", K_PUBLISHED },
+  { "running-routers", K_RUNNING_ROUTERS },
   { NULL, -1 }
 };
 
-#define MAX_ARGS 8
+#define MAX_ARGS 1024
 struct directory_token {
   directory_keyword tp;
   union {
@@ -495,6 +497,7 @@ router_dump_token(directory_token_t *tok) {
     case K_LINK_KEY: printf("Link-key"); break;
     case K_ROUTER_SIGNATURE: printf("Router-signature"); break;
     case K_PUBLISHED: printf("Published"); break;
+    case K_RUNNING_ROUTERS: printf("Running-routers"); break;
     default:
       printf("?????? %d\n", tok->tp); return;
     }
@@ -551,7 +554,7 @@ static char *find_whitespace(char *s) {
 
 int router_get_list_from_string(char *s) 
 {
-  if (router_get_list_from_string_impl(&s, &directory)) {
+  if (router_get_list_from_string_impl(&s, &directory, -1, NULL)) {
     log(LOG_WARNING, "Error parsing router file");
     return -1;
   }
@@ -653,6 +656,10 @@ int router_get_dir_from_string_impl(char *s, directory_t **dest,
   char signed_digest[128];
   directory_t *new_dir = NULL;
   char *versions;
+  struct tm published;
+  time_t published_on;
+  const char *good_nickname_lst[1024];
+  int n_good_nicknames;
   
 #define NEXT_TOK()                                                      \
   do {                                                                  \
@@ -675,6 +682,18 @@ int router_get_dir_from_string_impl(char *s, directory_t **dest,
   NEXT_TOK();
   TOK_IS(K_SIGNED_DIRECTORY, "signed-directory");
 
+  NEXT_TOK();
+  TOK_IS(K_PUBLISHED, "published");
+  if (tok.val.cmd.n_args != 2) {
+    log_fn(LOG_WARNING, "Invalid published line");
+    goto err;
+  }
+  tok.val.cmd.args[1][-1] = ' ';
+  if (!strptime(tok.val.cmd.args[0], "%Y-%m-%d %H:%M:%S", &published)) {
+    log_fn(LOG_WARNING, "Published time was unparseable"); goto err;
+  }
+  published_on = timegm(&published);  
+
   NEXT_TOK();
   TOK_IS(K_RECOMMENDED_SOFTWARE, "recommended-software");
   if (tok.val.cmd.n_args != 1) {
@@ -683,11 +702,18 @@ int router_get_dir_from_string_impl(char *s, directory_t **dest,
   }
   versions = strdup(tok.val.cmd.args[0]);
   
-  if (router_get_list_from_string_impl(&s, &new_dir)) {
+  NEXT_TOK();
+  TOK_IS(K_RUNNING_ROUTERS, "running-routers");
+  n_good_nicknames = tok.val.cmd.n_args;
+  memcpy(good_nickname_lst, tok.val.cmd.args, n_good_nicknames);
+
+  if (router_get_list_from_string_impl(&s, &new_dir,
+                                       n_good_nicknames, good_nickname_lst)) {
     log_fn(LOG_WARNING, "Error reading routers from directory");
     goto err;
   }
   new_dir->software_versions = versions;
+  new_dir->published_on = published_on;
 
   NEXT_TOK();
   TOK_IS(K_DIRECTORY_SIGNATURE, "directory-signature");
@@ -725,11 +751,14 @@ int router_get_dir_from_string_impl(char *s, directory_t **dest,
 #undef TOK_IS
 }
 
-int router_get_list_from_string_impl(char **s, directory_t **dest)
+int router_get_list_from_string_impl(char **s, directory_t **dest, 
+                                     int n_good_nicknames, 
+                                     const char **good_nickname_lst)
 {
   routerinfo_t *router;
   routerinfo_t **rarray;
   int rarray_len = 0;
+  int i, router_is_running;
 
   assert(s);
 
@@ -749,6 +778,15 @@ int router_get_list_from_string_impl(char **s, directory_t **dest)
       routerinfo_free(router);
       continue;
     } 
+    if (n_good_nicknames>=0) {
+      router->is_running = 0;
+      for (i = 0; i < n_good_nicknames; ++i) {
+        if (0==strcasecmp(good_nickname_lst[i], router->nickname)) {
+          router->is_running = 1;
+          break;
+        }
+      }
+    }
     rarray[rarray_len++] = router;
   }
  
@@ -846,31 +884,38 @@ routerinfo_t *router_get_entry_from_string(char**s) {
      thank me for this someday. */
   router->onion_pkey = router->identity_pkey = router->link_pkey = NULL; 
 
-  if (tok->val.cmd.n_args != 5) {
+  if (tok->val.cmd.n_args != 6) {
     log_fn(LOG_WARNING,"Wrong # of arguments to \"router\"");
     goto err;
   }
-
+  if (!(router->nickname = strdup(ARGS[0])))
+    goto err;
+  if (strlen(router->nickname) > MAX_NICKNAME_LEN)
+    goto err;
+  if (strspn(router->nickname, LEGAL_NICKNAME_CHARACTERS) != 
+      strlen(router->nickname))
+    goto err;
+  
   /* read router.address */
-  if (!(router->address = strdup(ARGS[0])))
+  if (!(router->address = strdup(ARGS[1])))
     goto err;
   router->addr = 0;
 
   /* Read router->or_port */
-  router->or_port = atoi(ARGS[1]);
+  router->or_port = atoi(ARGS[2]);
   if(!router->or_port) {
     log_fn(LOG_WARNING,"or_port unreadable or 0. Failing.");
     goto err;
   }
   
   /* Router->ap_port */
-  router->ap_port = atoi(ARGS[2]);
+  router->ap_port = atoi(ARGS[3]);
   
   /* Router->dir_port */
-  router->dir_port = atoi(ARGS[3]);
+  router->dir_port = atoi(ARGS[4]);
 
   /* Router->bandwidth */
-  router->bandwidth = atoi(ARGS[4]);
+  router->bandwidth = atoi(ARGS[5]);
   if (!router->bandwidth) {
     log_fn(LOG_WARNING,"bandwidth unreadable or 0. Failing.");
   }
@@ -885,8 +930,8 @@ routerinfo_t *router_get_entry_from_string(char**s) {
   if (tok->val.cmd.n_args != 2) {
     log_fn(LOG_WARNING, "Wrong number of arguments to published"); goto err;
   }
-  tok->val.cmd.args[1][-1] = ' '; /* Re-insert space. */
-  if (!strptime(tok->val.cmd.args[0], "%Y-%m-%d %H:%M:%S", &published)) {
+  ARGS[1][-1] = ' '; /* Re-insert space. */
+  if (!strptime(ARGS[0], "%Y-%m-%d %H:%M:%S", &published)) {
     log_fn(LOG_WARNING, "Published time was unparseable"); goto err;
   }
   router->published_on = timegm(&published);
@@ -1073,127 +1118,6 @@ int router_compare_to_exit_policy(connection_t *conn) {
   return 0; /* accept all by default. */
 }
 
-/*** Fingerprint handling code. ***/
-typedef struct fingerprint_entry_t {
-  char *nickname;
-  char *fingerprint;
-} fingerprint_entry_t;
-
-static fingerprint_entry_t fingerprint_list[MAX_ROUTERS_IN_DIR];
-static int n_fingerprints = 0;
-/* return 0 on success, -1 on failure */
-int directory_parse_fingerprint_file(const char *fname)
-{
-  FILE *file;
-#define BUF_LEN (FINGERPRINT_LEN+MAX_NICKNAME_LEN+20)
-  char buf[BUF_LEN+1];
-  char *cp, *nickname, *fingerprint;
-  fingerprint_entry_t fingerprint_list_tmp[MAX_ROUTERS_IN_DIR];
-  int n_fingerprints_tmp = 0;
-  int lineno=0;
-  int i;
-  if (!(file = fopen(fname, "r"))) {
-    log(LOG_WARNING, "Cannot open fingerprint file %s", fname);
-    goto err;
-  }
-  while (1) {
-    cp = fgets(buf, BUF_LEN, file);
-    ++lineno;
-    if (!cp) {
-      if (feof(file))
-        break;
-      else {
-        log(LOG_WARNING, "Error reading from fingerprint file");
-        goto err;
-      }
-    }
-    buf[BUF_LEN]='\0';
-    cp = buf;
-    while (isspace(*cp))
-      ++cp;
-    if (*cp == '#' || *cp == '\0') 
-      continue;
-    nickname = cp;
-    cp = strchr(cp, ' ');
-    if (!cp) {
-      log(LOG_WARNING, "Bad line %d of fingerprint file", lineno);
-      goto err;
-    }
-    *cp++ = '\0';
-    while (isspace(*cp))
-      ++cp;
-    if (strlen(cp) < FINGERPRINT_LEN) {
-      log(LOG_WARNING, "Bad line %d of fingerprint file", lineno);
-      goto err;
-    }
-    fingerprint = cp;
-    cp[FINGERPRINT_LEN] = '\0';
-    if (strlen(nickname) > MAX_NICKNAME_LEN) {
-      log(LOG_WARNING, "Nickname too long on line %d of fingerprint file",
-          lineno);
-      goto err;
-    }
-    if (!crypto_pk_check_fingerprint_syntax(fingerprint)) {
-      log(LOG_WARNING, "Invalid fingerprint on line %d of fingerprint file",
-          lineno);
-      goto err;
-    }
-    for (i = 0; i < n_fingerprints_tmp; ++i) {
-      if (0==strcasecmp(fingerprint_list_tmp[i].nickname, nickname)) {
-        log(LOG_WARNING, "Duplicate nickname on line %d of fingerprint file", lineno);
-        goto err;
-      }
-    }
-    fingerprint_list_tmp[n_fingerprints_tmp].nickname = strdup(nickname);
-    fingerprint_list_tmp[n_fingerprints_tmp].fingerprint = strdup(fingerprint);
-    ++n_fingerprints_tmp;
-  }
-  /* replace the global fingerprints list. */
-  for (i = 0; i < n_fingerprints; ++i) {
-    free(fingerprint_list[i].nickname);
-    free(fingerprint_list[i].fingerprint);
-  }
-  memcpy(fingerprint_list, fingerprint_list_tmp, 
-         sizeof(fingerprint_entry_t)*n_fingerprints_tmp);
-  n_fingerprints = n_fingerprints_tmp;
-  return 0; 
-
- err:
-  for (i = 0; i < n_fingerprints_tmp; ++i) {
-    free(fingerprint_list_tmp[i].nickname);
-    free(fingerprint_list_tmp[i].fingerprint);
-  }
-  return -1;
-#undef BUF_LEN
-}    
-
-/* return 1 if router's identity and nickname match. */
-int
-directory_check_router_identity(const routerinfo_t *router)
-{
-  int i;
-  char fp[FINGERPRINT_LEN+1];
-  if (crypto_pk_get_fingerprint(router->identity_pkey, fp)) {
-    /* XXX Error computing fingerprint: log */
-    return 0;
-  }
-  for (i=0;i<n_fingerprints;++i) {
-    if (0==strcasecmp(fingerprint_list[i].nickname,router->nickname)) {
-      /* Right nickname... */
-      if (0==strcasecmp(fingerprint_list[i].fingerprint, fp)) {
-        /* Right fingerprint. */
-        return 1;
-      } else {
-        /* Wrong fingerprint. */
-        return 0;
-      }
-    }
-  }
-  /* No match found. XXX log. */
-  return 0;
-}
-
-
 
 /*
   Local Variables: