Browse Source

Add "+HSPOST" and related "HS_DESC" event flags to the controller.

"+HSPOST" and the related event changes allow the uploading of HS
descriptors via the control port, and more comprehensive event
monitoring of HS descriptor upload status.
Donncha O'Cearbhaill 10 years ago
parent
commit
841c4aa715
6 changed files with 246 additions and 22 deletions
  1. 3 0
      changes/feature3523
  2. 193 5
      src/or/control.c
  3. 9 0
      src/or/control.h
  4. 6 0
      src/or/directory.c
  5. 31 17
      src/or/rendservice.c
  6. 4 0
      src/or/rendservice.h

+ 3 - 0
changes/feature3523

@@ -0,0 +1,3 @@
+  o Major features (controller):
+    - New HSPOST command to upload a hidden service descriptor. 
+      Closes ticket 3523. Patch by "DonnchaC".

+ 193 - 5
src/or/control.c

@@ -36,17 +36,14 @@
 #include "networkstatus.h"
 #include "networkstatus.h"
 #include "nodelist.h"
 #include "nodelist.h"
 #include "policies.h"
 #include "policies.h"
-#include "rendcommon.h"
-#include "rendservice.h"
 #include "reasons.h"
 #include "reasons.h"
 #include "rendclient.h"
 #include "rendclient.h"
 #include "rendcommon.h"
 #include "rendcommon.h"
+#include "rendservice.h"
 #include "rephist.h"
 #include "rephist.h"
 #include "router.h"
 #include "router.h"
 #include "routerlist.h"
 #include "routerlist.h"
 #include "routerparse.h"
 #include "routerparse.h"
-#include "rendclient.h"
-#include "rendcommon.h"
 
 
 #ifndef _WIN32
 #ifndef _WIN32
 #include <pwd.h>
 #include <pwd.h>
@@ -170,6 +167,8 @@ static int handle_control_usefeature(control_connection_t *conn,
                                      const char *body);
                                      const char *body);
 static int handle_control_hsfetch(control_connection_t *conn, uint32_t len,
 static int handle_control_hsfetch(control_connection_t *conn, uint32_t len,
                                   const char *body);
                                   const char *body);
+static int handle_control_hspost(control_connection_t *conn, uint32_t len,
+                                 const char *body);
 static int handle_control_add_onion(control_connection_t *conn, uint32_t len,
 static int handle_control_add_onion(control_connection_t *conn, uint32_t len,
                                     const char *body);
                                     const char *body);
 static int handle_control_del_onion(control_connection_t *conn, uint32_t len,
 static int handle_control_del_onion(control_connection_t *conn, uint32_t len,
@@ -3424,6 +3423,101 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len,
   return 0;
   return 0;
 }
 }
 
 
+/** Implementation for the HSPOST command. */
+static int
+handle_control_hspost(control_connection_t *conn,
+                      uint32_t len,
+                      const char *body)
+{
+  static const char *opt_server = "SERVER=";
+  smartlist_t *args = smartlist_new();
+  smartlist_t *hs_dirs = NULL;
+  const char *encoded_desc = body;
+  size_t encoded_desc_len = len;
+
+  char *cp = memchr(body, '\n', len);
+  char *argline = tor_strndup(body, cp-body);
+
+  /* If any SERVER= options were specified, try parse the options line */
+  if (!strcasecmpstart(argline, opt_server)) {
+    /* encoded_desc begins after a newline character */
+    cp = cp + 1;
+    encoded_desc = cp;
+    encoded_desc_len = len-(cp-body);
+
+    smartlist_split_string(args, argline, " ",
+                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+    SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
+      if (!strcasecmpstart(arg, opt_server)) {
+        const char *server = arg + strlen(opt_server);
+        const node_t *node = node_get_by_hex_id(server);
+
+        if (!node) {
+          connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
+                                   server);
+          goto done;
+        }
+        if (!node->rs->is_hs_dir) {
+          connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir"
+                                         "\r\n", server);
+          goto done;
+        }
+        /* Valid server, add it to our local list. */
+        if (!hs_dirs)
+          hs_dirs = smartlist_new();
+        smartlist_add(hs_dirs, node->rs);
+      } else {
+        connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n",
+                                 arg);
+        goto done;
+      }
+    } SMARTLIST_FOREACH_END(arg);
+  }
+
+  /* Read the dot encoded descriptor, and parse it. */
+  rend_encoded_v2_service_descriptor_t *desc =
+      tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
+  read_escaped_data(encoded_desc, encoded_desc_len, &desc->desc_str);
+
+  rend_service_descriptor_t *parsed = NULL;
+  char *intro_content = NULL;
+  size_t intro_size;
+  size_t encoded_size;
+  const char *next_desc;
+  if (!rend_parse_v2_service_descriptor(&parsed, desc->desc_id, &intro_content,
+                                        &intro_size, &encoded_size,
+                                        &next_desc, desc->desc_str, 1)) {
+    /* Post the descriptor. */
+    char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
+    if (!rend_get_service_id(parsed->pk, serviceid)) {
+      smartlist_t *descs = smartlist_new();
+      smartlist_add(descs, desc);
+
+      /* We are about to trigger HS descriptor upload so send the OK now
+       * because after that 650 event(s) are possible so better to have the
+       * 250 OK before them to avoid out of order replies. */
+      send_control_done(conn);
+
+      /* Trigger the descriptor upload */
+      directory_post_to_hs_dir(parsed, descs, hs_dirs, serviceid, 0);
+      smartlist_free(descs);
+    }
+
+    rend_service_descriptor_free(parsed);
+  } else {
+    connection_printf_to_buf(conn, "554 Invalid descriptor\r\n");
+  }
+
+  tor_free(intro_content);
+  rend_encoded_v2_service_descriptor_free(desc);
+ done:
+  tor_free(argline);
+  smartlist_free(hs_dirs); /* Contents belong to the rend service code. */
+  SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
+  smartlist_free(args);
+  return 0;
+}
+
 /** Called when we get a ADD_ONION command; parse the body, and set up
 /** Called when we get a ADD_ONION command; parse the body, and set up
  * the new ephemeral Onion Service. */
  * the new ephemeral Onion Service. */
 static int
 static int
@@ -4075,6 +4169,9 @@ connection_control_process_inbuf(control_connection_t *conn)
   } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) {
   } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) {
     if (handle_control_hsfetch(conn, cmd_data_len, args))
     if (handle_control_hsfetch(conn, cmd_data_len, args))
       return -1;
       return -1;
+  } else if (!strcasecmp(conn->incoming_cmd, "+HSPOST")) {
+    if (handle_control_hspost(conn, cmd_data_len, args))
+      return -1;
   } else if (!strcasecmp(conn->incoming_cmd, "ADD_ONION")) {
   } else if (!strcasecmp(conn->incoming_cmd, "ADD_ONION")) {
     int ret = handle_control_add_onion(conn, cmd_data_len, args);
     int ret = handle_control_add_onion(conn, cmd_data_len, args);
     memwipe(args, 0, cmd_data_len); /* Scrub the private key. */
     memwipe(args, 0, cmd_data_len); /* Scrub the private key. */
@@ -5802,6 +5899,31 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query,
                      desc_id_base32);
                      desc_id_base32);
 }
 }
 
 
