diff --git a/src/or/directory.c b/src/or/directory.c
index 47aeb78523..66f2d39cda 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -49,6 +49,8 @@ directory_send_command(connection_t *conn, const char *platform,
static int directory_handle_command(connection_t *conn);
static int body_is_plausible(const char *body, size_t body_len, int purpose);
static int purpose_is_private(uint8_t purpose);
+static char *http_get_header(const char *headers, const char *which);
+static char *http_get_origin(const char *headers, connection_t *conn);
/********* START VARIABLES **********/
@@ -508,6 +510,56 @@ parse_http_url(char *headers, char **url)
return 0;
}
+/** Return a copy of the first HTTP header in headers whose key is
+ * which. The key should be given with a terminating colon and space;
+ * this function copies everything after, up to but not including the
+ * following newline. */
+static char *
+http_get_header(const char *headers, const char *which)
+{
+ const char *cp = headers;
+ while (cp) {
+ if (!strcmpstart(cp, which)) {
+ char *eos;
+ cp += strlen(which);
+ if ((eos = strchr(cp,'\n')))
+ return tor_strndup(cp, eos-cp);
+ else
+ return tor_strdup(cp);
+ }
+ cp = strchr(headers, '\n');
+ if (cp)
+ ++cp;
+ }
+ return NULL;
+}
+
+/** Allocate and return a string describing the source of an HTTP request with
+ * headers headers received on conn. The format is either
+ * "'1.2.3.4'", or "'1.2.3.4' (forwarded for '5.6.7.8')".
+ */
+static char *
+http_get_origin(const char *headers, connection_t *conn)
+{
+ char *fwd;
+
+ fwd = http_get_header(headers, "Forwarded-For: ");
+ if (!fwd)
+ fwd = http_get_header(headers, "X-Forwarded-For: ");
+ if (fwd) {
+ size_t len = strlen(fwd)+strlen(conn->address)+32;
+ char *result = tor_malloc(len);
+ tor_snprintf(result, len, "'%s' (forwarded for '%s')", conn->address, fwd);
+ tor_free(fwd);
+ return result;
+ } else {
+ size_t len = strlen(conn->address)+3;
+ char *result = tor_malloc(len);
+ tor_snprintf(result, len, "'%s'", conn->address);
+ return result;
+ }
+}
+
/** Parse an HTTP response string headers of the form
* \verbatim
* "HTTP/1.\%d \%d\%s\r\n...".
@@ -942,7 +994,7 @@ directory_handle_command_get(connection_t *conn, char *headers,
{
size_t dlen;
const char *cp;
- char *url;
+ char *url = NULL;
char tmp[8192];
char date[RFC1123_TIME_LEN+1];
@@ -1060,7 +1112,8 @@ directory_handle_command_post(connection_t *conn, char *headers,
char *body, size_t body_len)
{
const char *cp;
- char *url;
+ char *origin = NULL;
+ char *url = NULL;
log_fn(LOG_DEBUG,"Received POST command.");
@@ -1078,6 +1131,7 @@ directory_handle_command_post(connection_t *conn, char *headers,
return 0;
}
log_fn(LOG_INFO,"rewritten url as '%s'.", url);
+ origin = http_get_origin(headers, conn);
if (!strcmp(url,"/tor/")) { /* server descriptor post */
const char *msg;
@@ -1087,7 +1141,7 @@ directory_handle_command_post(connection_t *conn, char *headers,
case -1:
/* malformed descriptor, or something wrong */
write_http_status_line(conn, 400, msg?msg:"Malformed or unacceptable server descriptor");
- log_fn(LOG_NOTICE,"Rejected descriptor published by '%s'.", conn->address);
+ log_fn(LOG_NOTICE,"Rejected descriptor published by %s.", origin);
break;
case 0:
/* descriptor was well-formed but server has not been approved */
@@ -1098,8 +1152,7 @@ directory_handle_command_post(connection_t *conn, char *headers,
write_http_status_line(conn, 200, msg?msg:"Verified server descriptor accepted");
break;
}
- tor_free(url);
- return 0;
+ goto done;
}
if (!strcmpstart(url,"/tor/rendezvous/publish")) {
@@ -1108,14 +1161,17 @@ directory_handle_command_post(connection_t *conn, char *headers,
write_http_status_line(conn, 400, "Invalid service descriptor rejected");
else
write_http_status_line(conn, 200, "Service descriptor stored");
- tor_free(url);
- return 0;
+ goto done;
}
/* we didn't recognize the url */
write_http_status_line(conn, 404, "Not found");
+
+ done:
tor_free(url);
+ tor_free(origin);
return 0;
+
}
/** Called when a dirserver receives data on a directory connection;