mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 04:13:28 +01:00
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:
parent
411049d0d4
commit
841c4aa715
3
changes/feature3523
Normal file
3
changes/feature3523
Normal file
@ -0,0 +1,3 @@
|
||||
o Major features (controller):
|
||||
- New HSPOST command to upload a hidden service descriptor.
|
||||
Closes ticket 3523. Patch by "DonnchaC".
|
198
src/or/control.c
198
src/or/control.c
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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++)
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user