+/** send HS_DESC upload event.
+ *
+ * <b>service_id</b> is the descriptor onion address.
+ * <b>hs_dir</b> is the description of contacting hs directory.
+ * <b>desc_id_base32</b> is the ID of requested hs descriptor.
+ */
+void
+control_event_hs_descriptor_upload(const char *service_id,
+                                   const char *id_digest,
+                                   const char *desc_id_base32)
+{
+  if (!service_id || !id_digest || !desc_id_base32) {
+    log_warn(LD_BUG, "Called with service_digest==%p, "
+             "desc_id_base32==%p, id_digest==%p", service_id,
+             desc_id_base32, id_digest);
+    return;
+  }
+
+  send_control_event(EVENT_HS_DESC, ALL_FORMATS,
+                     "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n",
+                     service_id,
+                     node_describe_longname_by_id(id_digest),
+                     desc_id_base32);
+}
+
 /** send HS_DESC event after got response from hs directory.
 /** send HS_DESC event after got response from hs directory.
  *
  *
  * NOTE: this is an internal function used by following functions:
  * NOTE: this is an internal function used by following functions:
@@ -5840,9 +5962,43 @@ control_event_hs_descriptor_receive_end(const char *action,
   tor_free(reason_field);
   tor_free(reason_field);
 }
 }
 
 
+/** send HS_DESC event after got response from hs directory.
+ *
+ * NOTE: this is an internal function used by following functions:
+ * control_event_hs_descriptor_uploaded
+ * control_event_hs_descriptor_upload_failed
+ *
+ * So do not call this function directly.
+ */
+void
+control_event_hs_descriptor_upload_end(const char *action,
+                                       const char *id_digest,
+                                       const char *reason)
+{
+  char *reason_field = NULL;
+
+  if (!action || !id_digest) {
+    log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action,
+             id_digest);
+    return;
+  }
+
+  if (reason) {
+    tor_asprintf(&reason_field, " REASON=%s", reason);
+  }
+
+  send_control_event(EVENT_HS_DESC, ALL_FORMATS,
+                     "650 HS_DESC %s UNKNOWN UNKNOWN %s%s\r\n",
+                     action,
+                     node_describe_longname_by_id(id_digest),
+                     reason_field ? reason_field : "");
+
+  tor_free(reason_field);
+}
+
 /** send HS_DESC RECEIVED event
 /** send HS_DESC RECEIVED event
  *
  *
- * called when a we successfully received a hidden service descriptor.
+ * called when we successfully received a hidden service descriptor.
  */
  */
 void
 void
 control_event_hs_descriptor_received(const char *onion_address,
 control_event_hs_descriptor_received(const char *onion_address,
@@ -5857,6 +6013,21 @@ control_event_hs_descriptor_received(const char *onion_address,
                                           auth_type, id_digest, NULL);
                                           auth_type, id_digest, NULL);
 }
 }
 
 
+/** send HS_DESC UPLOADED event
+ *
+ * called when we successfully uploaded a hidden service descriptor.
+ */
+void
+control_event_hs_descriptor_uploaded(const char *id_digest)
+{
+  if (!id_digest) {
+    log_warn(LD_BUG, "Called with id_digest==%p",
+             id_digest);
+    return;
+  }
+  control_event_hs_descriptor_upload_end("UPLOADED", id_digest, NULL);
+}
+
 /** Send HS_DESC event to inform controller that query <b>rend_query</b>
 /** Send HS_DESC event to inform controller that query <b>rend_query</b>
  * failed to retrieve hidden service descriptor identified by
  * failed to retrieve hidden service descriptor identified by
  * <b>id_digest</b>. If <b>reason</b> is not NULL, add it to REASON=
  * <b>id_digest</b>. If <b>reason</b> is not NULL, add it to REASON=
@@ -5909,6 +6080,23 @@ control_event_hs_descriptor_content(const char *onion_address,
   tor_free(esc_content);
   tor_free(esc_content);
 }
 }
 
 
+/** Send HS_DESC event to inform controller upload of hidden service
+ * descriptor identified by <b>id_digest</b> failed. If <b>reason</b>
+ * is not NULL, add it to REASON= field.
+ */
+void
+control_event_hs_descriptor_upload_failed(const char *id_digest,
+                                          const char *reason)
+{
+  if (!id_digest) {
+    log_warn(LD_BUG, "Called with id_digest==%p",
+             id_digest);
+    return;
+  }
+  control_event_hs_descriptor_upload_end("UPLOAD_FAILED",
+                                         id_digest, reason);
+}
+
 /** Free any leftover allocated memory of the control.c subsystem. */
 /** Free any leftover allocated memory of the control.c subsystem. */
 void
 void
 control_free_all(void)
 control_free_all(void)

+ 9 - 0
src/or/control.h

@@ -106,18 +106,27 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
 void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
 void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
                                            const char *desc_id_base32,
                                            const char *desc_id_base32,
                                            const char *hs_dir);
                                            const char *hs_dir);
+void control_event_hs_descriptor_upload(const char *service_id,
+                                        const char *desc_id_base32,
+                                        const char *hs_dir);
 void control_event_hs_descriptor_receive_end(const char *action,
 void control_event_hs_descriptor_receive_end(const char *action,
                                              const char *onion_address,
                                              const char *onion_address,
                                              rend_auth_type_t auth_type,
                                              rend_auth_type_t auth_type,
                                              const char *id_digest,
                                              const char *id_digest,
                                              const char *reason);
                                              const char *reason);
