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.
This commit is contained in:
Donncha O'Cearbhaill 2015-03-22 13:31:53 +00:00 committed by Nick Mathewson
parent 411049d0d4
commit 841c4aa715
6 changed files with 246 additions and 22 deletions

3
changes/feature3523 Normal file
View File

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

View File

@ -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)

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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);
/* 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++) {
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++)

View File

@ -120,5 +120,9 @@ rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
char **service_id_out);
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