Browse Source

Launch the PIR Server subprocess as needed

You need to set the PIR_SERVER_PATH environment variable
and also ensure that "Sandbox" is _not_ set in the torrc
(since that prevents subprocesses from launching).  Note
that chutnet automatically sets "Sandbox 1" in its
torrc_templates/common.i file, so you'll want to comment that out.

The communication protocol between the tor process and the PIR server
subprocess is not yet implemented.
Ian Goldberg 3 years ago
parent
commit
593d6a74a5
3 changed files with 218 additions and 0 deletions
  1. 2 0
      src/feature/dircache/dircache.c
  2. 212 0
      src/feature/hs/hs_cache.c
  3. 4 0
      src/feature/rend/rendcache.c

+ 2 - 0
src/feature/dircache/dircache.c

@@ -1723,6 +1723,8 @@ directory_handle_command(dir_connection_t *conn)
   // we should escape headers here as well,
   // but we can't call escaped() twice, as it uses the same buffer
   //log_debug(LD_DIRSERV,"headers %s, body %s.", headers, escaped(body));
+  log_info(LD_DIRSERV,"DIRCACHE headers %s", escaped(headers));
+  log_info(LD_DIRSERV,"DIRCACHE body %s", escaped(body));
 
   if (!strncasecmp(headers,"GET",3))
     r = directory_handle_command_get(conn, headers, body, body_len);

+ 212 - 0
src/feature/hs/hs_cache.c

@@ -24,6 +24,17 @@
 
 #include "feature/nodelist/networkstatus_st.h"
 
+#define SUBPROCESS_PRIVATE
+#include "lib/process/env.h"
+#include "lib/process/subprocess.h"
+#include "lib/evloop/compat_libevent.h"
+
+#include <event2/event.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
 static int cached_client_descriptor_has_expired(time_t now,
            const hs_cache_client_descriptor_t *cached_desc);
 
@@ -117,6 +128,11 @@ cache_get_dir_entry_size(const hs_cache_dir_descriptor_t *entry)
           + strlen(entry->encoded_desc));
 }
 
+// PIRONION: v3 store
+
+static int
+hs_cache_pirserver_insert_desc(hs_cache_dir_descriptor_t *desc);
+
 /* Try to store a valid version 3 descriptor in the directory cache. Return 0
  * on success else a negative value is returned indicating that we have a
  * newer version in our cache. On error, caller is responsible to free the
@@ -155,6 +171,9 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
    * has been removed from the cache. */
   store_v3_desc_as_dir(desc);
 
+  /* Also send it to the PIR server if we have one. */
+  hs_cache_pirserver_insert_desc(desc);
+
   /* Update our total cache size with this entry for the OOM. This uses the
    * old HS protocol cache subsystem for which we are tied with. */
   rend_cache_increment_allocation(cache_get_dir_entry_size(desc));
@@ -167,6 +186,8 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
   return -1;
 }
 
