30001: Fix a race condition in test_dir_handle_get.c
[tor.git] / src / feature / dircache / dircache.c
blob1123d034e009afdcdfd1bc269eeb90a08a80358d
1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2019, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
6 #define DIRCACHE_PRIVATE
8 #include "core/or/or.h"
10 #include "app/config/config.h"
11 #include "core/mainloop/connection.h"
12 #include "core/or/relay.h"
13 #include "feature/dirauth/dirvote.h"
14 #include "feature/dirauth/authmode.h"
15 #include "feature/dirauth/process_descs.h"
16 #include "feature/dircache/conscache.h"
17 #include "feature/dircache/consdiffmgr.h"
18 #include "feature/dircache/dircache.h"
19 #include "feature/dircache/dirserv.h"
20 #include "feature/dircommon/directory.h"
21 #include "feature/dircommon/fp_pair.h"
22 #include "feature/hs/hs_cache.h"
23 #include "feature/nodelist/authcert.h"
24 #include "feature/nodelist/networkstatus.h"
25 #include "feature/nodelist/routerlist.h"
26 #include "feature/relay/routermode.h"
27 #include "feature/rend/rendcache.h"
28 #include "feature/stats/geoip_stats.h"
29 #include "feature/stats/rephist.h"
30 #include "lib/compress/compress.h"
32 #include "feature/dircache/cached_dir_st.h"
33 #include "feature/dircommon/dir_connection_st.h"
34 #include "feature/nodelist/authority_cert_st.h"
35 #include "feature/nodelist/networkstatus_st.h"
36 #include "feature/nodelist/routerinfo_st.h"
38 /** Maximum size, in bytes, for any directory object that we're accepting
39 * as an upload. */
40 #define MAX_DIR_UL_SIZE ((1<<24)-1) /* 16MB-1 */
42 /** HTTP cache control: how long do we tell proxies they can cache each
43 * kind of document we serve? */
44 #define FULL_DIR_CACHE_LIFETIME (60*60)
45 #define RUNNINGROUTERS_CACHE_LIFETIME (20*60)
46 #define DIRPORTFRONTPAGE_CACHE_LIFETIME (20*60)
47 #define NETWORKSTATUS_CACHE_LIFETIME (5*60)
48 #define ROUTERDESC_CACHE_LIFETIME (30*60)
49 #define ROUTERDESC_BY_DIGEST_CACHE_LIFETIME (48*60*60)
50 #define ROBOTS_CACHE_LIFETIME (24*60*60)
51 #define MICRODESC_CACHE_LIFETIME (48*60*60)
52 /* Bandwidth files change every hour. */
53 #define BANDWIDTH_CACHE_LIFETIME (30*60)
54 /** Parse an HTTP request string <b>headers</b> of the form
55 * \verbatim
56 * "\%s [http[s]://]\%s HTTP/1..."
57 * \endverbatim
58 * If it's well-formed, strdup the second \%s into *<b>url</b>, and
59 * nul-terminate it. If the url doesn't start with "/tor/", rewrite it
60 * so it does. Return 0.
61 * Otherwise, return -1.
63 STATIC int
64 parse_http_url(const char *headers, char **url)
66 char *command = NULL;
67 if (parse_http_command(headers, &command, url) < 0) {
68 return -1;
70 if (strcmpstart(*url, "/tor/")) {
71 char *new_url = NULL;
72 tor_asprintf(&new_url, "/tor%s%s",
73 *url[0] == '/' ? "" : "/",
74 *url);
75 tor_free(*url);
76 *url = new_url;
78 tor_free(command);
79 return 0;
82 /** Create an http response for the client <b>conn</b> out of
83 * <b>status</b> and <b>reason_phrase</b>. Write it to <b>conn</b>.
85 static void
86 write_short_http_response(dir_connection_t *conn, int status,
87 const char *reason_phrase)
89 char *buf = NULL;
90 char *datestring = NULL;
92 IF_BUG_ONCE(!reason_phrase) { /* bullet-proofing */
93 reason_phrase = "unspecified";
96 if (server_mode(get_options())) {
97 /* include the Date: header, but only if we're a relay or bridge */
98 char datebuf[RFC1123_TIME_LEN+1];
99 format_rfc1123_time(datebuf, time(NULL));
100 tor_asprintf(&datestring, "Date: %s\r\n", datebuf);
103 tor_asprintf(&buf, "HTTP/1.0 %d %s\r\n%s\r\n",
104 status, reason_phrase, datestring?datestring:"");
106 log_debug(LD_DIRSERV,"Wrote status 'HTTP/1.0 %d %s'", status, reason_phrase);
107 connection_buf_add(buf, strlen(buf), TO_CONN(conn));
109 tor_free(datestring);
110 tor_free(buf);
113 /** Write the header for an HTTP/1.0 response onto <b>conn</b>-\>outbuf,
114 * with <b>type</b> as the Content-Type.
116 * If <b>length</b> is nonnegative, it is the Content-Length.
117 * If <b>encoding</b> is provided, it is the Content-Encoding.
118 * If <b>cache_lifetime</b> is greater than 0, the content may be cached for
119 * up to cache_lifetime seconds. Otherwise, the content may not be cached. */
120 static void
121 write_http_response_header_impl(dir_connection_t *conn, ssize_t length,
122 const char *type, const char *encoding,
123 const char *extra_headers,
124 long cache_lifetime)
126 char date[RFC1123_TIME_LEN+1];
127 time_t now = approx_time();
128 buf_t *buf = buf_new_with_capacity(1024);
130 tor_assert(conn);
132 format_rfc1123_time(date, now);
134 buf_add_printf(buf, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date);
135 if (type) {
136 buf_add_printf(buf, "Content-Type: %s\r\n", type);
138 if (!is_local_addr(&conn->base_.addr)) {
139 /* Don't report the source address for a nearby/private connection.
140 * Otherwise we tend to mis-report in cases where incoming ports are
141 * being forwarded to a Tor server running behind the firewall. */
142 buf_add_printf(buf, X_ADDRESS_HEADER "%s\r\n", conn->base_.address);
144 if (encoding) {
145 buf_add_printf(buf, "Content-Encoding: %s\r\n", encoding);
147 if (length >= 0) {
148 buf_add_printf(buf, "Content-Length: %ld\r\n", (long)length);
150 if (cache_lifetime > 0) {
151 char expbuf[RFC1123_TIME_LEN+1];
152 format_rfc1123_time(expbuf, (time_t)(now + cache_lifetime));
153 /* We could say 'Cache-control: max-age=%d' here if we start doing
154 * http/1.1 */
155 buf_add_printf(buf, "Expires: %s\r\n", expbuf);
156 } else if (cache_lifetime == 0) {
157 /* We could say 'Cache-control: no-cache' here if we start doing
158 * http/1.1 */
159 buf_add_string(buf, "Pragma: no-cache\r\n");
161 if (extra_headers) {
162 buf_add_string(buf, extra_headers);
164 buf_add_string(buf, "\r\n");
166 connection_buf_add_buf(TO_CONN(conn), buf);
167 buf_free(buf);
170 /** As write_http_response_header_impl, but translates method into
171 * encoding */
172 static void
173 write_http_response_headers(dir_connection_t *conn, ssize_t length,
174 compress_method_t method,
175 const char *extra_headers, long cache_lifetime)
177 write_http_response_header_impl(conn, length,
178 "text/plain",
179 compression_method_get_name(method),
180 extra_headers,
181 cache_lifetime);
184 /** As write_http_response_headers, but assumes extra_headers is NULL */
185 static void
186 write_http_response_header(dir_connection_t *conn, ssize_t length,
187 compress_method_t method,
188 long cache_lifetime)
190 write_http_response_headers(conn, length, method, NULL, cache_lifetime);
193 /** Array of compression methods to use (if supported) for serving
194 * precompressed data, ordered from best to worst. */
195 static compress_method_t srv_meth_pref_precompressed[] = {
196 LZMA_METHOD,
197 ZSTD_METHOD,
198 ZLIB_METHOD,
199 GZIP_METHOD,
200 NO_METHOD
203 /** Array of compression methods to use (if supported) for serving
204 * streamed data, ordered from best to worst. */
205 static compress_method_t srv_meth_pref_streaming_compression[] = {
206 ZSTD_METHOD,
207 ZLIB_METHOD,
208 GZIP_METHOD,
209 NO_METHOD
212 /** Parse the compression methods listed in an Accept-Encoding header <b>h</b>,
213 * and convert them to a bitfield where compression method x is supported if
214 * and only if 1 &lt;&lt; x is set in the bitfield. */
215 STATIC unsigned
216 parse_accept_encoding_header(const char *h)
218 unsigned result = (1u << NO_METHOD);
219 smartlist_t *methods = smartlist_new();
220 smartlist_split_string(methods, h, ",",
221 SPLIT_SKIP_SPACE|SPLIT_STRIP_SPACE|SPLIT_IGNORE_BLANK, 0);
223 SMARTLIST_FOREACH_BEGIN(methods, const char *, m) {
224 compress_method_t method = compression_method_get_by_name(m);
225 if (method != UNKNOWN_METHOD) {
226 tor_assert(((unsigned)method) < 8*sizeof(unsigned));
227 result |= (1u << method);
229 } SMARTLIST_FOREACH_END(m);
230 SMARTLIST_FOREACH_BEGIN(methods, char *, m) {
231 tor_free(m);
232 } SMARTLIST_FOREACH_END(m);
233 smartlist_free(methods);
234 return result;
237 /** Decide whether a client would accept the consensus we have.
239 * Clients can say they only want a consensus if it's signed by more
240 * than half the authorities in a list. They pass this list in
241 * the url as "...consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b>".
243 * <b>fpr</b> may be an abbreviated fingerprint, i.e. only a left substring
244 * of the full authority identity digest. (Only strings of even length,
245 * i.e. encodings of full bytes, are handled correctly. In the case
246 * of an odd number of hex digits the last one is silently ignored.)
248 * Returns 1 if more than half of the requested authorities signed the
249 * consensus, 0 otherwise.
251 static int
252 client_likes_consensus(const struct consensus_cache_entry_t *ent,
253 const char *want_url)
255 smartlist_t *voters = smartlist_new();
256 int need_at_least;
257 int have = 0;
259 if (consensus_cache_entry_get_voter_id_digests(ent, voters) != 0) {
260 smartlist_free(voters);
261 return 1; // We don't know the voters; assume the client won't mind. */
264 smartlist_t *want_authorities = smartlist_new();
265 dir_split_resource_into_fingerprints(want_url, want_authorities, NULL, 0);
266 need_at_least = smartlist_len(want_authorities)/2+1;
268 SMARTLIST_FOREACH_BEGIN(want_authorities, const char *, want_digest) {
270 SMARTLIST_FOREACH_BEGIN(voters, const char *, digest) {
271 if (!strcasecmpstart(digest, want_digest)) {
272 have++;
273 break;
275 } SMARTLIST_FOREACH_END(digest);
277 /* early exit, if we already have enough */
278 if (have >= need_at_least)
279 break;
280 } SMARTLIST_FOREACH_END(want_digest);
282 SMARTLIST_FOREACH(want_authorities, char *, d, tor_free(d));
283 smartlist_free(want_authorities);
284 SMARTLIST_FOREACH(voters, char *, cp, tor_free(cp));
285 smartlist_free(voters);
286 return (have >= need_at_least);
289 /** Return the compression level we should use for sending a compressed
290 * response of size <b>n_bytes</b>. */
291 STATIC compression_level_t
292 choose_compression_level(ssize_t n_bytes)
294 if (! have_been_under_memory_pressure()) {
295 return HIGH_COMPRESSION; /* we have plenty of RAM. */
296 } else if (n_bytes < 0) {
297 return HIGH_COMPRESSION; /* unknown; might be big. */
298 } else if (n_bytes < 1024) {
299 return LOW_COMPRESSION;
300 } else if (n_bytes < 2048) {
301 return MEDIUM_COMPRESSION;
302 } else {
303 return HIGH_COMPRESSION;
307 /** Information passed to handle a GET request. */
308 typedef struct get_handler_args_t {
309 /** Bitmask of compression methods that the client said (or implied) it
310 * supported. */
311 unsigned compression_supported;
312 /** If nonzero, the time included an if-modified-since header with this
313 * value. */
314 time_t if_modified_since;
315 /** String containing the requested URL or resource. */
316 const char *url;
317 /** String containing the HTTP headers */
318 const char *headers;
319 } get_handler_args_t;
321 /** Entry for handling an HTTP GET request.
323 * This entry matches a request if "string" is equal to the requested
324 * resource, or if "is_prefix" is true and "string" is a prefix of the
325 * requested resource.
327 * The 'handler' function is called to handle the request. It receives
328 * an arguments structure, and must return 0 on success or -1 if we should
329 * close the connection.
331 typedef struct url_table_ent_s {
332 const char *string;
333 int is_prefix;
334 int (*handler)(dir_connection_t *conn, const get_handler_args_t *args);
335 } url_table_ent_t;
337 static int handle_get_frontpage(dir_connection_t *conn,
338 const get_handler_args_t *args);
339 static int handle_get_current_consensus(dir_connection_t *conn,
340 const get_handler_args_t *args);
341 static int handle_get_status_vote(dir_connection_t *conn,
342 const get_handler_args_t *args);
343 static int handle_get_microdesc(dir_connection_t *conn,
344 const get_handler_args_t *args);
345 static int handle_get_descriptor(dir_connection_t *conn,
346 const get_handler_args_t *args);
347 static int handle_get_keys(dir_connection_t *conn,
348 const get_handler_args_t *args);
349 static int handle_get_hs_descriptor_v2(dir_connection_t *conn,
350 const get_handler_args_t *args);
351 static int handle_get_robots(dir_connection_t *conn,
352 const get_handler_args_t *args);
353 static int handle_get_networkstatus_bridges(dir_connection_t *conn,
354 const get_handler_args_t *args);
355 static int handle_get_next_bandwidth(dir_connection_t *conn,
356 const get_handler_args_t *args);
358 /** Table for handling GET requests. */
359 static const url_table_ent_t url_table[] = {
360 { "/tor/", 0, handle_get_frontpage },
361 { "/tor/status-vote/current/consensus", 1, handle_get_current_consensus },
362 { "/tor/status-vote/current/", 1, handle_get_status_vote },
363 { "/tor/status-vote/next/bandwidth", 0, handle_get_next_bandwidth },
364 { "/tor/status-vote/next/", 1, handle_get_status_vote },
365 { "/tor/micro/d/", 1, handle_get_microdesc },
366 { "/tor/server/", 1, handle_get_descriptor },
367 { "/tor/extra/", 1, handle_get_descriptor },
368 { "/tor/keys/", 1, handle_get_keys },
369 { "/tor/rendezvous2/", 1, handle_get_hs_descriptor_v2 },
370 { "/tor/hs/3/", 1, handle_get_hs_descriptor_v3 },
371 { "/tor/robots.txt", 0, handle_get_robots },
372 { "/tor/networkstatus-bridges", 0, handle_get_networkstatus_bridges },
373 { NULL, 0, NULL },
376 /** Helper function: called when a dirserver gets a complete HTTP GET
377 * request. Look for a request for a directory or for a rendezvous
378 * service descriptor. On finding one, write a response into
379 * conn-\>outbuf. If the request is unrecognized, send a 404.
380 * Return 0 if we handled this successfully, or -1 if we need to close
381 * the connection. */
382 MOCK_IMPL(STATIC int,
383 directory_handle_command_get,(dir_connection_t *conn, const char *headers,
384 const char *req_body, size_t req_body_len))
386 char *url, *url_mem, *header;
387 time_t if_modified_since = 0;
388 int zlib_compressed_in_url;
389 unsigned compression_methods_supported;
391 /* We ignore the body of a GET request. */
392 (void)req_body;
393 (void)req_body_len;
395 log_debug(LD_DIRSERV,"Received GET command.");
397 conn->base_.state = DIR_CONN_STATE_SERVER_WRITING;
399 if (parse_http_url(headers, &url) < 0) {
400 write_short_http_response(conn, 400, "Bad request");
401 return 0;
403 if ((header = http_get_header(headers, "If-Modified-Since: "))) {
404 struct tm tm;
405 if (parse_http_time(header, &tm) == 0) {
406 if (tor_timegm(&tm, &if_modified_since)<0) {
407 if_modified_since = 0;
408 } else {
409 log_debug(LD_DIRSERV, "If-Modified-Since is '%s'.", escaped(header));
412 /* The correct behavior on a malformed If-Modified-Since header is to
413 * act as if no If-Modified-Since header had been given. */
414 tor_free(header);
416 log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url));
418 url_mem = url;
420 size_t url_len = strlen(url);
422 zlib_compressed_in_url = url_len > 2 && !strcmp(url+url_len-2, ".z");
423 if (zlib_compressed_in_url) {
424 url[url_len-2] = '\0';
428 if ((header = http_get_header(headers, "Accept-Encoding: "))) {
429 compression_methods_supported = parse_accept_encoding_header(header);
430 tor_free(header);
431 } else {
432 compression_methods_supported = (1u << NO_METHOD);
434 if (zlib_compressed_in_url) {
435 compression_methods_supported |= (1u << ZLIB_METHOD);
438 /* Remove all methods that we don't both support. */
439 compression_methods_supported &= tor_compress_get_supported_method_bitmask();
441 get_handler_args_t args;
442 args.url = url;
443 args.headers = headers;
444 args.if_modified_since = if_modified_since;
445 args.compression_supported = compression_methods_supported;
447 int i, result = -1;
448 for (i = 0; url_table[i].string; ++i) {
449 int match;
450 if (url_table[i].is_prefix) {
451 match = !strcmpstart(url, url_table[i].string);
452 } else {
453 match = !strcmp(url, url_table[i].string);
455 if (match) {
456 result = url_table[i].handler(conn, &args);
457 goto done;
461 /* we didn't recognize the url */
462 write_short_http_response(conn, 404, "Not found");
463 result = 0;
465 done:
466 tor_free(url_mem);
467 return result;
470 /** Helper function for GET / or GET /tor/
472 static int
473 handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args)
475 (void) args; /* unused */
476 const char *frontpage = get_dirportfrontpage();
478 if (frontpage) {
479 size_t dlen;
480 dlen = strlen(frontpage);
481 /* Let's return a disclaimer page (users shouldn't use V1 anymore,
482 and caches don't fetch '/', so this is safe). */
484 /* [We don't check for write_bucket_low here, since we want to serve
485 * this page no matter what.] */
486 write_http_response_header_impl(conn, dlen, "text/html", "identity",
487 NULL, DIRPORTFRONTPAGE_CACHE_LIFETIME);
488 connection_buf_add(frontpage, dlen, TO_CONN(conn));
489 } else {
490 write_short_http_response(conn, 404, "Not found");
492 return 0;
495 /** Warn that the cached consensus <b>consensus</b> of type
496 * <b>flavor</b> too new or too old, based on <b>is_too_new</b>,
497 * and will not be served to clients. Rate-limit the warning to avoid logging
498 * an entry on every request.
500 static void
501 warn_consensus_is_not_reasonably_live(
502 const struct consensus_cache_entry_t *consensus,
503 const char *flavor, time_t now, bool is_too_new)
505 #define NOT_REASONABLY_LIVE_WARNING_INTERVAL (60*60)
506 static ratelim_t warned[2] = { RATELIM_INIT(
507 NOT_REASONABLY_LIVE_WARNING_INTERVAL),
508 RATELIM_INIT(
509 NOT_REASONABLY_LIVE_WARNING_INTERVAL) };
510 char timestamp[ISO_TIME_LEN+1];
511 /* valid_after if is_too_new, valid_until if !is_too_new */
512 time_t valid_time = 0;
513 char *dupes = NULL;
515 if (is_too_new) {
516 if (consensus_cache_entry_get_valid_after(consensus, &valid_time))
517 return;
518 dupes = rate_limit_log(&warned[1], now);
519 } else {
520 if (consensus_cache_entry_get_valid_until(consensus, &valid_time))
521 return;
522 dupes = rate_limit_log(&warned[0], now);
525 if (dupes) {
526 format_local_iso_time(timestamp, valid_time);
527 log_warn(LD_DIRSERV, "Our %s%sconsensus is too %s, so we will not "
528 "serve it to clients. It was valid %s %s local time and we "
529 "continued to serve it for up to 24 hours %s.%s",
530 flavor ? flavor : "",
531 flavor ? " " : "",
532 is_too_new ? "new" : "old",
533 is_too_new ? "after" : "until",
534 timestamp,
535 is_too_new ? "before it was valid" : "after it expired",
536 dupes);
537 tor_free(dupes);
542 * Parse a single hex-encoded sha3-256 digest from <b>hex</b> into
543 * <b>digest</b>. Return 0 on success. On failure, report that the hash came
544 * from <b>location</b>, report that we are taking <b>action</b> with it, and
545 * return -1.
547 static int
548 parse_one_diff_hash(uint8_t *digest, const char *hex, const char *location,
549 const char *action)
551 if (base16_decode((char*)digest, DIGEST256_LEN, hex, strlen(hex)) ==
552 DIGEST256_LEN) {
553 return 0;
554 } else {
555 log_fn(LOG_PROTOCOL_WARN, LD_DIR,
556 "%s contained bogus digest %s; %s.",
557 location, escaped(hex), action);
558 return -1;
562 /** If there is an X-Or-Diff-From-Consensus header included in <b>headers</b>,
563 * set <b>digest_out<b> to a new smartlist containing every 256-bit
564 * hex-encoded digest listed in that header and return 0. Otherwise return
565 * -1. */
566 static int
567 parse_or_diff_from_header(smartlist_t **digests_out, const char *headers)
569 char *hdr = http_get_header(headers, X_OR_DIFF_FROM_CONSENSUS_HEADER);
570 if (hdr == NULL) {
571 return -1;
573 smartlist_t *hex_digests = smartlist_new();
574 *digests_out = smartlist_new();
575 smartlist_split_string(hex_digests, hdr, " ",
576 SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
577 SMARTLIST_FOREACH_BEGIN(hex_digests, const char *, hex) {
578 uint8_t digest[DIGEST256_LEN];
579 if (!parse_one_diff_hash(digest, hex, "X-Or-Diff-From-Consensus header",
580 "ignoring")) {
581 smartlist_add(*digests_out, tor_memdup(digest, sizeof(digest)));
583 } SMARTLIST_FOREACH_END(hex);
584 SMARTLIST_FOREACH(hex_digests, char *, cp, tor_free(cp));
585 smartlist_free(hex_digests);
586 tor_free(hdr);
587 return 0;
590 /** Fallback compression method. The fallback compression method is used in
591 * case a client requests a non-compressed document. We only store compressed
592 * documents, so we use this compression method to fetch the document and let
593 * the spooling system do the streaming decompression.
595 #define FALLBACK_COMPRESS_METHOD ZLIB_METHOD
598 * Try to find the best consensus diff possible in order to serve a client
599 * request for a diff from one of the consensuses in <b>digests</b> to the
600 * current consensus of flavor <b>flav</b>. The client supports the
601 * compression methods listed in the <b>compression_methods</b> bitfield:
602 * place the method chosen (if any) into <b>compression_used_out</b>.
604 static struct consensus_cache_entry_t *
605 find_best_diff(const smartlist_t *digests, int flav,
606 unsigned compression_methods,
607 compress_method_t *compression_used_out)
609 struct consensus_cache_entry_t *result = NULL;
611 SMARTLIST_FOREACH_BEGIN(digests, const uint8_t *, diff_from) {
612 unsigned u;
613 for (u = 0; u < ARRAY_LENGTH(srv_meth_pref_precompressed); ++u) {
614 compress_method_t method = srv_meth_pref_precompressed[u];
615 if (0 == (compression_methods & (1u<<method)))
616 continue; // client doesn't like this one, or we don't have it.
617 if (consdiffmgr_find_diff_from(&result, flav, DIGEST_SHA3_256,
618 diff_from, DIGEST256_LEN,
619 method) == CONSDIFF_AVAILABLE) {
620 tor_assert_nonfatal(result);
621 *compression_used_out = method;
622 return result;
625 } SMARTLIST_FOREACH_END(diff_from);
627 SMARTLIST_FOREACH_BEGIN(digests, const uint8_t *, diff_from) {
628 if (consdiffmgr_find_diff_from(&result, flav, DIGEST_SHA3_256, diff_from,
629 DIGEST256_LEN, FALLBACK_COMPRESS_METHOD) == CONSDIFF_AVAILABLE) {
630 tor_assert_nonfatal(result);
631 *compression_used_out = FALLBACK_COMPRESS_METHOD;
632 return result;
634 } SMARTLIST_FOREACH_END(diff_from);
636 return NULL;
639 /** Lookup the cached consensus document by the flavor found in <b>flav</b>.
640 * The preferred set of compression methods should be listed in the
641 * <b>compression_methods</b> bitfield. The compression method chosen (if any)
642 * is stored in <b>compression_used_out</b>. */
643 static struct consensus_cache_entry_t *
644 find_best_consensus(int flav,
645 unsigned compression_methods,
646 compress_method_t *compression_used_out)
648 struct consensus_cache_entry_t *result = NULL;
649 unsigned u;
651 for (u = 0; u < ARRAY_LENGTH(srv_meth_pref_precompressed); ++u) {
652 compress_method_t method = srv_meth_pref_precompressed[u];
654 if (0 == (compression_methods & (1u<<method)))
655 continue;
657 if (consdiffmgr_find_consensus(&result, flav,
658 method) == CONSDIFF_AVAILABLE) {
659 tor_assert_nonfatal(result);
660 *compression_used_out = method;
661 return result;
665 if (consdiffmgr_find_consensus(&result, flav,
666 FALLBACK_COMPRESS_METHOD) == CONSDIFF_AVAILABLE) {
667 tor_assert_nonfatal(result);
668 *compression_used_out = FALLBACK_COMPRESS_METHOD;
669 return result;
672 return NULL;
675 /** Try to find the best supported compression method possible from a given
676 * <b>compression_methods</b>. Return NO_METHOD if no mutually supported
677 * compression method could be found. */
678 static compress_method_t
679 find_best_compression_method(unsigned compression_methods, int stream)
681 unsigned u;
682 compress_method_t *methods;
683 size_t length;
685 if (stream) {
686 methods = srv_meth_pref_streaming_compression;
687 length = ARRAY_LENGTH(srv_meth_pref_streaming_compression);
688 } else {
689 methods = srv_meth_pref_precompressed;
690 length = ARRAY_LENGTH(srv_meth_pref_precompressed);
693 for (u = 0; u < length; ++u) {
694 compress_method_t method = methods[u];
695 if (compression_methods & (1u<<method))
696 return method;
699 return NO_METHOD;
702 /** Check if any of the digests in <b>digests</b> matches the latest consensus
703 * flavor (given in <b>flavor</b>) that we have available. */
704 static int
705 digest_list_contains_best_consensus(consensus_flavor_t flavor,
706 const smartlist_t *digests)
708 const networkstatus_t *ns = NULL;
710 if (digests == NULL)
711 return 0;
713 ns = networkstatus_get_latest_consensus_by_flavor(flavor);
715 if (ns == NULL)
716 return 0;
718 SMARTLIST_FOREACH_BEGIN(digests, const uint8_t *, digest) {
719 if (tor_memeq(ns->digest_sha3_as_signed, digest, DIGEST256_LEN))
720 return 1;
721 } SMARTLIST_FOREACH_END(digest);
723 return 0;
726 /** Encodes the results of parsing a consensus request to figure out what
727 * consensus, and possibly what diffs, the user asked for. */
728 typedef struct {
729 /** name of the flavor to retrieve. */
730 char *flavor;
731 /** flavor to retrive, as enum. */
732 consensus_flavor_t flav;
733 /** plus-separated list of authority fingerprints; see
734 * client_likes_consensus(). Aliases the URL in the request passed to
735 * parse_consensus_request(). */
736 const char *want_fps;
737 /** Optionally, a smartlist of sha3 digests-as-signed of the consensuses
738 * to return a diff from. */
739 smartlist_t *diff_from_digests;
740 /** If true, never send a full consensus. If there is no diff, send
741 * a 404 instead. */
742 int diff_only;
743 } parsed_consensus_request_t;
745 /** Remove all data held in <b>req</b>. Do not free <b>req</b> itself, since
746 * it is stack-allocated. */
747 static void
748 parsed_consensus_request_clear(parsed_consensus_request_t *req)
750 if (!req)
751 return;
752 tor_free(req->flavor);
753 if (req->diff_from_digests) {
754 SMARTLIST_FOREACH(req->diff_from_digests, uint8_t *, d, tor_free(d));
755 smartlist_free(req->diff_from_digests);
757 memset(req, 0, sizeof(parsed_consensus_request_t));
761 * Parse the URL and relevant headers of <b>args</b> for a current-consensus
762 * request to learn what flavor of consensus we want, what keys it must be
763 * signed with, and what diffs we would accept (or demand) instead. Return 0
764 * on success and -1 on failure.
766 static int
767 parse_consensus_request(parsed_consensus_request_t *out,
768 const get_handler_args_t *args)
770 const char *url = args->url;
771 memset(out, 0, sizeof(parsed_consensus_request_t));
772 out->flav = FLAV_NS;
774 const char CONSENSUS_URL_PREFIX[] = "/tor/status-vote/current/consensus/";
775 const char CONSENSUS_FLAVORED_PREFIX[] =
776 "/tor/status-vote/current/consensus-";
778 /* figure out the flavor if any, and who we wanted to sign the thing */
779 const char *after_flavor = NULL;
781 if (!strcmpstart(url, CONSENSUS_FLAVORED_PREFIX)) {
782 const char *f, *cp;
783 f = url + strlen(CONSENSUS_FLAVORED_PREFIX);
784 cp = strchr(f, '/');
785 if (cp) {
786 after_flavor = cp+1;
787 out->flavor = tor_strndup(f, cp-f);
788 } else {
789 out->flavor = tor_strdup(f);
791 int flav = networkstatus_parse_flavor_name(out->flavor);
792 if (flav < 0)
793 flav = FLAV_NS;
794 out->flav = flav;
795 } else {
796 if (!strcmpstart(url, CONSENSUS_URL_PREFIX))
797 after_flavor = url+strlen(CONSENSUS_URL_PREFIX);
800 /* see whether we've been asked explicitly for a diff from an older
801 * consensus. (The user might also have said that a diff would be okay,
802 * via X-Or-Diff-From-Consensus */
803 const char DIFF_COMPONENT[] = "diff/";
804 char *diff_hash_in_url = NULL;
805 if (after_flavor && !strcmpstart(after_flavor, DIFF_COMPONENT)) {
806 after_flavor += strlen(DIFF_COMPONENT);
807 const char *cp = strchr(after_flavor, '/');
808 if (cp) {
809 diff_hash_in_url = tor_strndup(after_flavor, cp-after_flavor);
810 out->want_fps = cp+1;
811 } else {
812 diff_hash_in_url = tor_strdup(after_flavor);
813 out->want_fps = NULL;
815 } else {
816 out->want_fps = after_flavor;
819 if (diff_hash_in_url) {
820 uint8_t diff_from[DIGEST256_LEN];
821 out->diff_from_digests = smartlist_new();
822 out->diff_only = 1;
823 int ok = !parse_one_diff_hash(diff_from, diff_hash_in_url, "URL",
824 "rejecting");
825 tor_free(diff_hash_in_url);
826 if (ok) {
827 smartlist_add(out->diff_from_digests,
828 tor_memdup(diff_from, DIGEST256_LEN));
829 } else {
830 return -1;
832 } else {
833 parse_or_diff_from_header(&out->diff_from_digests, args->headers);
836 return 0;
839 /** Helper function for GET /tor/status-vote/current/consensus
841 static int
842 handle_get_current_consensus(dir_connection_t *conn,
843 const get_handler_args_t *args)
845 const compress_method_t compress_method =
846 find_best_compression_method(args->compression_supported, 0);
847 const time_t if_modified_since = args->if_modified_since;
848 int clear_spool = 0;
850 /* v3 network status fetch. */
851 long lifetime = NETWORKSTATUS_CACHE_LIFETIME;
853 time_t now = time(NULL);
854 parsed_consensus_request_t req;
856 if (parse_consensus_request(&req, args) < 0) {
857 write_short_http_response(conn, 404, "Couldn't parse request");
858 goto done;
861 if (digest_list_contains_best_consensus(req.flav,
862 req.diff_from_digests)) {
863 write_short_http_response(conn, 304, "Not modified");
864 geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED);
865 goto done;
868 struct consensus_cache_entry_t *cached_consensus = NULL;
870 compress_method_t compression_used = NO_METHOD;
871 if (req.diff_from_digests) {
872 cached_consensus = find_best_diff(req.diff_from_digests, req.flav,
873 args->compression_supported,
874 &compression_used);
877 if (req.diff_only && !cached_consensus) {
878 write_short_http_response(conn, 404, "No such diff available");
879 geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
880 goto done;
883 if (! cached_consensus) {
884 cached_consensus = find_best_consensus(req.flav,
885 args->compression_supported,
886 &compression_used);
889 time_t valid_after, fresh_until, valid_until;
890 int have_valid_after = 0, have_fresh_until = 0, have_valid_until = 0;
891 if (cached_consensus) {
892 have_valid_after =
893 !consensus_cache_entry_get_valid_after(cached_consensus, &valid_after);
894 have_fresh_until =
895 !consensus_cache_entry_get_fresh_until(cached_consensus, &fresh_until);
896 have_valid_until =
897 !consensus_cache_entry_get_valid_until(cached_consensus, &valid_until);
900 if (cached_consensus && have_valid_after &&
901 !networkstatus_valid_after_is_reasonably_live(valid_after, now)) {
902 write_short_http_response(conn, 404, "Consensus is too new");
903 warn_consensus_is_not_reasonably_live(cached_consensus, req.flavor, now,
905 geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
906 goto done;
907 } else if (
908 cached_consensus && have_valid_until &&
909 !networkstatus_valid_until_is_reasonably_live(valid_until, now)) {
910 write_short_http_response(conn, 404, "Consensus is too old");
911 warn_consensus_is_not_reasonably_live(cached_consensus, req.flavor, now,
913 geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
914 goto done;
917 if (cached_consensus && req.want_fps &&
918 !client_likes_consensus(cached_consensus, req.want_fps)) {
919 write_short_http_response(conn, 404, "Consensus not signed by sufficient "
920 "number of requested authorities");
921 geoip_note_ns_response(GEOIP_REJECT_NOT_ENOUGH_SIGS);
922 goto done;
925 conn->spool = smartlist_new();
926 clear_spool = 1;
928 spooled_resource_t *spooled;
929 if (cached_consensus) {
930 spooled = spooled_resource_new_from_cache_entry(cached_consensus);
931 smartlist_add(conn->spool, spooled);
935 lifetime = (have_fresh_until && fresh_until > now) ? fresh_until - now : 0;
937 size_t size_guess = 0;
938 int n_expired = 0;
939 dirserv_spool_remove_missing_and_guess_size(conn, if_modified_since,
940 compress_method != NO_METHOD,
941 &size_guess,
942 &n_expired);
944 if (!smartlist_len(conn->spool) && !n_expired) {
945 write_short_http_response(conn, 404, "Not found");
946 geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
947 goto done;
948 } else if (!smartlist_len(conn->spool)) {
949 write_short_http_response(conn, 304, "Not modified");
950 geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED);
951 goto done;
954 if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
955 log_debug(LD_DIRSERV,
956 "Client asked for network status lists, but we've been "
957 "writing too many bytes lately. Sending 503 Dir busy.");
958 write_short_http_response(conn, 503, "Directory busy, try again later");
959 geoip_note_ns_response(GEOIP_REJECT_BUSY);
960 goto done;
963 tor_addr_t addr;
964 if (tor_addr_parse(&addr, (TO_CONN(conn))->address) >= 0) {
965 geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS,
966 &addr, NULL,
967 time(NULL));
968 geoip_note_ns_response(GEOIP_SUCCESS);
969 /* Note that a request for a network status has started, so that we
970 * can measure the download time later on. */
971 if (conn->dirreq_id)
972 geoip_start_dirreq(conn->dirreq_id, size_guess, DIRREQ_TUNNELED);
973 else
974 geoip_start_dirreq(TO_CONN(conn)->global_identifier, size_guess,
975 DIRREQ_DIRECT);
978 /* Use this header to tell caches that the response depends on the
979 * X-Or-Diff-From-Consensus header (or lack thereof). */
980 const char vary_header[] = "Vary: X-Or-Diff-From-Consensus\r\n";
982 clear_spool = 0;
984 // The compress_method might have been NO_METHOD, but we store the data
985 // compressed. Decompress them using `compression_used`. See fallback code in
986 // find_best_consensus() and find_best_diff().
987 write_http_response_headers(conn, -1,
988 compress_method == NO_METHOD ?
989 NO_METHOD : compression_used,
990 vary_header,
991 smartlist_len(conn->spool) == 1 ? lifetime : 0);
993 if (compress_method == NO_METHOD && smartlist_len(conn->spool))
994 conn->compress_state = tor_compress_new(0, compression_used,
995 HIGH_COMPRESSION);
997 /* Prime the connection with some data. */
998 const int initial_flush_result = connection_dirserv_flushed_some(conn);
999 tor_assert_nonfatal(initial_flush_result == 0);
1000 goto done;
1002 done:
1003 parsed_consensus_request_clear(&req);
1004 if (clear_spool) {
1005 dir_conn_clear_spool(conn);
1007 return 0;
1010 /** Helper function for GET /tor/status-vote/{current,next}/...
1012 static int
1013 handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
1015 const char *url = args->url;
1017 ssize_t body_len = 0;
1018 ssize_t estimated_len = 0;
1019 int lifetime = 60; /* XXXX?? should actually use vote intervals. */
1020 /* This smartlist holds strings that we can compress on the fly. */
1021 smartlist_t *items = smartlist_new();
1022 /* This smartlist holds cached_dir_t objects that have a precompressed
1023 * deflated version. */
1024 smartlist_t *dir_items = smartlist_new();
1025 dirvote_dirreq_get_status_vote(url, items, dir_items);
1026 if (!smartlist_len(dir_items) && !smartlist_len(items)) {
1027 write_short_http_response(conn, 404, "Not found");
1028 goto vote_done;
1031 /* We're sending items from at most one kind of source */
1032 tor_assert_nonfatal(smartlist_len(items) == 0 ||
1033 smartlist_len(dir_items) == 0);
1035 int streaming;
1036 unsigned mask;
1037 if (smartlist_len(items)) {
1038 /* We're taking strings and compressing them on the fly. */
1039 streaming = 1;
1040 mask = ~0u;
1041 } else {
1042 /* We're taking cached_dir_t objects. We only have them uncompressed
1043 * or deflated. */
1044 streaming = 0;
1045 mask = (1u<<NO_METHOD) | (1u<<ZLIB_METHOD);
1047 const compress_method_t compress_method = find_best_compression_method(
1048 args->compression_supported&mask, streaming);
1050 SMARTLIST_FOREACH(dir_items, cached_dir_t *, d,
1051 body_len += compress_method != NO_METHOD ?
1052 d->dir_compressed_len : d->dir_len);
1053 estimated_len += body_len;
1054 SMARTLIST_FOREACH(items, const char *, item, {
1055 size_t ln = strlen(item);
1056 if (compress_method != NO_METHOD) {
1057 estimated_len += ln/2;
1058 } else {
1059 body_len += ln; estimated_len += ln;
1063 if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
1064 write_short_http_response(conn, 503, "Directory busy, try again later");
1065 goto vote_done;
1067 write_http_response_header(conn, body_len ? body_len : -1,
1068 compress_method,
1069 lifetime);
1071 if (smartlist_len(items)) {
1072 if (compress_method != NO_METHOD) {
1073 conn->compress_state = tor_compress_new(1, compress_method,
1074 choose_compression_level(estimated_len));
1075 SMARTLIST_FOREACH(items, const char *, c,
1076 connection_buf_add_compress(c, strlen(c), conn, 0));
1077 connection_buf_add_compress("", 0, conn, 1);
1078 } else {
1079 SMARTLIST_FOREACH(items, const char *, c,
1080 connection_buf_add(c, strlen(c), TO_CONN(conn)));
1082 } else {
1083 SMARTLIST_FOREACH(dir_items, cached_dir_t *, d,
1084 connection_buf_add(compress_method != NO_METHOD ?
1085 d->dir_compressed : d->dir,
1086 compress_method != NO_METHOD ?
1087 d->dir_compressed_len : d->dir_len,
1088 TO_CONN(conn)));
1090 vote_done:
1091 smartlist_free(items);
1092 smartlist_free(dir_items);
1093 goto done;
1095 done:
1096 return 0;
1099 /** Helper function for GET /tor/micro/d/...
1101 static int
1102 handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args)
1104 const char *url = args->url;
1105 const compress_method_t compress_method =
1106 find_best_compression_method(args->compression_supported, 1);
1107 int clear_spool = 1;
1109 conn->spool = smartlist_new();
1111 dir_split_resource_into_spoolable(url+strlen("/tor/micro/d/"),
1112 DIR_SPOOL_MICRODESC,
1113 conn->spool, NULL,
1114 DSR_DIGEST256|DSR_BASE64|DSR_SORT_UNIQ);
1116 size_t size_guess = 0;
1117 dirserv_spool_remove_missing_and_guess_size(conn, 0,
1118 compress_method != NO_METHOD,
1119 &size_guess, NULL);
1120 if (smartlist_len(conn->spool) == 0) {
1121 write_short_http_response(conn, 404, "Not found");
1122 goto done;
1124 if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
1125 log_info(LD_DIRSERV,
1126 "Client asked for server descriptors, but we've been "
1127 "writing too many bytes lately. Sending 503 Dir busy.");
1128 write_short_http_response(conn, 503, "Directory busy, try again later");
1129 goto done;
1132 clear_spool = 0;
1133 write_http_response_header(conn, -1,
1134 compress_method,
1135 MICRODESC_CACHE_LIFETIME);
1137 if (compress_method != NO_METHOD)
1138 conn->compress_state = tor_compress_new(1, compress_method,
1139 choose_compression_level(size_guess));
1141 const int initial_flush_result = connection_dirserv_flushed_some(conn);
1142 tor_assert_nonfatal(initial_flush_result == 0);
1143 goto done;
1146 done:
1147 if (clear_spool) {
1148 dir_conn_clear_spool(conn);
1150 return 0;
1153 /** Helper function for GET /tor/{server,extra}/...
1155 static int
1156 handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args)
1158 const char *url = args->url;
1159 const compress_method_t compress_method =
1160 find_best_compression_method(args->compression_supported, 1);
1161 const or_options_t *options = get_options();
1162 int clear_spool = 1;
1163 if (!strcmpstart(url,"/tor/server/") ||
1164 (!options->BridgeAuthoritativeDir &&
1165 !options->BridgeRelay && !strcmpstart(url,"/tor/extra/"))) {
1166 int res;
1167 const char *msg = NULL;
1168 int cache_lifetime = 0;
1169 int is_extra = !strcmpstart(url,"/tor/extra/");
1170 url += is_extra ? strlen("/tor/extra/") : strlen("/tor/server/");
1171 dir_spool_source_t source;
1172 time_t publish_cutoff = 0;
1173 if (!strcmpstart(url, "d/")) {
1174 source =
1175 is_extra ? DIR_SPOOL_EXTRA_BY_DIGEST : DIR_SPOOL_SERVER_BY_DIGEST;
1176 } else {
1177 source =
1178 is_extra ? DIR_SPOOL_EXTRA_BY_FP : DIR_SPOOL_SERVER_BY_FP;
1179 /* We only want to apply a publish cutoff when we're requesting
1180 * resources by fingerprint. */
1181 publish_cutoff = time(NULL) - ROUTER_MAX_AGE_TO_PUBLISH;
1184 conn->spool = smartlist_new();
1185 res = dirserv_get_routerdesc_spool(conn->spool, url,
1186 source,
1187 connection_dir_is_encrypted(conn),
1188 &msg);
1190 if (!strcmpstart(url, "all")) {
1191 cache_lifetime = FULL_DIR_CACHE_LIFETIME;
1192 } else if (smartlist_len(conn->spool) == 1) {
1193 cache_lifetime = ROUTERDESC_BY_DIGEST_CACHE_LIFETIME;
1196 size_t size_guess = 0;
1197 int n_expired = 0;
1198 dirserv_spool_remove_missing_and_guess_size(conn, publish_cutoff,
1199 compress_method != NO_METHOD,
1200 &size_guess, &n_expired);
1202 /* If we are the bridge authority and the descriptor is a bridge
1203 * descriptor, remember that we served this descriptor for desc stats. */
1204 /* XXXX it's a bit of a kludge to have this here. */
1205 if (get_options()->BridgeAuthoritativeDir &&
1206 source == DIR_SPOOL_SERVER_BY_FP) {
1207 SMARTLIST_FOREACH_BEGIN(conn->spool, spooled_resource_t *, spooled) {
1208 const routerinfo_t *router =
1209 router_get_by_id_digest((const char *)spooled->digest);
1210 /* router can be NULL here when the bridge auth is asked for its own
1211 * descriptor. */
1212 if (router && router->purpose == ROUTER_PURPOSE_BRIDGE)
1213 rep_hist_note_desc_served(router->cache_info.identity_digest);
1214 } SMARTLIST_FOREACH_END(spooled);
1217 if (res < 0 || size_guess == 0 || smartlist_len(conn->spool) == 0) {
1218 if (msg == NULL)
1219 msg = "Not found";
1220 write_short_http_response(conn, 404, msg);
1221 } else {
1222 if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
1223 log_info(LD_DIRSERV,
1224 "Client asked for server descriptors, but we've been "
1225 "writing too many bytes lately. Sending 503 Dir busy.");
1226 write_short_http_response(conn, 503,
1227 "Directory busy, try again later");
1228 dir_conn_clear_spool(conn);
1229 goto done;
1231 write_http_response_header(conn, -1, compress_method, cache_lifetime);
1232 if (compress_method != NO_METHOD)
1233 conn->compress_state = tor_compress_new(1, compress_method,
1234 choose_compression_level(size_guess));
1235 clear_spool = 0;
1236 /* Prime the connection with some data. */
1237 int initial_flush_result = connection_dirserv_flushed_some(conn);
1238 tor_assert_nonfatal(initial_flush_result == 0);
1240 goto done;
1242 done:
1243 if (clear_spool)
1244 dir_conn_clear_spool(conn);
1245 return 0;
1248 /** Helper function for GET /tor/keys/...
1250 static int
1251 handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
1253 const char *url = args->url;
1254 const compress_method_t compress_method =
1255 find_best_compression_method(args->compression_supported, 1);
1256 const time_t if_modified_since = args->if_modified_since;
1258 smartlist_t *certs = smartlist_new();
1259 ssize_t len = -1;
1260 if (!strcmp(url, "/tor/keys/all")) {
1261 authority_cert_get_all(certs);
1262 } else if (!strcmp(url, "/tor/keys/authority")) {
1263 authority_cert_t *cert = get_my_v3_authority_cert();
1264 if (cert)
1265 smartlist_add(certs, cert);
1266 } else if (!strcmpstart(url, "/tor/keys/fp/")) {
1267 smartlist_t *fps = smartlist_new();
1268 dir_split_resource_into_fingerprints(url+strlen("/tor/keys/fp/"),
1269 fps, NULL,
1270 DSR_HEX|DSR_SORT_UNIQ);
1271 SMARTLIST_FOREACH(fps, char *, d, {
1272 authority_cert_t *c = authority_cert_get_newest_by_id(d);
1273 if (c) smartlist_add(certs, c);
1274 tor_free(d);
1276 smartlist_free(fps);
1277 } else if (!strcmpstart(url, "/tor/keys/sk/")) {
1278 smartlist_t *fps = smartlist_new();
1279 dir_split_resource_into_fingerprints(url+strlen("/tor/keys/sk/"),
1280 fps, NULL,
1281 DSR_HEX|DSR_SORT_UNIQ);
1282 SMARTLIST_FOREACH(fps, char *, d, {
1283 authority_cert_t *c = authority_cert_get_by_sk_digest(d);
1284 if (c) smartlist_add(certs, c);
1285 tor_free(d);
1287 smartlist_free(fps);
1288 } else if (!strcmpstart(url, "/tor/keys/fp-sk/")) {
1289 smartlist_t *fp_sks = smartlist_new();
1290 dir_split_resource_into_fingerprint_pairs(url+strlen("/tor/keys/fp-sk/"),
1291 fp_sks);
1292 SMARTLIST_FOREACH(fp_sks, fp_pair_t *, pair, {
1293 authority_cert_t *c = authority_cert_get_by_digests(pair->first,
1294 pair->second);
1295 if (c) smartlist_add(certs, c);
1296 tor_free(pair);
1298 smartlist_free(fp_sks);
1299 } else {
1300 write_short_http_response(conn, 400, "Bad request");
1301 goto keys_done;
1303 if (!smartlist_len(certs)) {
1304 write_short_http_response(conn, 404, "Not found");
1305 goto keys_done;
1307 SMARTLIST_FOREACH(certs, authority_cert_t *, c,
1308 if (c->cache_info.published_on < if_modified_since)
1309 SMARTLIST_DEL_CURRENT(certs, c));
1310 if (!smartlist_len(certs)) {
1311 write_short_http_response(conn, 304, "Not modified");
1312 goto keys_done;
1314 len = 0;
1315 SMARTLIST_FOREACH(certs, authority_cert_t *, c,
1316 len += c->cache_info.signed_descriptor_len);
1318 if (global_write_bucket_low(TO_CONN(conn),
1319 compress_method != NO_METHOD ? len/2 : len,
1320 2)) {
1321 write_short_http_response(conn, 503, "Directory busy, try again later");
1322 goto keys_done;
1325 write_http_response_header(conn,
1326 compress_method != NO_METHOD ? -1 : len,
1327 compress_method,
1328 60*60);
1329 if (compress_method != NO_METHOD) {
1330 conn->compress_state = tor_compress_new(1, compress_method,
1331 choose_compression_level(len));
1332 SMARTLIST_FOREACH(certs, authority_cert_t *, c,
1333 connection_buf_add_compress(
1334 c->cache_info.signed_descriptor_body,
1335 c->cache_info.signed_descriptor_len,
1336 conn, 0));
1337 connection_buf_add_compress("", 0, conn, 1);
1338 } else {
1339 SMARTLIST_FOREACH(certs, authority_cert_t *, c,
1340 connection_buf_add(c->cache_info.signed_descriptor_body,
1341 c->cache_info.signed_descriptor_len,
1342 TO_CONN(conn)));
1344 keys_done:
1345 smartlist_free(certs);
1346 goto done;
1348 done:
1349 return 0;
1352 /** Helper function for GET /tor/rendezvous2/
1354 static int
1355 handle_get_hs_descriptor_v2(dir_connection_t *conn,
1356 const get_handler_args_t *args)
1358 const char *url = args->url;
1359 if (connection_dir_is_encrypted(conn)) {
1360 /* Handle v2 rendezvous descriptor fetch request. */
1361 const char *descp;
1362 const char *query = url + strlen("/tor/rendezvous2/");
1363 if (rend_valid_descriptor_id(query)) {
1364 log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'",
1365 safe_str(escaped(query)));
1366 switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) {
1367 case 1: /* valid */
1368 write_http_response_header(conn, strlen(descp), NO_METHOD, 0);
1369 connection_buf_add(descp, strlen(descp), TO_CONN(conn));
1370 break;
1371 case 0: /* well-formed but not present */
1372 write_short_http_response(conn, 404, "Not found");
1373 break;
1374 case -1: /* not well-formed */
1375 write_short_http_response(conn, 400, "Bad request");
1376 break;
1378 } else { /* not well-formed */
1379 write_short_http_response(conn, 400, "Bad request");
1381 goto done;
1382 } else {
1383 /* Not encrypted! */
1384 write_short_http_response(conn, 404, "Not found");
1386 done:
1387 return 0;
1390 /** Helper function for GET /tor/hs/3/<z>. Only for version 3.
1392 STATIC int
1393 handle_get_hs_descriptor_v3(dir_connection_t *conn,
1394 const get_handler_args_t *args)
1396 int retval;
1397 const char *desc_str = NULL;
1398 const char *pubkey_str = NULL;
1399 const char *url = args->url;
1401 /* Reject unencrypted dir connections */
1402 if (!connection_dir_is_encrypted(conn)) {
1403 write_short_http_response(conn, 404, "Not found");
1404 goto done;
1407 /* After the path prefix follows the base64 encoded blinded pubkey which we
1408 * use to get the descriptor from the cache. Skip the prefix and get the
1409 * pubkey. */
1410 tor_assert(!strcmpstart(url, "/tor/hs/3/"));
1411 pubkey_str = url + strlen("/tor/hs/3/");
1412 retval = hs_cache_lookup_as_dir(HS_VERSION_THREE,
1413 pubkey_str, &desc_str);
1414 if (retval <= 0 || desc_str == NULL) {
1415 write_short_http_response(conn, 404, "Not found");
1416 goto done;
1419 /* Found requested descriptor! Pass it to this nice client. */
1420 write_http_response_header(conn, strlen(desc_str), NO_METHOD, 0);
1421 connection_buf_add(desc_str, strlen(desc_str), TO_CONN(conn));
1423 done:
1424 return 0;
1427 /** Helper function for GET /tor/networkstatus-bridges
1429 static int
1430 handle_get_networkstatus_bridges(dir_connection_t *conn,
1431 const get_handler_args_t *args)
1433 const char *headers = args->headers;
1435 const or_options_t *options = get_options();
1436 if (options->BridgeAuthoritativeDir &&
1437 options->BridgePassword_AuthDigest_ &&
1438 connection_dir_is_encrypted(conn)) {
1439 char *status;
1440 char digest[DIGEST256_LEN];
1442 char *header = http_get_header(headers, "Authorization: Basic ");
1443 if (header)
1444 crypto_digest256(digest, header, strlen(header), DIGEST_SHA256);
1446 /* now make sure the password is there and right */
1447 if (!header ||
1448 tor_memneq(digest,
1449 options->BridgePassword_AuthDigest_, DIGEST256_LEN)) {
1450 write_short_http_response(conn, 404, "Not found");
1451 tor_free(header);
1452 goto done;
1454 tor_free(header);
1456 /* all happy now. send an answer. */
1457 status = networkstatus_getinfo_by_purpose("bridge", time(NULL));
1458 size_t dlen = strlen(status);
1459 write_http_response_header(conn, dlen, NO_METHOD, 0);
1460 connection_buf_add(status, dlen, TO_CONN(conn));
1461 tor_free(status);
1462 goto done;
1464 done:
1465 return 0;
1468 /** Helper function for GET the bandwidth file used for the next vote */
1469 static int
1470 handle_get_next_bandwidth(dir_connection_t *conn,
1471 const get_handler_args_t *args)
1473 log_debug(LD_DIR, "Getting next bandwidth.");
1474 const or_options_t *options = get_options();
1475 const compress_method_t compress_method =
1476 find_best_compression_method(args->compression_supported, 1);
1478 if (options->V3BandwidthsFile) {
1479 char *bandwidth = read_file_to_str(options->V3BandwidthsFile,
1480 RFTS_IGNORE_MISSING, NULL);
1481 if (bandwidth != NULL) {
1482 ssize_t len = strlen(bandwidth);
1483 write_http_response_header(conn, compress_method != NO_METHOD ? -1 : len,
1484 compress_method, BANDWIDTH_CACHE_LIFETIME);
1485 if (compress_method != NO_METHOD) {
1486 conn->compress_state = tor_compress_new(1, compress_method,
1487 choose_compression_level(len/2));
1488 log_debug(LD_DIR, "Compressing bandwidth file.");
1489 connection_buf_add_compress(bandwidth, len, conn, 0);
1490 /* Flush the compression state. */
1491 connection_buf_add_compress("", 0, conn, 1);
1492 } else {
1493 log_debug(LD_DIR, "Not compressing bandwidth file.");
1494 connection_buf_add(bandwidth, len, TO_CONN(conn));
1496 tor_free(bandwidth);
1497 return 0;
1500 write_short_http_response(conn, 404, "Not found");
1501 return 0;
1504 /** Helper function for GET robots.txt or /tor/robots.txt */
1505 static int
1506 handle_get_robots(dir_connection_t *conn, const get_handler_args_t *args)
1508 (void)args;
1510 const char robots[] = "User-agent: *\r\nDisallow: /\r\n";
1511 size_t len = strlen(robots);
1512 write_http_response_header(conn, len, NO_METHOD, ROBOTS_CACHE_LIFETIME);
1513 connection_buf_add(robots, len, TO_CONN(conn));
1515 return 0;
1518 /* Given the <b>url</b> from a POST request, try to extract the version number
1519 * using the provided <b>prefix</b>. The version should be after the prefix and
1520 * ending with the separator "/". For instance:
1521 * /tor/hs/3/publish
1523 * On success, <b>end_pos</b> points to the position right after the version
1524 * was found. On error, it is set to NULL.
1526 * Return version on success else negative value. */
1527 STATIC int
1528 parse_hs_version_from_post(const char *url, const char *prefix,
1529 const char **end_pos)
1531 int ok;
1532 unsigned long version;
1533 const char *start;
1534 char *end = NULL;
1536 tor_assert(url);
1537 tor_assert(prefix);
1538 tor_assert(end_pos);
1540 /* Check if the prefix does start the url. */
1541 if (strcmpstart(url, prefix)) {
1542 goto err;
1544 /* Move pointer to the end of the prefix string. */
1545 start = url + strlen(prefix);
1546 /* Try this to be the HS version and if we are still at the separator, next
1547 * will be move to the right value. */
1548 version = tor_parse_long(start, 10, 0, INT_MAX, &ok, &end);
1549 if (!ok) {
1550 goto err;
1553 *end_pos = end;
1554 return (int) version;
1555 err:
1556 *end_pos = NULL;
1557 return -1;
1560 /* Handle the POST request for a hidden service descripror. The request is in
1561 * <b>url</b>, the body of the request is in <b>body</b>. Return 200 on success
1562 * else return 400 indicating a bad request. */
1563 STATIC int
1564 handle_post_hs_descriptor(const char *url, const char *body)
1566 int version;
1567 const char *end_pos;
1569 tor_assert(url);
1570 tor_assert(body);
1572 version = parse_hs_version_from_post(url, "/tor/hs/", &end_pos);
1573 if (version < 0) {
1574 goto err;
1577 /* We have a valid version number, now make sure it's a publish request. Use
1578 * the end position just after the version and check for the command. */
1579 if (strcmpstart(end_pos, "/publish")) {
1580 goto err;
1583 switch (version) {
1584 case HS_VERSION_THREE:
1585 if (hs_cache_store_as_dir(body) < 0) {
1586 goto err;
1588 log_info(LD_REND, "Publish request for HS descriptor handled "
1589 "successfully.");
1590 break;
1591 default:
1592 /* Unsupported version, return a bad request. */
1593 goto err;
1596 return 200;
1597 err:
1598 /* Bad request. */
1599 return 400;
1602 /** Helper function: called when a dirserver gets a complete HTTP POST
1603 * request. Look for an uploaded server descriptor or rendezvous
1604 * service descriptor. On finding one, process it and write a
1605 * response into conn-\>outbuf. If the request is unrecognized, send a
1606 * 400. Always return 0. */
1607 MOCK_IMPL(STATIC int,
1608 directory_handle_command_post,(dir_connection_t *conn, const char *headers,
1609 const char *body, size_t body_len))
1611 char *url = NULL;
1612 const or_options_t *options = get_options();
1614 log_debug(LD_DIRSERV,"Received POST command.");
1616 conn->base_.state = DIR_CONN_STATE_SERVER_WRITING;
1618 if (!public_server_mode(options)) {
1619 log_info(LD_DIR, "Rejected dir post request from %s "
1620 "since we're not a public relay.", conn->base_.address);
1621 write_short_http_response(conn, 503, "Not acting as a public relay");
1622 goto done;
1625 if (parse_http_url(headers, &url) < 0) {
1626 write_short_http_response(conn, 400, "Bad request");
1627 return 0;
1629 log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url));
1631 /* Handle v2 rendezvous service publish request. */
1632 if (connection_dir_is_encrypted(conn) &&
1633 !strcmpstart(url,"/tor/rendezvous2/publish")) {
1634 if (rend_cache_store_v2_desc_as_dir(body) < 0) {
1635 log_warn(LD_REND, "Rejected v2 rend descriptor (body size %d) from %s.",
1636 (int)body_len, conn->base_.address);
1637 write_short_http_response(conn, 400,
1638 "Invalid v2 service descriptor rejected");
1639 } else {
1640 write_short_http_response(conn, 200, "Service descriptor (v2) stored");
1641 log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted");
1643 goto done;
1646 /* Handle HS descriptor publish request. */
1647 /* XXX: This should be disabled with a consensus param until we want to
1648 * the prop224 be deployed and thus use. */
1649 if (connection_dir_is_encrypted(conn) && !strcmpstart(url, "/tor/hs/")) {
1650 const char *msg = "HS descriptor stored successfully.";
1652 /* We most probably have a publish request for an HS descriptor. */
1653 int code = handle_post_hs_descriptor(url, body);
1654 if (code != 200) {
1655 msg = "Invalid HS descriptor. Rejected.";
1657 write_short_http_response(conn, code, msg);
1658 goto done;
1661 if (!authdir_mode(options)) {
1662 /* we just provide cached directories; we don't want to
1663 * receive anything. */
1664 write_short_http_response(conn, 400, "Nonauthoritative directory does not "
1665 "accept posted server descriptors");
1666 goto done;
1669 if (authdir_mode(options) &&
1670 !strcmp(url,"/tor/")) { /* server descriptor post */
1671 const char *msg = "[None]";
1672 uint8_t purpose = authdir_mode_bridge(options) ?
1673 ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL;
1674 was_router_added_t r = dirserv_add_multiple_descriptors(body, body_len,
1675 purpose, conn->base_.address, &msg);
1676 tor_assert(msg);
1678 if (r == ROUTER_ADDED_SUCCESSFULLY) {
1679 write_short_http_response(conn, 200, msg);
1680 } else if (WRA_WAS_OUTDATED(r)) {
1681 write_http_response_header_impl(conn, -1, NULL, NULL,
1682 "X-Descriptor-Not-New: Yes\r\n", -1);
1683 } else {
1684 log_info(LD_DIRSERV,
1685 "Rejected router descriptor or extra-info from %s "
1686 "(\"%s\").",
1687 conn->base_.address, msg);
1688 write_short_http_response(conn, 400, msg);
1690 goto done;
1693 if (authdir_mode_v3(options) &&
1694 !strcmp(url,"/tor/post/vote")) { /* v3 networkstatus vote */
1695 const char *msg = "OK";
1696 int status;
1697 if (dirvote_add_vote(body, &msg, &status)) {
1698 write_short_http_response(conn, status, "Vote stored");
1699 } else {
1700 tor_assert(msg);
1701 log_warn(LD_DIRSERV, "Rejected vote from %s (\"%s\").",
1702 conn->base_.address, msg);
1703 write_short_http_response(conn, status, msg);
1705 goto done;
1708 if (authdir_mode_v3(options) &&
1709 !strcmp(url,"/tor/post/consensus-signature")) { /* sigs on consensus. */
1710 const char *msg = NULL;
1711 if (dirvote_add_signatures(body, conn->base_.address, &msg)>=0) {
1712 write_short_http_response(conn, 200, msg?msg:"Signatures stored");
1713 } else {
1714 log_warn(LD_DIR, "Unable to store signatures posted by %s: %s",
1715 conn->base_.address, msg?msg:"???");
1716 write_short_http_response(conn, 400,
1717 msg?msg:"Unable to store signatures");
1719 goto done;
1722 /* we didn't recognize the url */
1723 write_short_http_response(conn, 404, "Not found");
1725 done:
1726 tor_free(url);
1727 return 0;
1730 /** If <b>headers</b> indicates that a proxy was involved, then rewrite
1731 * <b>conn</b>-\>address to describe our best guess of the address that
1732 * originated this HTTP request. */
1733 static void
1734 http_set_address_origin(const char *headers, connection_t *conn)
1736 char *fwd;
1738 fwd = http_get_header(headers, "Forwarded-For: ");
1739 if (!fwd)
1740 fwd = http_get_header(headers, "X-Forwarded-For: ");
1741 if (fwd) {
1742 tor_addr_t toraddr;
1743 if (tor_addr_parse(&toraddr,fwd) == -1 ||
1744 tor_addr_is_internal(&toraddr,0)) {
1745 log_debug(LD_DIR, "Ignoring local/internal IP %s", escaped(fwd));
1746 tor_free(fwd);
1747 return;
1750 tor_free(conn->address);
1751 conn->address = tor_strdup(fwd);
1752 tor_free(fwd);
1756 /** Called when a dirserver receives data on a directory connection;
1757 * looks for an HTTP request. If the request is complete, remove it
1758 * from the inbuf, try to process it; otherwise, leave it on the
1759 * buffer. Return a 0 on success, or -1 on error.
1762 directory_handle_command(dir_connection_t *conn)
1764 char *headers=NULL, *body=NULL;
1765 size_t body_len=0;
1766 int r;
1768 tor_assert(conn);
1769 tor_assert(conn->base_.type == CONN_TYPE_DIR);
1771 switch (connection_fetch_from_buf_http(TO_CONN(conn),
1772 &headers, MAX_HEADERS_SIZE,
1773 &body, &body_len, MAX_DIR_UL_SIZE, 0)) {
1774 case -1: /* overflow */
1775 log_warn(LD_DIRSERV,
1776 "Request too large from address '%s' to DirPort. Closing.",
1777 safe_str(conn->base_.address));
1778 return -1;
1779 case 0:
1780 log_debug(LD_DIRSERV,"command not all here yet.");
1781 return 0;
1782 /* case 1, fall through */
1785 http_set_address_origin(headers, TO_CONN(conn));
1786 // we should escape headers here as well,
1787 // but we can't call escaped() twice, as it uses the same buffer
1788 //log_debug(LD_DIRSERV,"headers %s, body %s.", headers, escaped(body));
1790 if (!strncasecmp(headers,"GET",3))
1791 r = directory_handle_command_get(conn, headers, body, body_len);
1792 else if (!strncasecmp(headers,"POST",4))
1793 r = directory_handle_command_post(conn, headers, body, body_len);
1794 else {
1795 log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
1796 "Got headers %s with unknown command. Closing.",
1797 escaped(headers));
1798 r = -1;
1801 tor_free(headers); tor_free(body);
1802 return r;