+void control_event_hs_descriptor_upload_end(const char *action,
+                                            const char *hs_dir,
+                                            const char *reason);
 void control_event_hs_descriptor_received(const char *onion_address,
 void control_event_hs_descriptor_received(const char *onion_address,
                                           rend_auth_type_t auth_type,
                                           rend_auth_type_t auth_type,
                                           const char *id_digest);
                                           const char *id_digest);
+void control_event_hs_descriptor_uploaded(const char *hs_dir);
 void control_event_hs_descriptor_failed(const char *onion_address,
 void control_event_hs_descriptor_failed(const char *onion_address,
                                         rend_auth_type_t auth_type,
                                         rend_auth_type_t auth_type,
                                         const char *id_digest,
                                         const char *id_digest,
                                         const char *reason);
                                         const char *reason);
+void control_event_hs_descriptor_upload_failed(const char *hs_dir,
+                                               const char *reason);
 void control_event_hs_descriptor_content(const char *onion_address,
 void control_event_hs_descriptor_content(const char *onion_address,
                                          const char *desc_id,
                                          const char *desc_id,
                                          const char *hsdir_fp,
                                          const char *hsdir_fp,

+ 6 - 0
src/or/directory.c

@@ -2185,6 +2185,9 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
   }
   }
 
 
   if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
   if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
+    #define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \
+      control_event_hs_descriptor_upload_failed(conn->identity_digest, \
+                                                reason) )
     log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
     log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
              "(%s))",
              "(%s))",
              status_code, escaped(reason));
              status_code, escaped(reason));
@@ -2193,17 +2196,20 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
         log_info(LD_REND,
         log_info(LD_REND,
                  "Uploading rendezvous descriptor: finished with status "
                  "Uploading rendezvous descriptor: finished with status "
                  "200 (%s)", escaped(reason));
                  "200 (%s)", escaped(reason));
+        control_event_hs_descriptor_uploaded(conn->identity_digest);
         break;
         break;
       case 400:
       case 400:
         log_warn(LD_REND,"http status 400 (%s) response from dirserver "
         log_warn(LD_REND,"http status 400 (%s) response from dirserver "
                  "'%s:%d'. Malformed rendezvous descriptor?",
                  "'%s:%d'. Malformed rendezvous descriptor?",
                  escaped(reason), conn->base_.address, conn->base_.port);
                  escaped(reason), conn->base_.address, conn->base_.port);
+        SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED");
         break;
         break;
       default:
       default:
         log_warn(LD_REND,"http status %d (%s) response unexpected (server "
         log_warn(LD_REND,"http status %d (%s) response unexpected (server "
                  "'%s:%d').",
                  "'%s:%d').",
                  status_code, escaped(reason), conn->base_.address,
                  status_code, escaped(reason), conn->base_.address,
                  conn->base_.port);
                  conn->base_.port);
+        SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED");
         break;
         break;
     }
     }
   }
   }

+ 31 - 17
src/or/rendservice.c

@@ -15,6 +15,7 @@
 #include "circuitlist.h"
 #include "circuitlist.h"
 #include "circuituse.h"
 #include "circuituse.h"
 #include "config.h"
 #include "config.h"
+#include "control.h"
 #include "directory.h"
 #include "directory.h"
 #include "main.h"
 #include "main.h"
 #include "networkstatus.h"
 #include "networkstatus.h"
@@ -3098,14 +3099,16 @@ find_intro_point(origin_circuit_t *circ)
   return NULL;
   return NULL;
 }
 }
 
 
-/** Determine the responsible hidden service directories for the
- * rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them;
- * <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
- * purposes. */
-static void
+/** Upload the rend_encoded_v2_service_descriptor_t's in <b>descs</b>
+ * associated with the rend_service_descriptor_t <b>renddesc</b> to
+ * the responsible hidden service directories OR the hidden service
+ * directories specified by <b>hs_dirs</b>; <b>service_id</b> and
+ * <b>seconds_valid</b> are only passed for logging purposes.
+ */
+void
 directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
 directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