+// PIRONION: v3 lookup
+
 /* Using the query which is the base64 encoded blinded key of a version 3
  * descriptor, lookup in our directory cache the entry. If found, 1 is
  * returned and desc_out is populated with a newly allocated string being the
@@ -955,6 +976,195 @@ hs_cache_get_max_descriptor_size(void)
                                             HS_DESC_MAX_LEN, 1, INT32_MAX);
 }
 
+static process_handle_t *pirserver;
+static struct event *pirserver_read_ev;
+
+typedef enum {
+    PIRSERVER_READSTATE_HEADER,
+    PIRSERVER_READSTATE_BODY
+} PIRServerReadState;
+
+#define PIRSERVER_READHDR_SIZE 13
+
+static void
+hs_cache_pirserver_received(const unsigned char *hdrbuf,
+    const char *bodybuf, size_t bodylen)
+{
+    /* PIRONION TODO: actually deliver the received message */
+    log_info(LD_DIRSERV,"PIRSERVER response header %p flag %d body len %ld body %s", *(void**)hdrbuf, hdrbuf[8], bodylen, escaped(bodybuf));
+}
+
+static void
+hs_cache_pirserver_recvcb(evutil_socket_t fd, short what,
+        ATTR_UNUSED void *arg) {
+    static PIRServerReadState readstate = PIRSERVER_READSTATE_HEADER;
+    static size_t readoff = 0;
+    static size_t readleft = PIRSERVER_READHDR_SIZE;
+    static unsigned char hdrbuf[PIRSERVER_READHDR_SIZE];
+    static char *bodybuf = NULL;
+
+    if (!(what & EV_READ)) {
+        /* Not sure why we're here */
+        return;
+    }
+
+    if (readstate == PIRSERVER_READSTATE_HEADER) {
+        int res = read(fd, hdrbuf + readoff, readleft);
+        if (res <= 0) return;
+        readoff += res;
+        readleft -= res;
+        if (readleft == 0) {
+            readleft = ntohl(*(uint32_t*)(hdrbuf+PIRSERVER_READHDR_SIZE-4));
+            free(bodybuf);
+            bodybuf = NULL;
+            if (readleft > 0) {
+                bodybuf = malloc(readleft);
+                readoff = 0;
+                readstate = PIRSERVER_READSTATE_BODY;
+            } else {
+                hs_cache_pirserver_received(hdrbuf, NULL, 0);
+                readoff = 0;
+                readleft = PIRSERVER_READHDR_SIZE;
+                readstate = PIRSERVER_READSTATE_HEADER;
+            }
+        }
+    } else if (readstate == PIRSERVER_READSTATE_BODY) {
+        int res = read(fd, bodybuf + readoff, readleft);
+        if (res <= 0) return;
+        readoff += res;
+        readleft -= res;
+        if (readleft == 0) {
+            /* Reading is complete */
+            hs_cache_pirserver_received(hdrbuf, bodybuf, readoff);
+
+            /* Prepare for the next output from the PIR server */
+            free(bodybuf);
+            bodybuf = NULL;
+            readoff = 0;
+            readleft = PIRSERVER_READHDR_SIZE;
+            readstate = PIRSERVER_READSTATE_HEADER;
+        }
+    }
+}
+
+/* Poke the hidden service cache PIR subsystem to launch the PIR
+   server if needed. */
+static void
+hs_cache_pir_poke(void)
+{
+    int res;
+    const char *pirserverpath = getenv("PIR_SERVER_PATH");
+    smartlist_t *env_vars = get_current_process_environment_variables();
+    const char *argv[2];
+    process_environment_t *env;
+
+    if (pirserver && pirserver->status == PROCESS_STATUS_RUNNING) {
+        /* The PIR server appears to be open */
+        return;
+    }
+
+    if (pirserver) {
+        if (pirserver_read_ev) {
+            event_free(pirserver_read_ev);
+            pirserver_read_ev = NULL;
+        }
+        tor_process_handle_destroy(pirserver, 1);
+        pirserver = NULL;
+    }
+
+    if (!pirserverpath) {
+        /* We don't have a configured PIR server */
+        return;
+    }
+
+    argv[0] = pirserverpath;
+    argv[1] = NULL;
+
+    env = process_environment_make(env_vars);
+
+    res = tor_spawn_background(pirserverpath, argv, env, &pirserver);
+
+    SMARTLIST_FOREACH(env_vars, void *, x, tor_free(x));
+    smartlist_free(env_vars);
+
+    if (res != PROCESS_STATUS_RUNNING) {
+        /* Launch failure */
+        return;
+    }
+
+    /* Create a libevent event to listen to the PIR server's responses. */
+    pirserver_read_ev = event_new(tor_libevent_get_base(),
+        pirserver->stdout_pipe, EV_READ|EV_PERSIST,
+        hs_cache_pirserver_recvcb, NULL);
+    event_add(pirserver_read_ev, NULL);
+}
+
+/* Initialize the hidden service cache PIR subsystem. */
+static void
+hs_cache_pir_init(void)
+{
+    pirserver = NULL;
+    pirserver_read_ev = NULL;
+}
+
+static int
+hs_cache_pirserver_send(const unsigned char *buf, size_t len)
+{
+    size_t written = 0;
+
+    hs_cache_pir_poke();
+    if (pirserver == NULL || pirserver->status != PROCESS_STATUS_RUNNING) {
+        /* Launch failed */
+        return -1;
+    }
+
+    /* PIRONION TODO: For now, we're going to assume that writing to the
+     * pir server will never block, but we should actually put this into
+     * a write buffer. */
+    while (len) {
+        ssize_t res = write(pirserver->stdin_pipe, buf, len);
+        if (res < 0) return res;
+        if (res == 0) return written;
+        written += res;
+        buf += res;
+        len -= res;
+    }
+    return written;
+}
+
+static int
+hs_cache_pirserver_insert_desc(hs_cache_dir_descriptor_t *desc)
+{
+    unsigned char hdr[13];
+    size_t encoded_desc_len;
+    size_t len;
+    int res;
+    int written = 0;
+
+    /* PIRONION TODO: This isn't the real insertion protocol yet. */
+
+    memmove(hdr, "12345678\x01", 9);
+    encoded_desc_len = strlen(desc->encoded_desc);
+    len = DIGEST256_LEN + encoded_desc_len;
+    *(uint32_t*)(hdr+9) = htonl(len);
+
+    res = hs_cache_pirserver_send(hdr, 13);
+    if (res <= 0) return -1;
+    written += res;
+
+    res = hs_cache_pirserver_send(desc->key, DIGEST256_LEN);
+    if (res <= 0) return -1;
+    written += res;
+
+    res = hs_cache_pirserver_send(
+            (const unsigned char *)desc->encoded_desc,
+            encoded_desc_len);
+    if (res <= 0) return -1;
+    written += res;
+
+    return written;
+}
+
 /* Initialize the hidden service cache subsystem. */
 void
 hs_cache_init(void)
@@ -968,6 +1178,8 @@ hs_cache_init(void)
 
   tor_assert(!hs_cache_client_intro_state);
   hs_cache_client_intro_state = digest256map_new();
+
+  hs_cache_pir_init();
 }
 
 /* Cleanup the hidden service cache subsystem. */

+ 4 - 0
src/feature/rend/rendcache.c

@@ -582,6 +582,8 @@ rend_cache_lookup_v2_desc_as_service(const char *query, rend_cache_entry_t **e)
   return ret;
 }
 
+// PIRONION: v2 lookup
+
 /** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
  * copy the pointer to it to *<b>desc</b>.  Return 1 on success, 0 on
  * well-formed-but-not-found, and -1 on failure.
@@ -610,6 +612,8 @@ rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc)
   return 0;
 }
 
+// PIRONION: v2 store
+
 /** Parse the v2 service descriptor(s) in <b>desc</b> and store it/them to the
  * local rend cache. Don't attempt to decrypt the included list of introduction
  * points (as we don't have a descriptor cookie for it).