Browse Source

Start the pirserver code

Ian Goldberg 3 years ago
commit
746f980c29
4 changed files with 209 additions and 0 deletions
  1. 2 0
      Makefile
  2. 113 0
      pirserver.cc
  3. 33 0
      pirserver.h
  4. 61 0
      protocol

+ 2 - 0
Makefile

@@ -0,0 +1,2 @@
+CXXFLAGS=-g -Wall
+

+ 113 - 0
pirserver.cc

@@ -0,0 +1,113 @@
+// Implementation of the main loop of the pirserver, responsible for the
+// communication with the tor process.  All the actual private lookup
+// work is done by an appropriate subclass of PIRServer.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include "pirserver.h"
+
+#define PIRSERVER_HDR_SIZE 13
+
+#define PIRSERVER_REQUEST_PARAMS 0x01
+#define PIRSERVER_REQUEST_STORE 0x02
+#define PIRSERVER_REQUEST_LOOKUP 0x03
+
+#define PIRSERVER_RESPONSE_PARAMS 0xFF
+#define PIRSERVER_RESPONSE_LOOKUP_SUCCESS 0xFE
+#define PIRSERVER_RESPONSE_LOOKUP_FAILURE 0xFD
+
+static int
+read_all(char *buf, size_t len)
+{
+    int tot_read = 0;
+    while(len > 0) {
+        int res = read(0, buf, len);
+        if (res <= 0) return res;
+        buf += res;
+        len -= res;
+        tot_read += res;
+    }
+    return tot_read;
+}
+
+static int
+write_all(const char *buf, size_t len)
+{
+    int tot_written = 0;
+    while(len > 0) {
+        int res = write(1, buf, len);
+        if (res <= 0) return res;
+        buf += res;
+        len -= res;
+        tot_written += res;
+    }
+    return tot_written;
+}
+
+void
+PIRServer::mainloop()
+{
+    char header[PIRSERVER_HDR_SIZE];
+    size_t bodylen = 0;
+    char *body = NULL;
+    string query, response;
+    size_t response_len;
+    while(1) {
+        // Read the request from stdin
+        int res = read_all(header, PIRSERVER_HDR_SIZE);
+        if (res <= 0) return; // stdin has reached EOF (or error); we
+                              //  will terminate
+        bodylen = ntohl(*(uint32_t*)(header+PIRSERVER_HDR_SIZE-4));
+        if (bodylen > 0) {
+            body = (char *)malloc(bodylen);
+            res = read_all(body, bodylen);
+            if (res <= 0) return;
+        }
+
+        // We have a complete request.  Dispatch it.
+        switch(header[8]) {
+        case PIRSERVER_REQUEST_PARAMS:
+            get_params(response);
+            response_len = response.length();
+            header[8] = PIRSERVER_RESPONSE_PARAMS;
+            *(uint32_t*)(header+PIRSERVER_HDR_SIZE-4) = htonl(response_len);
+            res = write_all(header, PIRSERVER_HDR_SIZE);
+            if (res <= 0) return;
+            if (response_len > 0) {
+                res = write_all(response.c_str(), response_len);
+                if (res <= 0) return;
+            }
+            break;
+        case PIRSERVER_REQUEST_STORE:
+            if (bodylen >= 32) {
+                string key(body, 32);
+                string value(body+32, bodylen-32);
+                store(key, value);
+            }
+            break;
+        case PIRSERVER_REQUEST_LOOKUP:
+            query.assign(body, bodylen);
+            if (lookup(query, response)) {
+                response_len = response.length();
+                header[8] = PIRSERVER_RESPONSE_LOOKUP_SUCCESS;
+            } else {
+                response_len = 0;
+                header[8] = PIRSERVER_RESPONSE_LOOKUP_FAILURE;
+            }
+            *(uint32_t*)(header+PIRSERVER_HDR_SIZE-4) = htonl(response_len);
+            res = write_all(header, PIRSERVER_HDR_SIZE);
+            if (res <= 0) return;
+            if (response_len > 0) {
+                res = write_all(response.c_str(), response_len);
+                if (res <= 0) return;
+            }
+            break;
+        }
+
+        // Clean up for the next request.
+        free(body);
+        body = NULL;
+        bodylen = 0;
+    }
+}