-                         smartlist_t *descs, const char *service_id,
-                         int seconds_valid)
+                         smartlist_t *descs, smartlist_t *hs_dirs,
+                         const char *service_id, int seconds_valid)
 {
 {
   int i, j, failed_upload = 0;
   int i, j, failed_upload = 0;
   smartlist_t *responsible_dirs = smartlist_new();
   smartlist_t *responsible_dirs = smartlist_new();
@@ -3113,14 +3116,21 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
   routerstatus_t *hs_dir;
   routerstatus_t *hs_dir;
   for (i = 0; i < smartlist_len(descs); i++) {
   for (i = 0; i < smartlist_len(descs); i++) {
     rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
     rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
-    /* Determine responsible dirs. */
-    if (hid_serv_get_responsible_directories(responsible_dirs,
-                                             desc->desc_id) < 0) {
-      log_warn(LD_REND, "Could not determine the responsible hidden service "
-                        "directories to post descriptors to.");
-      smartlist_free(responsible_dirs);
-      smartlist_free(successful_uploads);
-      return;
+    /** If any HSDirs are specified, they should be used instead of
+     *  the responsible directories */
+    if (hs_dirs && smartlist_len(hs_dirs) > 0) {
+      smartlist_add_all(responsible_dirs, hs_dirs);
+    } else {
+      /* Determine responsible dirs. */
+      if (hid_serv_get_responsible_directories(responsible_dirs,
+                                               desc->desc_id) < 0) {
+        log_warn(LD_REND, "Could not determine the responsible hidden service "
+                          "directories to post descriptors to.");
+        control_event_hs_descriptor_upload(service_id,
+                                           "UNKNOWN",
+                                           "UNKNOWN");
+        goto done;
+      }
     }
     }
     for (j = 0; j < smartlist_len(responsible_dirs); j++) {
     for (j = 0; j < smartlist_len(responsible_dirs); j++) {
       char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
       char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
@@ -3160,6 +3170,9 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
                hs_dir->nickname,
                hs_dir->nickname,
                hs_dir_ip,
                hs_dir_ip,
                hs_dir->or_port);
                hs_dir->or_port);
+      control_event_hs_descriptor_upload(service_id,
+                                         hs_dir->identity_digest,
+                                         desc_id_base32);
       tor_free(hs_dir_ip);
       tor_free(hs_dir_ip);
       /* Remember successful upload to this router for next time. */
       /* Remember successful upload to this router for next time. */
       if (!smartlist_contains_digest(successful_uploads,
       if (!smartlist_contains_digest(successful_uploads,
@@ -3187,6 +3200,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
       }
       }
     });
     });
   }
   }
+ done:
   smartlist_free(responsible_dirs);
   smartlist_free(responsible_dirs);
   smartlist_free(successful_uploads);
   smartlist_free(successful_uploads);
 }
 }
@@ -3251,7 +3265,7 @@ upload_service_descriptor(rend_service_t *service)
         rend_get_service_id(service->desc->pk, serviceid);
         rend_get_service_id(service->desc->pk, serviceid);
         log_info(LD_REND, "Launching upload for hidden service %s",
         log_info(LD_REND, "Launching upload for hidden service %s",
                      serviceid);
                      serviceid);
-        directory_post_to_hs_dir(service->desc, descs, serviceid,
+        directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
                                  seconds_valid);
                                  seconds_valid);
         /* Free memory for descriptors. */
         /* Free memory for descriptors. */
         for (i = 0; i < smartlist_len(descs); i++)
         for (i = 0; i < smartlist_len(descs); i++)
@@ -3280,7 +3294,7 @@ upload_service_descriptor(rend_service_t *service)
             smartlist_free(client_cookies);
             smartlist_free(client_cookies);
             return;
             return;
           }
           }
-          directory_post_to_hs_dir(service->desc, descs, serviceid,
+          directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
                                    seconds_valid);
                                    seconds_valid);
           /* Free memory for descriptors. */
           /* Free memory for descriptors. */
           for (i = 0; i < smartlist_len(descs); i++)
           for (i = 0; i < smartlist_len(descs); i++)

+ 4 - 0
src/or/rendservice.h

@@ -120,5 +120,9 @@ rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
                                char **service_id_out);
                                char **service_id_out);
 int rend_service_del_ephemeral(const char *service_id);
 int rend_service_del_ephemeral(const char *service_id);
 
 
+void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
+                              smartlist_t *descs, smartlist_t *hs_dirs,
+                              const char *service_id, int seconds_valid);
+
 #endif
 #endif