Commit 841c4aa7 authored by Donncha O'Cearbhaill's avatar Donncha O'Cearbhaill Committed by Nick Mathewson
Browse files

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.
parent 411049d0
Loading
Loading
Loading
Loading

changes/feature3523

0 → 100644
+3 −0
Original line number Diff line number Diff line
  o Major features (controller):
    - New HSPOST command to upload a hidden service descriptor. 
      Closes ticket 3523. Patch by "DonnchaC".
+193 −5
Original line number Diff line number Diff line
@@ -36,17 +36,14 @@
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
#include "rendcommon.h"
#include "rendservice.h"
#include "reasons.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
#include "rendclient.h"
#include "rendcommon.h"

#ifndef _WIN32
#include <pwd.h>
@@ -170,6 +167,8 @@ static int handle_control_usefeature(control_connection_t *conn,
                                     const char *body);
static int handle_control_hsfetch(control_connection_t *conn, uint32_t len,
                                  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,
                                    const char *body);
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;
}

/** 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
 * the new ephemeral Onion Service. */
static int
@@ -4075,6 +4169,9 @@ connection_control_process_inbuf(control_connection_t *conn)
  } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) {
    if (handle_control_hsfetch(conn, cmd_data_len, args))
      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")) {
    int ret = handle_control_add_onion(conn, cmd_data_len, args);
    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);
}

/** 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.
 *
 * 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);
}

/** 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
 *
 * called when a we successfully received a hidden service descriptor.
 * called when we successfully received a hidden service descriptor.
 */
void
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);
}

/** 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>
 * failed to retrieve hidden service descriptor identified by
 * <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);
}

/** 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. */
void
control_free_all(void)
+9 −0
Original line number Diff line number Diff line
@@ -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,
                                           const char *desc_id_base32,
                                           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,
                                             const char *onion_address,
                                             rend_auth_type_t auth_type,
                                             const char *id_digest,
                                             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,
                                          rend_auth_type_t auth_type,
                                          const char *id_digest);
void control_event_hs_descriptor_uploaded(const char *hs_dir);
void control_event_hs_descriptor_failed(const char *onion_address,
                                        rend_auth_type_t auth_type,
                                        const char *id_digest,
                                        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,
                                         const char *desc_id,
                                         const char *hsdir_fp,
+6 −0
Original line number Diff line number Diff line
@@ -2185,6 +2185,9 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
  }

  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 "
             "(%s))",
             status_code, escaped(reason));
@@ -2193,17 +2196,20 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
        log_info(LD_REND,
                 "Uploading rendezvous descriptor: finished with status "
                 "200 (%s)", escaped(reason));
        control_event_hs_descriptor_uploaded(conn->identity_digest);
        break;
      case 400:
        log_warn(LD_REND,"http status 400 (%s) response from dirserver "
                 "'%s:%d'. Malformed rendezvous descriptor?",
                 escaped(reason), conn->base_.address, conn->base_.port);
        SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED");
        break;
      default:
        log_warn(LD_REND,"http status %d (%s) response unexpected (server "
                 "'%s:%d').",
                 status_code, escaped(reason), conn->base_.address,
                 conn->base_.port);
        SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED");
        break;
    }
  }
+31 −17
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
#include "control.h"
#include "directory.h"
#include "main.h"
#include "networkstatus.h"
@@ -3098,14 +3099,16 @@ find_intro_point(origin_circuit_t *circ)
  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,
                         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;
  smartlist_t *responsible_dirs = smartlist_new();
@@ -3113,14 +3116,21 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
  routerstatus_t *hs_dir;
  for (i = 0; i < smartlist_len(descs); i++) {
    rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
    /** 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.");
      smartlist_free(responsible_dirs);
      smartlist_free(successful_uploads);
      return;
        control_event_hs_descriptor_upload(service_id,
                                           "UNKNOWN",
                                           "UNKNOWN");
        goto done;
      }
    }
    for (j = 0; j < smartlist_len(responsible_dirs); j++) {
      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_ip,
               hs_dir->or_port);
      control_event_hs_descriptor_upload(service_id,
                                         hs_dir->identity_digest,
                                         desc_id_base32);
      tor_free(hs_dir_ip);
      /* Remember successful upload to this router for next time. */
      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(successful_uploads);
}
@@ -3251,7 +3265,7 @@ upload_service_descriptor(rend_service_t *service)
        rend_get_service_id(service->desc->pk, serviceid);
        log_info(LD_REND, "Launching upload for hidden service %s",
                     serviceid);
        directory_post_to_hs_dir(service->desc, descs, serviceid,
        directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
                                 seconds_valid);
        /* Free memory for descriptors. */
        for (i = 0; i < smartlist_len(descs); i++)
@@ -3280,7 +3294,7 @@ upload_service_descriptor(rend_service_t *service)
            smartlist_free(client_cookies);
            return;
          }
          directory_post_to_hs_dir(service->desc, descs, serviceid,
          directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
                                   seconds_valid);
          /* Free memory for descriptors. */
          for (i = 0; i < smartlist_len(descs); i++)
Loading