[or-cvs] Refactor directory servers

Roger Dingledine arma at seul.org
Tue Mar 30 22:57:51 UTC 2004


Update of /home/or/cvsroot/src/or
In directory moria.mit.edu:/home2/arma/work/onion/cvs/src/or

Modified Files:
	connection.c directory.c main.c or.h router.c 
Log Message:
Refactor directory servers
* read all the time (before we would ignore eof sometimes, oops)
* we can handle different urls now
* send back 404 for an un-handled url
* commands initiated by the client can handle payloads now
* introduce conn->purpose to avoid exponential state-space explosion


Index: connection.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection.c,v
retrieving revision 1.189
retrieving revision 1.190
diff -u -d -r1.189 -r1.190
--- connection.c	29 Mar 2004 20:04:09 -0000	1.189
+++ connection.c	30 Mar 2004 22:57:49 -0000	1.190
@@ -49,14 +49,11 @@
     "open" },                          /* 8 */
   { "ready" }, /* dir listener, 0 */
   { "",                           /* dir, 0 */
-    "connecting (fetch)",              /* 1 */
-    "connecting (upload)",             /* 2 */
-    "client sending fetch",            /* 3 */
-    "client sending upload",           /* 4 */
-    "client reading fetch",            /* 5 */
-    "client reading upload",           /* 6 */
-    "awaiting command",                /* 7 */
-    "writing" },                       /* 8 */
+    "connecting",                      /* 1 */
+    "client sending",                  /* 2 */
+    "client reading",                  /* 3 */
+    "awaiting command",                /* 4 */
+    "writing" },                       /* 5 */
   { "",                    /* dns worker, 0 */
     "idle",                            /* 1 */
     "busy" },                          /* 2 */
@@ -373,6 +370,7 @@
       conn->state = AP_CONN_STATE_SOCKS_WAIT;
       break;
     case CONN_TYPE_DIR:
+      conn->purpose = DIR_PURPOSE_SERVER;
       conn->state = DIR_CONN_STATE_SERVER_COMMAND_WAIT;
       break;
   }
