📄 directory.c
字号:
strlcpy(conn->rend_query, payload, sizeof(conn->rend_query));
conn->rend_version = 2;
payload = NULL;
httpcommand = "GET";
len = strlen(resource) + 32;
url = tor_malloc(len);
tor_snprintf(url, len, "/tor/rendezvous2/%s", resource);
break;
case DIR_PURPOSE_UPLOAD_RENDDESC:
tor_assert(!resource);
tor_assert(payload);
httpcommand = "POST";
url = tor_strdup("/tor/rendezvous/publish");
break;
case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
tor_assert(!resource);
tor_assert(payload);
httpcommand = "POST";
url = tor_strdup("/tor/rendezvous2/publish");
break;
default:
tor_assert(0);
return;
}
if (strlen(proxystring) + strlen(url) >= 4096) {
log_warn(LD_BUG,
"Squid does not like URLs longer than 4095 bytes, and this "
"one is %d bytes long: %s%s",
(int)(strlen(proxystring) + strlen(url)), proxystring, url);
}
tor_snprintf(request, sizeof(request), "%s %s", httpcommand, proxystring);
connection_write_to_buf(request, strlen(request), TO_CONN(conn));
connection_write_to_buf(url, strlen(url), TO_CONN(conn));
tor_free(url);
if (!strcmp(httpcommand, "GET") && !payload) {
tor_snprintf(request, sizeof(request),
" HTTP/1.0\r\nHost: %s%s%s\r\n\r\n",
hoststring,
imsstring,
proxyauthstring);
} else {
tor_snprintf(request, sizeof(request),
" HTTP/1.0\r\nContent-Length: %lu\r\nHost: %s%s%s\r\n\r\n",
payload ? (unsigned long)payload_len : 0,
hoststring,
imsstring,
proxyauthstring);
}
connection_write_to_buf(request, strlen(request), TO_CONN(conn));
if (payload) {
/* then send the payload afterwards too */
connection_write_to_buf(payload, payload_len, TO_CONN(conn));
}
}
/** Parse an HTTP request string <b>headers</b> of the form
* \verbatim
* "\%s [http[s]://]\%s HTTP/1..."
* \endverbatim
* If it's well-formed, strdup the second \%s into *<b>url</b>, and
* nul-terminate it. If the url doesn't start with "/tor/", rewrite it
* so it does. Return 0.
* Otherwise, return -1.
*/
static int
parse_http_url(const char *headers, char **url)
{
char *s, *start, *tmp;
s = (char *)eat_whitespace_no_nl(headers);
if (!*s) return -1;
s = (char *)find_whitespace(s); /* get past GET/POST */
if (!*s) return -1;
s = (char *)eat_whitespace_no_nl(s);
if (!*s) return -1;
start = s; /* this is it, assuming it's valid */
s = (char *)find_whitespace(start);
if (!*s) return -1;
/* tolerate the http[s] proxy style of putting the hostname in the url */
if (s-start >= 4 && !strcmpstart(start,"http")) {
tmp = start + 4;
if (*tmp == 's')
tmp++;
if (s-tmp >= 3 && !strcmpstart(tmp,"://")) {
tmp = strchr(tmp+3, '/');
if (tmp && tmp < s) {
log_debug(LD_DIR,"Skipping over 'http[s]://hostname' string");
start = tmp;
}
}
}
if (s-start < 5 || strcmpstart(start,"/tor/")) { /* need to rewrite it */
*url = tor_malloc(s - start + 5);
strlcpy(*url,"/tor", s-start+5);
strlcat((*url)+4, start, s-start+1);
} else {
*url = tor_strndup(start, s-start);
}
return 0;
}
/** Return a copy of the first HTTP header in <b>headers</b> whose key is
* <b>which</b>. The key should be given with a terminating colon and space;
* this function copies everything after, up to but not including the
* following \\r\\n. */
static char *
http_get_header(const char *headers, const char *which)
{
const char *cp = headers;
while (cp) {
if (!strcasecmpstart(cp, which)) {
char *eos;
cp += strlen(which);
if ((eos = strchr(cp,'\r')))
return tor_strndup(cp, eos-cp);
else
return tor_strdup(cp);
}
cp = strchr(cp, '\n');
if (cp)
++cp;
}
return NULL;
}
/** If <b>headers</b> indicates that a proxy was involved, then rewrite
* <b>conn</b>-\>address to describe our best guess of the address that
* originated this HTTP request. */
static void
http_set_address_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) {
struct in_addr in;
if (!tor_inet_aton(fwd, &in) || is_internal_IP(ntohl(in.s_addr), 0)) {
log_debug(LD_DIR, "Ignoring unrecognized or internal IP %s",
escaped(fwd));
tor_free(fwd);
return;
}
tor_free(conn->address);
conn->address = tor_strdup(fwd);
tor_free(fwd);
}
}
/** Parse an HTTP response string <b>headers</b> of the form
* \verbatim
* "HTTP/1.\%d \%d\%s\r\n...".
* \endverbatim
*
* If it's well-formed, assign the status code to *<b>code</b> and
* return 0. Otherwise, return -1.
*
* On success: If <b>date</b> is provided, set *date to the Date
* header in the http headers, or 0 if no such header is found. If
* <b>compression</b> is provided, set *<b>compression</b> to the
* compression method given in the Content-Encoding header, or 0 if no
* such header is found, or -1 if the value of the header is not
* recognized. If <b>reason</b> is provided, strdup the reason string
* into it.
*/
int
parse_http_response(const char *headers, int *code, time_t *date,
compress_method_t *compression, char **reason)
{
int n1, n2;
char datestr[RFC1123_TIME_LEN+1];
smartlist_t *parsed_headers;
tor_assert(headers);
tor_assert(code);
while (TOR_ISSPACE(*headers)) headers++; /* tolerate leading whitespace */
if (sscanf(headers, "HTTP/1.%d %d", &n1, &n2) < 2 ||
(n1 != 0 && n1 != 1) ||
(n2 < 100 || n2 >= 600)) {
log_warn(LD_HTTP,"Failed to parse header %s",escaped(headers));
return -1;
}
*code = n2;
parsed_headers = smartlist_create();
smartlist_split_string(parsed_headers, headers, "\n",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
if (reason) {
smartlist_t *status_line_elements = smartlist_create();
tor_assert(smartlist_len(parsed_headers));
smartlist_split_string(status_line_elements,
smartlist_get(parsed_headers, 0),
" ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
tor_assert(smartlist_len(status_line_elements) <= 3);
if (smartlist_len(status_line_elements) == 3) {
*reason = smartlist_get(status_line_elements, 2);
smartlist_set(status_line_elements, 2, NULL); /* Prevent free */
}
SMARTLIST_FOREACH(status_line_elements, char *, cp, tor_free(cp));
smartlist_free(status_line_elements);
}
if (date) {
*date = 0;
SMARTLIST_FOREACH(parsed_headers, const char *, s,
if (!strcmpstart(s, "Date: ")) {
strlcpy(datestr, s+6, sizeof(datestr));
/* This will do nothing on failure, so we don't need to check
the result. We shouldn't warn, since there are many other valid
date formats besides the one we use. */
parse_rfc1123_time(datestr, date);
break;
});
}
if (compression) {
const char *enc = NULL;
SMARTLIST_FOREACH(parsed_headers, const char *, s,
if (!strcmpstart(s, "Content-Encoding: ")) {
enc = s+18; break;
});
if (!enc || !strcmp(enc, "identity")) {
*compression = NO_METHOD;
} else if (!strcmp(enc, "deflate") || !strcmp(enc, "x-deflate")) {
*compression = ZLIB_METHOD;
} else if (!strcmp(enc, "gzip") || !strcmp(enc, "x-gzip")) {
*compression = GZIP_METHOD;
} else {
log_info(LD_HTTP, "Unrecognized content encoding: %s. Trying to deal.",
escaped(enc));
*compression = UNKNOWN_METHOD;
}
}
SMARTLIST_FOREACH(parsed_headers, char *, s, tor_free(s));
smartlist_free(parsed_headers);
return 0;
}
/** Return true iff <b>body</b> doesn't start with a plausible router or
* running-list or directory opening. This is a sign of possible compression.
**/
static int
body_is_plausible(const char *body, size_t len, int purpose)
{
int i;
if (len == 0)
return 1; /* empty bodies don't need decompression */
if (len < 32)
return 0;
if (purpose != DIR_PURPOSE_FETCH_RENDDESC) {
if (!strcmpstart(body,"router") ||
!strcmpstart(body,"signed-directory") ||
!strcmpstart(body,"network-status") ||
!strcmpstart(body,"running-routers"))
return 1;
for (i=0;i<32;++i) {
if (!TOR_ISPRINT(body[i]) && !TOR_ISSPACE(body[i]))
return 0;
}
return 1;
} else {
return 1;
}
}
/** Called when we've just fetched a bunch of router descriptors in
* <b>body</b>. The list <b>which</b>, if present, holds digests for
* descriptors we requested: descriptor digests if <b>descriptor_digests</b>
* is true, or identity digests otherwise. Parse the descriptors, validate
* them, and annotate them as having purpose <b>purpose</b> and as having been
* downloaded from <b>source</b>. */
static void
load_downloaded_routers(const char *body, smartlist_t *which,
int descriptor_digests,
int router_purpose,
const char *source)
{
char buf[256];
char time_buf[ISO_TIME_LEN+1];
int general = router_purpose == ROUTER_PURPOSE_GENERAL;
format_iso_time(time_buf, time(NULL));
tor_assert(source);
if (tor_snprintf(buf, sizeof(buf),
"@downloaded-at %s\n"
"@source %s\n"
"%s%s%s", time_buf, escaped(source),
!general ? "@purpose " : "",
!general ? router_purpose_to_string(router_purpose) : "",
!general ? "\n" : "")<0)
return;
router_load_routers_from_string(body, NULL, SAVED_NOWHERE, which,
descriptor_digests, buf);
}
/** We are a client, and we've finished reading the server's
* response. Parse it and act appropriately.
*
* If we're still happy with using this directory server in the future, return
* 0. Otherwise return -1; and the caller should consider trying the request
* again.
*
* The caller will take care of marking the connection for close.
*/
static int
connection_dir_client_reached_eof(dir_connection_t *conn)
{
char *body;
char *headers;
char *reason = NULL;
size_t body_len=0, orig_len=0;
int status_code;
time_t date_header=0;
long delta;
compress_method_t compression;
int plausible;
int skewed=0;
int allow_partial = (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO);
int was_compressed=0;
time_t now = time(NULL);
switch (fetch_from_buf_http(conn->_base.inbuf,
&headers, MAX_HEADERS_SIZE,
&body, &body_len, MAX_DIR_DL_SIZE,
allow_partial)) {
case -1: /* overflow */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -