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