+ 33 - 0
pirserver.h

@@ -0,0 +1,33 @@
+#ifndef __PIRSERVER_H__
+#define __PIRSERVER_H__
+
+#include <string>
+
+using std::string;
+
+// Derive a class from this, implementing the virtual functions, and
+// call mainloop().
+
+class PIRServer {
+public:
+    // Fill the current params into the passed string argument
+    virtual void get_params(string &params) = 0;
+
+    // Store the given value at the given key.  If the value is the
+    // empty string, delete the key.  If the key has already been
+    // stored, overwrite the value with this one.  The key will be
+    // exactly 32 bytes long.
+    virtual void store(const string &key, const string &value) = 0;
+
+    // Perform a private lookup.  The client's private query message is
+    // lookup_query.  If successful, return true and fill
+    // lookup_response with the private response.  If unsuccessful,
+    // return false.
+    virtual bool lookup(const string &lookup_query,
+                        string &lookup_response) = 0;
+
+    // Call this to run the server, and exit when it returns
+    void mainloop();
+};
+
+#endif

+ 61 - 0
protocol

@@ -0,0 +1,61 @@
+The tor process will launch the pirserver process, and hold pipes to the
+pirserver's stdin, stdout, and stderr.  All communication between the
+processes is over those pipes.  Anything the pirserver outputs over the
+stderr pipe should be logged by the tor process.  If the pirserver finds
+its stdin has reached EOF, it should terminate.
+
+The general format of request messages (tor -> pirserver) is:
+
+request_id: 8 bytes
+request_type: 1 byte
+body_len: 4 bytes (big-endian unsigned int)
+body: (body_len) bytes
+
+The general format of response messages (pirserver -> tor) is:
+
+request_id: 8 bytes (must match the request_id being responded to)
+response_type: 1 byte
+body_len: 4 bytes (big-endian unsigned int)
+body: (body_len) bytes
+
+There are three request types:
+
+PIRSERVER_REQUEST_PARAMS (0x01)
+Request the current pirserver params data
+    The body_len must be 0.  The pirserver will respond with
+    PIRSERVER_RESPONSE_PARAMS. (TODO: do we want to allow for an error
+    response to this request?)
+
+PIRSERVER_REQUEST_STORE (0x02)
+Store an object with a given 32-byte key
+    The body_len must be at least 32.  The first 32 bytes of the body
+    are the key under which to store the object, and the remaining bytes
+    are the object.  If body_len is exactly 32, it means to remove the
+    object with the specified key.  Storing an object with an
+    already-existing key will overwrite the old object with that key.
+    This request does not elicit a response from the pirserver.
+
+PIRSERVER_REQUEST_LOOKUP (0x03)
+Privately look up an object
+    The body_len will depend on the details of the particular private
+    lookup scheme (XPIR, ZeroTrace, trivial, etc.).
+
+There are three response types:
+
+PIRSERVER_RESPONSE_PARAMS (0xFF)
+Reply with the current pirserver params data
+    The body is the params data, which will be sent to the client either
+    in an HTTP 200 reply to an explicit params request, or else in a
+    special cell piggybacked on the creation of the directory lookup
+    circuit.  The body_len will depend on the details on the particular
+    private lookup scheme.
+
+PIRSERVER_RESPONSE_LOOKUP_SUCCESS (0xFE)
+Response to a successful lookup
+    The body is the result of the private lookup requested by the
+    corresponding PIRSERVER_REQUEST_LOOKUP.  The body_len will depend on
+    the details on the particular private lookup scheme.
+
+PIRSERVER_RESPONSE_LOOKUP_FAILURE (0xFD)
+Response to a failed lookup
+    The body_len must be 0.