@@ -599,8 +597,7 @@
 
   if(connection_read_to_buf(conn) < 0) {
     if(conn->type == CONN_TYPE_DIR &&
-       (conn->state == DIR_CONN_STATE_CONNECTING_FETCH ||
-        conn->state == DIR_CONN_STATE_CONNECTING_UPLOAD)) {
+       conn->state == DIR_CONN_STATE_CONNECTING) {
        /* it's a directory server and connecting failed: forget about this router */
        /* XXX I suspect pollerr may make Windows not get to this point. :( */
        router_mark_as_down(conn->nickname);
@@ -1029,6 +1026,9 @@
   } else {
     assert(!conn->socks_request);
   }
+  if(conn->type != CONN_TYPE_DIR) {
+    assert(!conn->purpose); /* only used for dir types currently */
+  }
 
   switch(conn->type)
     {
@@ -1053,6 +1053,8 @@
     case CONN_TYPE_DIR:
       assert(conn->state >= _DIR_CONN_STATE_MIN &&
              conn->state <= _DIR_CONN_STATE_MAX);
+      assert(conn->purpose >= _DIR_PURPOSE_MIN &&
+             conn->purpose <= _DIR_PURPOSE_MAX);
       break;
     case CONN_TYPE_DNSWORKER:
       assert(conn->state == DNSWORKER_STATE_IDLE ||

Index: directory.c
===================================================================
RCS file: /home/or/cvsroot/src/or/directory.c,v
retrieving revision 1.70
retrieving revision 1.71
diff -u -d -r1.70 -r1.71
--- directory.c	29 Mar 2004 23:23:01 -0000	1.70
+++ directory.c	30 Mar 2004 22:57:49 -0000	1.71
@@ -4,7 +4,8 @@
 
 #include "or.h"
 
-static int directory_send_command(connection_t *conn, int command);
+static void directory_send_command(connection_t *conn,
+                                   int purpose, const char *payload);
 static int directory_handle_command(connection_t *conn);
 
 /********* START VARIABLES **********/
@@ -17,20 +18,26 @@
 
 /********* END VARIABLES ************/
 
-void directory_initiate_command(routerinfo_t *router, int command) {
+void directory_initiate_command(routerinfo_t *router, int purpose, const char *payload) {
   connection_t *conn;
 
-  switch(command) {
-    case DIR_CONN_STATE_CONNECTING_FETCH:
+  switch(purpose) {
+    case DIR_PURPOSE_FETCH_DIR:
       log_fn(LOG_DEBUG,"initiating directory fetch");
       break;
-    case DIR_CONN_STATE_CONNECTING_UPLOAD:
-      log_fn(LOG_DEBUG,"initiating directory upload");
+    case DIR_PURPOSE_FETCH_HIDSERV:
+      log_fn(LOG_DEBUG,"initiating hidden-service descriptor fetch");
+      break;
+    case DIR_PURPOSE_UPLOAD_DIR:
+      log_fn(LOG_DEBUG,"initiating server descriptor upload");
+      break;
+    case DIR_PURPOSE_UPLOAD_HIDSERV:
+      log_fn(LOG_DEBUG,"initiating hidden-service descriptor upload");
       break;
   }
 
   if (!router) { /* i guess they didn't have one in mind for me to use */
-    log_fn(LOG_WARN,"No running dirservers known. Not trying.");
+    log_fn(LOG_WARN,"No running dirservers known. Not trying. (purpose %d)", purpose);
     return;
   }
 
@@ -44,57 +51,99 @@
   assert(router->identity_pkey);
   conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey);
 
-  conn->state = command;
+  conn->purpose = purpose;
 
   if(connection_add(conn) < 0) { /* no space, forget it */
     connection_free(conn);
     return;
   }
 
-  switch(connection_connect(conn, router->address, router->addr, router->dir_port)) {
-    case -1:
-      router_mark_as_down(conn->nickname); /* don't try him again */
-      connection_mark_for_close(conn, 0);
-      return;
-    case 0:
-      connection_set_poll_socket(conn);
-      connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
-      /* writable indicates finish, readable indicates broken link,
-         error indicates broken link in windowsland. */
-      return;
-    /* case 1: fall through */
-  }
+  /* queue the command on the outbuf */
+  directory_send_command(conn, purpose, payload);
 
-  connection_set_poll_socket(conn);
-  if(directory_send_command(conn, command) < 0) {
-    connection_mark_for_close(conn, 0);
+  if(purpose == DIR_PURPOSE_FETCH_DIR ||
+     purpose == DIR_PURPOSE_UPLOAD_DIR) {
+
+    /* then we want to connect directly */
+    conn->state = DIR_CONN_STATE_CONNECTING;
+
+    switch(connection_connect(conn, conn->address, conn->addr, conn->port)) {
+      case -1:
+        router_mark_as_down(conn->nickname); /* don't try him again */
+        connection_mark_for_close(conn, 0);
+        return;
+      case 1:
+        conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
+        /* fall through */
+      case 0:
+        connection_set_poll_socket(conn);
+        connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
+        /* writable indicates finish, readable indicates broken link,
+           error indicates broken link in windowsland. */
+    }
+  } else { /* we want to connect via tor */
+    /* make an AP connection
+     *   populate it and add it at the right state
+     * socketpair and hook up both sides
+     */
+
+    conn->state = DIR_CONN_STATE_CLIENT_SENDING; 
+    connection_set_poll_socket(conn);
   }
 }
 
-static int directory_send_command(connection_t *conn, int command) {
+static void directory_send_command(connection_t *conn,
+                                   int purpose, const char *payload) {
   char fetchstring[] = "GET / HTTP/1.0\r\n\r\n";
-  const char *s;
   char tmp[8192];
 
   assert(conn && conn->type == CONN_TYPE_DIR);
 
-  switch(command) {
-    case DIR_CONN_STATE_CONNECTING_FETCH:
+  switch(purpose) {
+    case DIR_PURPOSE_FETCH_DIR:
+      assert(payload == NULL);
       connection_write_to_buf(fetchstring, strlen(fetchstring), conn);
-      conn->state = DIR_CONN_STATE_CLIENT_SENDING_FETCH;
       break;
-    case DIR_CONN_STATE_CONNECTING_UPLOAD:
-      s = router_get_my_descriptor();
-      if(!s) {
-        log_fn(LOG_WARN,"Failed to get my descriptor.");
-        return -1;
-      }
+    case DIR_PURPOSE_UPLOAD_DIR:
+      assert(payload);
       snprintf(tmp, sizeof(tmp), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s",
-               (int)strlen(s), s);
+               (int)strlen(payload), payload);
+      connection_write_to_buf(tmp, strlen(tmp), conn);
+      break;
+    case DIR_PURPOSE_FETCH_HIDSERV:
+      assert(payload);
+      snprintf(tmp, sizeof(tmp), "GET /hidserv/%s HTTP/1.0\r\n\r\n", payload);
+      connection_write_to_buf(tmp, strlen(tmp), conn);
+      break;
+    case DIR_PURPOSE_UPLOAD_HIDSERV:
+      assert(payload);
+      snprintf(tmp, sizeof(tmp),
+        "POST /hidserv/ HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s",
+        (int)strlen(payload), payload);
       connection_write_to_buf(tmp, strlen(tmp), conn);
-      conn->state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD;
       break;
   }
+}
+
+/* Parse "%s %s HTTP/1..."
+ * If it's well-formed, point *url to the second %s,
+ * null-terminate it (this modifies headers!) and return 0.
+ * Otherwise, return -1.
+ */
+int parse_http_url(char *headers, char **url) {
+  char *s, *tmp;
+
+  s = (char *)eat_whitespace_no_nl(headers);
+  if (!*s) return -1;
+  s = (char *)find_whitespace(s); /* get past GET/POST */
+  if (!*s) return -1;
+  s = (char *)eat_whitespace_no_nl(s);
+  if (!*s) return -1;
+  tmp = s; /* this is it, assuming it's valid */
+  s = (char *)find_whitespace(s);
+  if (!*s) return -1;
+  *s = 0;
+  *url = tmp;
   return 0;
 }
 
@@ -131,8 +180,7 @@
   assert(conn && conn->type == CONN_TYPE_DIR);
 
   if(conn->inbuf_reached_eof) {
-    if(conn->state != DIR_CONN_STATE_CLIENT_READING_FETCH &&
-       conn->state != DIR_CONN_STATE_CLIENT_READING_UPLOAD) {
+    if(conn->state != DIR_CONN_STATE_CLIENT_READING) {
       log_fn(LOG_INFO,"conn reached eof, not reading. Closing.");
       connection_close_immediate(conn); /* it was an error; give up on flushing */
       connection_mark_for_close(conn,0);
@@ -160,7 +208,7 @@
       return -1;
     }
 
-    if(conn->state == DIR_CONN_STATE_CLIENT_READING_FETCH) {
+    if(conn->purpose == DIR_PURPOSE_FETCH_DIR) {
       /* fetch/process the directory to learn about new routers. */
       int directorylen;
       directorylen = strlen(directory);
@@ -192,7 +240,7 @@
       return 0;
     }
 
-    if(conn->state == DIR_CONN_STATE_CLIENT_READING_UPLOAD) {
+    if(conn->purpose == DIR_PURPOSE_UPLOAD_DIR) {
       switch(status_code) {
         case 200:
           log_fn(LOG_INFO,"eof (status 200) while reading upload response: finished.");
@@ -204,15 +252,35 @@
           log_fn(LOG_WARN,"http status 403 (unapproved server) response from dirserver. Is your clock skewed? Have you mailed arma your identity fingerprint? Are you using the right key?");
 
           break;
+        default:
+          log_fn(LOG_WARN,"http status %d response unrecognized.", status_code);
+          break;
       }
       free(directory); free(headers);
       connection_mark_for_close(conn,0);
       return 0;
     }
+
+    if(conn->purpose == DIR_PURPOSE_FETCH_HIDSERV) {
+
+
+    }
+
+    if(conn->purpose == DIR_PURPOSE_UPLOAD_HIDSERV) {
+
+
+    }
+    assert(0); /* never reached */
   }
 
-  if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT)
-    return directory_handle_command(conn);
+  if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) {
+    if (directory_handle_command(conn) < 0) {
+      connection_mark_for_close(conn,0);
+      return -1;
+    } else {
+      return 0;
+    }
+  }
 
   /* XXX for READ states, might want to make sure inbuf isn't too big */
 
@@ -223,54 +291,94 @@
 static char answer200[] = "HTTP/1.0 200 OK\r\n\r\n";
 static char answer400[] = "HTTP/1.0 400 Bad request\r\n\r\n";
 static char answer403[] = "HTTP/1.0 403 Unapproved server\r\n\r\n";
+static char answer404[] = "HTTP/1.0 404 Not found\r\n\r\n";
 static char answer503[] = "HTTP/1.0 503 Directory unavailable\r\n\r\n";
 
+/* always returns 0 */
 static int directory_handle_command_get(connection_t *conn,
                                         char *headers, char *body) {
   size_t dlen;
   const char *cp;
+  char *url;
 
-  /* XXX should check url and http version */
   log_fn(LOG_DEBUG,"Received GET command.");
 
-  dlen = dirserv_get_directory(&cp);
+  conn->state = DIR_CONN_STATE_SERVER_WRITING;
 
-  if(dlen == 0) {
-    log_fn(LOG_WARN,"My directory is empty. Closing.");
-    connection_write_to_buf(answer503, strlen(answer503), conn);
-    conn->state = DIR_CONN_STATE_SERVER_WRITING;
+  if (parse_http_url(headers, &url) < 0) {
+    connection_write_to_buf(answer400, strlen(answer400), conn);
     return 0;
   }
 
-  log_fn(LOG_DEBUG,"Dumping directory to client.");
-  connection_write_to_buf(answer200, strlen(answer200), conn);
-  connection_write_to_buf(cp, dlen, conn);
-  conn->state = DIR_CONN_STATE_SERVER_WRITING;
+  if(!strcmp(url,"/")) { /* directory fetch */
+    dlen = dirserv_get_directory(&cp);
+
+    if(dlen == 0) {
+      log_fn(LOG_WARN,"My directory is empty. Closing.");
+      connection_write_to_buf(answer503, strlen(answer503), conn);
+      return 0;
+    }
+
+    log_fn(LOG_DEBUG,"Dumping directory to client.");
+    connection_write_to_buf(answer200, strlen(answer200), conn);
+    connection_write_to_buf(cp, dlen, conn);
+    return 0;
+  }
+
+  if(!strncmp(url,"/hidserv/",9)) { /* hidserv descriptor fetch */
+    /* ask back-end for the hidden-services descriptor in
+     * url+9, and return it with a 200 if valid, or give a 404
+     * otherwise
+     */
+
+  }
+
+  /* we didn't recognize the url */
+  connection_write_to_buf(answer404, strlen(answer404), conn);
   return 0;
 }
 
+/* always returns 0 */
 static int directory_handle_command_post(connection_t *conn,
                                          char *headers, char *body) {
   const char *cp;
+  char *url;
 
-  /* XXX should check url and http version */
   log_fn(LOG_DEBUG,"Received POST command.");
-  cp = body;
-  switch(dirserv_add_descriptor(&cp)) {
-    case -1:
-      /* malformed descriptor, or something wrong */
-      connection_write_to_buf(answer400, strlen(answer400), conn);
-      break;
-    case 0:
-      /* descriptor was well-formed but server has not been approved */
-      connection_write_to_buf(answer403, strlen(answer403), conn);
-      break;
-    case 1:
-      dirserv_get_directory(&cp); /* rebuild and write to disk */
-      connection_write_to_buf(answer200, strlen(answer200), conn);
-      break;
-  }
+
   conn->state = DIR_CONN_STATE_SERVER_WRITING;
+
+  if (parse_http_url(headers, &url) < 0) {
+    connection_write_to_buf(answer400, strlen(answer400), conn);
+    return 0;
+  }
+
+  if(!strcmp(url,"/")) { /* server descriptor post */
+    cp = body;
+    switch(dirserv_add_descriptor(&cp)) {
+      case -1:
+        /* malformed descriptor, or something wrong */
+        connection_write_to_buf(answer400, strlen(answer400), conn);
+        break;
+      case 0:
+        /* descriptor was well-formed but server has not been approved */
+        connection_write_to_buf(answer403, strlen(answer403), conn);
+        break;
+      case 1:
+        dirserv_get_directory(&cp); /* rebuild and write to disk */
+        connection_write_to_buf(answer200, strlen(answer200), conn);
+        break;
+    }
+  }
+
+  if(!strncmp(url,"/hidserv/",9)) { /* hidserv descriptor post */
+    /* pass 'body' to the backend */
+    /* return 400, 403, or 200 as appropriate */
+
+  }
+
+  /* we didn't recognize the url */
+  connection_write_to_buf(answer404, strlen(answer404), conn);
   return 0;
 }
 
@@ -312,8 +420,7 @@
   assert(conn && conn->type == CONN_TYPE_DIR);
 
   switch(conn->state) {
-    case DIR_CONN_STATE_CONNECTING_FETCH:
-    case DIR_CONN_STATE_CONNECTING_UPLOAD:
+    case DIR_CONN_STATE_CONNECTING:
       if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0)  { /* not yet */
         if(!ERRNO_CONN_EINPROGRESS(errno)) {
           log_fn(LOG_DEBUG,"in-progress connect failed. Removing.");
@@ -329,16 +436,12 @@
       log_fn(LOG_INFO,"Dir connection to router %s:%u established.",
           conn->address,conn->port);
 
-      return directory_send_command(conn, conn->state);
-    case DIR_CONN_STATE_CLIENT_SENDING_FETCH:
-      log_fn(LOG_DEBUG,"client finished sending fetch command.");
-      conn->state = DIR_CONN_STATE_CLIENT_READING_FETCH;
-      connection_watch_events(conn, POLLIN);
+      conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
       return 0;
-    case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD:
-      log_fn(LOG_DEBUG,"client finished sending upload command.");
-      conn->state = DIR_CONN_STATE_CLIENT_READING_UPLOAD;
-      connection_watch_events(conn, POLLIN);
+    case DIR_CONN_STATE_CLIENT_SENDING:
+      log_fn(LOG_DEBUG,"client finished sending command.");
+      conn->state = DIR_CONN_STATE_CLIENT_READING;
+      connection_stop_writing(conn);
       return 0;
     case DIR_CONN_STATE_SERVER_WRITING:
       log_fn(LOG_INFO,"Finished writing server response. Closing.");

Index: main.c
===================================================================
RCS file: /home/or/cvsroot/src/or/main.c,v
retrieving revision 1.211
retrieving revision 1.212
diff -u -d -r1.211 -r1.212
--- main.c	30 Mar 2004 03:15:53 -0000	1.211
+++ main.c	30 Mar 2004 22:57:49 -0000	1.212
@@ -329,7 +329,7 @@
       /* NOTE directory servers do not currently fetch directories.
        * Hope this doesn't bite us later. */
       directory_initiate_command(router_pick_directory_server(),
-                                 DIR_CONN_STATE_CONNECTING_FETCH);
+                                 DIR_PURPOSE_FETCH_DIR, NULL);
     } else {
       /* We're a directory; dump any old descriptors. */
       dirserv_remove_old_servers();
@@ -519,7 +519,8 @@
     }
   } else {
     /* fetch a new directory */
-    directory_initiate_command(router_pick_directory_server(), DIR_CONN_STATE_CONNECTING_FETCH);
+    directory_initiate_command(router_pick_directory_server(),
+                               DIR_PURPOSE_FETCH_DIR, NULL);
   }
   if(options.ORPort) {
     router_rebuild_descriptor();

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.261
retrieving revision 1.262
diff -u -d -r1.261 -r1.262
--- or.h	30 Mar 2004 19:52:42 -0000	1.261
+++ or.h	30 Mar 2004 22:57:49 -0000	1.262
@@ -171,15 +171,20 @@
 #define _AP_CONN_STATE_MAX 8
 
 #define _DIR_CONN_STATE_MIN 1
-#define DIR_CONN_STATE_CONNECTING_FETCH 1
-#define DIR_CONN_STATE_CONNECTING_UPLOAD 2
-#define DIR_CONN_STATE_CLIENT_SENDING_FETCH 3
-#define DIR_CONN_STATE_CLIENT_SENDING_UPLOAD 4
-#define DIR_CONN_STATE_CLIENT_READING_FETCH 5
-#define DIR_CONN_STATE_CLIENT_READING_UPLOAD 6
-#define DIR_CONN_STATE_SERVER_COMMAND_WAIT 7
-#define DIR_CONN_STATE_SERVER_WRITING 8
-#define _DIR_CONN_STATE_MAX 8
+#define DIR_CONN_STATE_CONNECTING 1
+#define DIR_CONN_STATE_CLIENT_SENDING 2
+#define DIR_CONN_STATE_CLIENT_READING 3
+#define DIR_CONN_STATE_SERVER_COMMAND_WAIT 4
+#define DIR_CONN_STATE_SERVER_WRITING 5
+#define _DIR_CONN_STATE_MAX 5
+
+#define _DIR_PURPOSE_MIN 1
+#define DIR_PURPOSE_FETCH_DIR 1
+#define DIR_PURPOSE_FETCH_HIDSERV 2
+#define DIR_PURPOSE_UPLOAD_DIR 3
+#define DIR_PURPOSE_UPLOAD_HIDSERV 4
+#define DIR_PURPOSE_SERVER 5
+#define _DIR_PURPOSE_MAX 5
 
 #define CIRCUIT_STATE_BUILDING 0 /* I'm the OP, still haven't done all my handshakes */
 #define CIRCUIT_STATE_ONIONSKIN_PENDING 1 /* waiting to process the onionskin */
@@ -331,6 +336,7 @@
 
   uint8_t type;
   uint8_t state;
+  uint8_t purpose; /* only used for DIR types currently */
   uint8_t wants_to_read; /* should we start reading again once
                           * the bandwidth throttler allows it?
                           */
@@ -830,7 +836,7 @@
 
 /********************************* directory.c ***************************/
 
-void directory_initiate_command(routerinfo_t *router, int command);
+void directory_initiate_command(routerinfo_t *router, int purpose, const char *payload);
 int connection_dir_process_inbuf(connection_t *conn);
 int connection_dir_finished_flushing(connection_t *conn);
 

Index: router.c
===================================================================
RCS file: /home/or/cvsroot/src/or/router.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- router.c	12 Mar 2004 12:43:13 -0000	1.15
+++ router.c	30 Mar 2004 22:57:49 -0000	1.16
@@ -236,12 +236,14 @@
   int i;
   routerinfo_t *router;
   routerlist_t *rl;
+  const char *s;
 
   router_get_routerlist(&rl);
   if(!rl)
     return;
 
-  if (!router_get_my_descriptor()) {
+  s = router_get_my_descriptor();
+  if (!s) {
     log_fn(LOG_WARN, "No descriptor; skipping upload");
     return;
   }
@@ -249,7 +251,7 @@
   for(i=0;i<rl->n_routers;i++) {
     router = rl->routers[i];
     if(router->dir_port > 0)
-      directory_initiate_command(router, DIR_CONN_STATE_CONNECTING_UPLOAD);
+      directory_initiate_command(router, DIR_PURPOSE_UPLOAD_DIR, s);
   }
 }
 



More information about the tor-commits mailing list