Rename dirauth/mode.h to dirauth/authmode.h
[tor.git] / src / feature / dircache / dircache.c
blobc58512814ea714e064842d50155036d68eda7636
1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2018, 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/router.h"
27 #include "feature/rend/rendcache.h"
28 #include "feature/stats/geoip.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)
53 /** Parse an HTTP request string <b>headers</b> of the form
54 * \verbatim
55 * "\%s [http[s]://]\%s HTTP/1..."
56 * \endverbatim
57 * If it's well-formed, strdup the second \%s into *<b>url</b>, and
58 * nul-terminate it. If the url doesn't start with "/tor/", rewrite it
59 * so it does. Return 0.
60 * Otherwise, return -1.
62 STATIC int
63 parse_http_url(const char *headers, char **url)
65 char *command = NULL;
66 if (parse_http_command(headers, &command, url) < 0) {
67 return -1;
69 if (strcmpstart(*url, "/tor/")) {
70 char *new_url = NULL;
71 tor_asprintf(&new_url, "/tor%s%s",
72 *url[0] == '/' ? "" : "/",
73 *url);
74 tor_free(*url);
75 *url = new_url;
77 tor_free(command);
78 return 0;
81 /** Create an http response for the client <b>conn</b> out of
82 * <b>status</b> and <b>reason_phrase</b>. Write it to <b>conn</b>.
84 static void
85 write_short_http_response(dir_connection_t *conn, int status,
86 const char *reason_phrase)
88 char *buf = NULL;
89 char *datestring = NULL;
91 IF_BUG_ONCE(!reason_phrase) { /* bullet-proofing */
92 reason_phrase = "unspecified";
95 if (server_mode(get_options())) {
96 /* include the Date: header, but only if we're a relay or bridge */
97 char datebuf[RFC1123_TIME_LEN+1];
98 format_rfc1123_time(datebuf, time(NULL));
99 tor_asprintf(&datestring, "Date: %s\r\n", datebuf);
102 tor_asprintf(&buf, "HTTP/1.0 %d %s\r\n%s\r\n",
103 status, reason_phrase, datestring?datestring:"");
105 log_debug(LD_DIRSERV,"Wrote status 'HTTP/1.0 %d %s'", status, reason_phrase);
106 connection_buf_add(buf, strlen(buf), TO_CONN(conn));
108 tor_free(datestring);
109 tor_free(buf);
112 /** Write the header for an HTTP/1.0 response onto <b>conn</b>-\>outbuf,
113 * with <b>type</b> as the Content-Type.
115 * If <b>length</b> is nonnegative, it is the Content-Length.
116 * If <b>encoding</b> is provided, it is the Content-Encoding.
117 * If <b>cache_lifetime</b> is greater than 0, the content may be cached for
118 * up to cache_lifetime seconds. Otherwise, the content may not be cached. */
119 static void
120 write_http_response_header_impl(dir_connection_t *conn, ssize_t length,
121 const char *type, const char *encoding,
122 const char *extra_headers,
123 long cache_lifetime)
125 char date[RFC1123_TIME_LEN+1];
126 time_t now = time(NULL);
127 buf_t *buf = buf_new_with_capacity(1024);
129 tor_assert(conn);
131 format_rfc1123_time(date, now);
133 buf_add_printf(buf, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date);
134 if (type) {
135 buf_add_printf(buf, "Content-Type: %s\r\n", type);
137 if (!is_local_addr(&conn->base_.addr)) {
138 /* Don't report the source address for a nearby/private connection.
139 * Otherwise we tend to mis-report in cases where incoming ports are
140 * being forwarded to a Tor server running behind the firewall. */
141 buf_add_printf(buf, X_ADDRESS_HEADER "%s\r\n", conn->base_.address);
143 if (encoding) {
144 buf_add_printf(buf, "Content-Encoding: %s\r\n", encoding);
146 if (length >= 0) {
147 buf_add_printf(buf, "Content-Length: %ld\r\n", (long)length);
149 if (cache_lifetime > 0) {
150 char expbuf[RFC1123_TIME_LEN+1];
151 format_rfc1123_time(expbuf, (time_t)(now + cache_lifetime));
152 /* We could say 'Cache-control: max-age=%d' here if we start doing
153 * http/1.1 */
154 buf_add_printf(buf, "Expires: %s\r\n", expbuf);
155 } else if (cache_lifetime == 0) {
156 /* We could say 'Cache-control: no-cache' here if we start doing
157 * http/1.1 */
158 buf_add_string(buf, "Pragma: no-cache\r\n");
160 if (extra_headers) {
161 buf_add_string(buf, extra_headers);
163 buf_add_string(buf, "\r\n");
165 connection_buf_add_buf(TO_CONN(conn), buf);
166 buf_free(buf);
169 /** As write_http_response_header_impl, but sets encoding and content-typed
170 * based on whether the response will be <b>compressed</b> or not. */
171 static void
172 write_http_response_headers(dir_connection_t *conn, ssize_t length,
173 compress_method_t method,
174 const char *extra_headers, long cache_lifetime)
176 const char *methodname = compression_method_get_name(method);
177 const char *doctype;
178 if (method == NO_METHOD)
179 doctype = "text/plain";
180 else
181 doctype = "application/octet-stream";
182 write_http_response_header_impl(conn, length,
183 doctype,
184 methodname,
185 extra_headers,
186 cache_lifetime);
189 /** As write_http_response_headers, but assumes extra_headers is NULL */
190 static void
191 write_http_response_header(dir_connection_t *conn, ssize_t length,
192 compress_method_t method,
193 long cache_lifetime)
195 write_http_response_headers(conn, length, method, NULL, cache_lifetime);
198 /** Array of compression methods to use (if supported) for serving
199 * precompressed data, ordered from best to worst. */
200 static compress_method_t srv_meth_pref_precompressed[] = {
201 LZMA_METHOD,
202 ZSTD_METHOD,
203 ZLIB_METHOD,
204 GZIP_METHOD,
205 NO_METHOD
208 /** Array of compression methods to use (if supported) for serving
209 * streamed data, ordered from best to worst. */
210 static compress_method_t srv_meth_pref_streaming_compression[] = {
211 ZSTD_METHOD,
212 ZLIB_METHOD,
213 GZIP_METHOD,
214 NO_METHOD
217 /** Parse the compression methods listed in an Accept-Encoding header <b>h</b>,
218 * and convert them to a bitfield where compression method x is supported if
219 * and only if 1 &lt;&lt; x is set in the bitfield. */
220 STATIC unsigned
221 parse_accept_encoding_header(const char *h)
223 unsigned result = (1u << NO_METHOD);
224 smartlist_t *methods = smartlist_new();
225 smartlist_split_string(methods, h, ",",
226 SPLIT_SKIP_SPACE|SPLIT_STRIP_SPACE|SPLIT_IGNORE_BLANK, 0);
228 SMARTLIST_FOREACH_BEGIN(methods, const char *, m) {
229 compress_method_t method = compression_method_get_by_name(m);
230 if (method != UNKNOWN_METHOD) {
231 tor_assert(((unsigned)method) < 8*sizeof(unsigned));
232 result |= (1u << method);
234 } SMARTLIST_FOREACH_END(m);
235 SMARTLIST_FOREACH_BEGIN(methods, char *, m) {
236 tor_free(m);
237 } SMARTLIST_FOREACH_END(m);
238 smartlist_free(methods);
239 return result;
242 /** Decide whether a client would accept the consensus we have.
244 * Clients can say they only want a consensus if it's signed by more
245 * than half the authorities in a list. They pass this list in
246 * the url as "...consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b>".
248 * <b>fpr</b> may be an abbreviated fingerprint, i.e. only a left substring
249 * of the full authority identity digest. (Only strings of even length,
250 * i.e. encodings of full bytes, are handled correctly. In the case
251 * of an odd number of hex digits the last one is silently ignored.)
253 * Returns 1 if more than half of the requested authorities signed the
254 * consensus, 0 otherwise.
256 static int
257 client_likes_consensus(const struct consensus_cache_entry_t *ent,
258 const char *want_url)
260 smartlist_t *voters = smartlist_new();
261 int need_at_least;
262 int have = 0;
264 if (consensus_cache_entry_get_voter_id_digests(ent, voters) != 0) {
265 smartlist_free(voters);
266 return 1; // We don't know the voters; assume the client won't mind. */
269 smartlist_t *want_authorities = smartlist_new();
270 dir_split_resource_into_fingerprints(want_url, want_authorities, NULL, 0);
271 need_at_least = smartlist_len(want_authorities)/2+1;
273 SMARTLIST_FOREACH_BEGIN(want_authorities, const char *, want_digest) {
275 SMARTLIST_FOREACH_BEGIN(voters, const char *, digest) {
276 if (!strcasecmpstart(digest, want_digest)) {
277 have++;
278 break;
280 } SMARTLIST_FOREACH_END(digest);
282 /* early exit, if we already have enough */
283 if (have >= need_at_least)
284 break;
285 } SMARTLIST_FOREACH_END(want_digest);
287 SMARTLIST_FOREACH(want_authorities, char *, d, tor_free(d));
288 smartlist_free(want_authorities);
289 SMARTLIST_FOREACH(voters, char *, cp, tor_free(cp));
290 smartlist_free(voters);
291 return (have >= need_at_least);
294 /** Return the compression level we should use for sending a compressed
295 * response of size <b>n_bytes</b>. */
296 STATIC compression_level_t
297 choose_compression_level(ssize_t n_bytes)
299 if (! have_been_under_memory_pressure()) {
300 return HIGH_COMPRESSION; /* we have plenty of RAM. */
301 } else if (n_bytes < 0) {
302 return HIGH_COMPRESSION; /* unknown; might be big. */
303 } else if (n_bytes < 1024) {
304 return LOW_COMPRESSION;
305 } else if (n_bytes < 2048) {
306 return MEDIUM_COMPRESSION;
307 } else {
308 return HIGH_COMPRESSION;
312 /** Information passed to handle a GET request. */
313 typedef struct get_handler_args_t {
314 /** Bitmask of compression methods that the client said (or implied) it
315 * supported. */
316 unsigned compression_supported;
317 /** If nonzero, the time included an if-modified-since header with this
318 * value. */
319 time_t if_modified_since;
320 /** String containing the requested URL or resource. */
321 const char *url;
322 /** String containing the HTTP headers */
323 const char *headers;
324 } get_handler_args_t;
326 /** Entry for handling an HTTP GET request.
328 * This entry matches a request if "string" is equal to the requested
329 * resource, or if "is_prefix" is true and "string" is a prefix of the
330 * requested resource.
332 * The 'handler' function is called to handle the request. It receives
333 * an arguments structure, and must return 0 on success or -1 if we should
334 * close the connection.
336 typedef struct url_table_ent_s {
337 const char *string;
338 int is_prefix;
339 int (*handler)(dir_connection_t *conn, const get_handler_args_t *args);
340 } url_table_ent_t;
342 static int handle_get_frontpage(dir_connection_t *conn,
343 const get_handler_args_t *args);
344 static int handle_get_current_consensus(dir_connection_t *conn,
345 const get_handler_args_t *args);
346 static int handle_get_status_vote(dir_connection_t *conn,
347 const get_handler_args_t *args);
348 static int handle_get_microdesc(dir_connection_t *conn,
349 const get_handler_args_t *args);
350 static int handle_get_descriptor(dir_connection_t *conn,
351 const get_handler_args_t *args);
352 static int handle_get_keys(dir_connection_t *conn,
353 const get_handler_args_t *args);
354 static int handle_get_hs_descriptor_v2(dir_connection_t *conn,
355 const get_handler_args_t *args);
356 static int handle_get_robots(dir_connection_t *conn,
357 const get_handler_args_t *args);
358 static int handle_get_networkstatus_bridges(dir_connection_t *conn,
359 const get_handler_args_t *args);
361 /** Table for handling GET requests. */
362 static const url_table_ent_t url_table[] = {
363 { "/tor/", 0, handle_get_frontpage },
364 { "/tor/status-vote/current/consensus", 1, handle_get_current_consensus },
365 { "/tor/status-vote/current/", 1, handle_get_status_vote },
366 { "/tor/status-vote/next/", 1, handle_get_status_vote },
367 { "/tor/micro/d/", 1, handle_get_microdesc },
368 { "/tor/server/", 1, handle_get_descriptor },
369 { "/tor/extra/", 1, handle_get_descriptor },
370 { "/tor/keys/", 1, handle_get_keys },
371 { "/tor/rendezvous2/", 1, handle_get_hs_descriptor_v2 },
372 { "/tor/hs/3/", 1, handle_get_hs_descriptor_v3 },
373 { "/tor/robots.txt", 0, handle_get_robots },
374 { "/tor/networkstatus-bridges", 0, handle_get_networkstatus_bridges },
375 { NULL, 0, NULL },
378 /** Helper function: called when a dirserver gets a complete HTTP GET
379 * request. Look for a request for a directory or for a rendezvous
380 * service descriptor. On finding one, write a response into
381 * conn-\>outbuf. If the request is unrecognized, send a 404.
382 * Return 0 if we handled this successfully, or -1 if we need to close
383 * the connection. */
384 MOCK_IMPL(STATIC int,
385 directory_handle_command_get,(dir_connection_t *conn, const char *headers,
386 const char *req_body, size_t req_body_len))
388 char *url, *url_mem, *header;
389 time_t if_modified_since = 0;
390 int zlib_compressed_in_url;
391 unsigned compression_methods_supported;
393 /* We ignore the body of a GET request. */
394 (void)req_body;
395 (void)req_body_len;
397 log_debug(LD_DIRSERV,"Received GET command.");
399 conn->base_.state = DIR_CONN_STATE_SERVER_WRITING;
401 if (parse_http_url(headers, &url) < 0) {
402 write_short_http_response(conn, 400, "Bad request");
403 return 0;
405 if ((header = http_get_header(headers, "If-Modified-Since: "))) {
406 struct tm tm;
407 if (parse_http_time(header, &tm) == 0) {
408 if (tor_timegm(&tm, &if_modified_since)<0) {
409 if_modified_since = 0;
410 } else {
411 log_debug(LD_DIRSERV, "If-Modified-Since is '%s'.", escaped(header));
414 /* The correct behavior on a malformed If-Modified-Since header is to
415 * act as if no If-Modified-Since header had been given. */
416 tor_free(header);
418 log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url));
420 url_mem = url;
422 size_t url_len = strlen(url);
424 zlib_compressed_in_url = url_len > 2 && !strcmp(url+url_len-2, ".z");
425 if (zlib_compressed_in_url) {
426 url[url_len-2] = '\0';
430 if ((header = http_get_header(headers, "Accept-Encoding: "))) {
431 compression_methods_supported = parse_accept_encoding_header(header);
432 tor_free(header);
433 } else {
434 compression_methods_supported = (1u << NO_METHOD);
436 if (zlib_compressed_in_url) {
437 compression_methods_supported |= (1u << ZLIB_METHOD);
440 /* Remove all methods that we don't both support. */
441 compression_methods_supported &= tor_compress_get_supported_method_bitmask();
443 get_handler_args_t args;
444 args.url = url;
445 args.headers = headers;
446 args.if_modified_since = if_modified_since;
447 args.compression_supported = compression_methods_supported;
449 int i, result = -1;
450 for (i = 0; url_table[i].string; ++i) {
451 int match;
452 if (url_table[i].is_prefix) {
453 match = !strcmpstart(url, url_table[i].string);
454 } else {
455 match = !strcmp(url, url_table[i].string);
457 if (match) {
458 result = url_table[i].handler(conn, &args);
459 goto done;
463 /* we didn't recognize the url */
464 write_short_http_response(conn, 404, "Not found");
465 result = 0;
467 done:
468 tor_free(url_mem);
469 return result;
472 /** Helper function for GET / or GET /tor/
474 static int
475 handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args)
477 (void) args; /* unused */
478 const char *frontpage = get_dirportfrontpage();
480 if (frontpage) {
481 size_t dlen;
482 dlen = strlen(frontpage);
483 /* Let's return a disclaimer page (users shouldn't use V1 anymore,
484 and caches don't fetch '/', so this is safe). */
486 /* [We don't check for write_bucket_low here, since we want to serve
487 * this page no matter what.] */
488 write_http_response_header_impl(conn, dlen, "text/html", "identity",
489 NULL, DIRPORTFRONTPAGE_CACHE_LIFETIME);
490 connection_buf_add(frontpage, dlen, TO_CONN(conn));
491 } else {
492 write_short_http_response(conn, 404, "Not found");
494 return 0;
497 /** Warn that the cached consensus <b>consensus</b> of type
498 * <b>flavor</b> is too old and will not be served to clients. Rate-limit the
499 * warning to avoid logging an entry on every request.
501 static void
502 warn_consensus_is_too_old(const struct consensus_cache_entry_t *consensus,
503 const char *flavor, time_t now)
505 #define TOO_OLD_WARNING_INTERVAL (60*60)
506 static ratelim_t warned = RATELIM_INIT(TOO_OLD_WARNING_INTERVAL);
507 char timestamp[ISO_TIME_LEN+1];
508 time_t valid_until;
509 char *dupes;
511 if (consensus_cache_entry_get_valid_until(consensus, &valid_until))
512 return;
514 if ((dupes = rate_limit_log(&warned, now))) {
515 format_local_iso_time(timestamp, valid_until);
516 log_warn(LD_DIRSERV, "Our %s%sconsensus is too old, so we will not "
517 "serve it to clients. It was valid until %s local time and we "
518 "continued to serve it for up to 24 hours after it expired.%s",
519 flavor ? flavor : "", flavor ? " " : "", timestamp, dupes);
520 tor_free(dupes);
525 * Parse a single hex-encoded sha3-256 digest from <b>hex</b> into
526 * <b>digest</b>. Return 0 on success. On failure, report that the hash came
527 * from <b>location</b>, report that we are taking <b>action</b> with it, and
528 * return -1.
530 static int
531 parse_one_diff_hash(uint8_t *digest, const char *hex, const char *location,
532 const char *action)
534 if (base16_decode((char*)digest, DIGEST256_LEN, hex, strlen(hex)) ==
535 DIGEST256_LEN) {
536 return 0;
537 } else {
538 log_fn(LOG_PROTOCOL_WARN, LD_DIR,
539 "%s contained bogus digest %s; %s.",
540 location, escaped(hex), action);
541 return -1;
545 /** If there is an X-Or-Diff-From-Consensus header included in <b>headers</b>,
546 * set <b>digest_out<b> to a new smartlist containing every 256-bit
547 * hex-encoded digest listed in that header and return 0. Otherwise return
548 * -1. */
549 static int
550 parse_or_diff_from_header(smartlist_t **digests_out, const char *headers)
552 char *hdr = http_get_header(headers, X_OR_DIFF_FROM_CONSENSUS_HEADER);
553 if (hdr == NULL) {
554 return -1;
556 smartlist_t *hex_digests = smartlist_new();
557 *digests_out = smartlist_new();
558 smartlist_split_string(hex_digests, hdr, " ",
559 SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
560 SMARTLIST_FOREACH_BEGIN(hex_digests, const char *, hex) {
561 uint8_t digest[DIGEST256_LEN];
562 if (!parse_one_diff_hash(digest, hex, "X-Or-Diff-From-Consensus header",
563 "ignoring")) {
564 smartlist_add(*digests_out, tor_memdup(digest, sizeof(digest)));
566 } SMARTLIST_FOREACH_END(hex);
567 SMARTLIST_FOREACH(hex_digests, char *, cp, tor_free(cp));
568 smartlist_free(hex_digests);
569 tor_free(hdr);
570 return 0;
573 /** Fallback compression method. The fallback compression method is used in
574 * case a client requests a non-compressed document. We only store compressed
575 * documents, so we use this compression method to fetch the document and let
576 * the spooling system do the streaming decompression.
578 #define FALLBACK_COMPRESS_METHOD ZLIB_METHOD
581 * Try to find the best consensus diff possible in order to serve a client
582 * request for a diff from one of the consensuses in <b>digests</b> to the
583 * current consensus of flavor <b>flav</b>. The client supports the
584 * compression methods listed in the <b>compression_methods</b> bitfield:
585 * place the method chosen (if any) into <b>compression_used_out</b>.
587 static struct consensus_cache_entry_t *
588 find_best_diff(const smartlist_t *digests, int flav,
589 unsigned compression_methods,
590 compress_method_t *compression_used_out)
592 struct consensus_cache_entry_t *result = NULL;
594 SMARTLIST_FOREACH_BEGIN(digests, const uint8_t *, diff_from) {
595 unsigned u;
596 for (u = 0; u < ARRAY_LENGTH(srv_meth_pref_precompressed); ++u) {
597 compress_method_t method = srv_meth_pref_precompressed[u];
598 if (0 == (compression_methods & (1u<<method)))
599 continue; // client doesn't like this one, or we don't have it.
600 if (consdiffmgr_find_diff_from(&result, flav, DIGEST_SHA3_256,
601 diff_from, DIGEST256_LEN,
602 method) == CONSDIFF_AVAILABLE) {
603 tor_assert_nonfatal(result);
604 *compression_used_out = method;
605 return result;
608 } SMARTLIST_FOREACH_END(diff_from);
610 SMARTLIST_FOREACH_BEGIN(digests, const uint8_t *, diff_from) {
611 if (consdiffmgr_find_diff_from(&result, flav, DIGEST_SHA3_256, diff_from,
612 DIGEST256_LEN, FALLBACK_COMPRESS_METHOD) == CONSDIFF_AVAILABLE) {
613 tor_assert_nonfatal(result);
614 *compression_used_out = FALLBACK_COMPRESS_METHOD;
615 return result;
617 } SMARTLIST_FOREACH_END(diff_from);
619 return NULL;
622 /** Lookup the cached consensus document by the flavor found in <b>flav</b>.
623 * The preferred set of compression methods should be listed in the
624 * <b>compression_methods</b> bitfield. The compression method chosen (if any)
625 * is stored in <b>compression_used_out</b>. */
626 static struct consensus_cache_entry_t *
627 find_best_consensus(int flav,
628 unsigned compression_methods,
629 compress_method_t *compression_used_out)
631 struct consensus_cache_entry_t *result = NULL;
632 unsigned u;
634 for (u = 0; u < ARRAY_LENGTH(srv_meth_pref_precompressed); ++u) {
635 compress_method_t method = srv_meth_pref_precompressed[u];
637 if (0 == (compression_methods & (1u<<method)))
638 continue;
640 if (consdiffmgr_find_consensus(&result, flav,
641 method) == CONSDIFF_AVAILABLE) {
642 tor_assert_nonfatal(result);
643 *compression_used_out = method;
644 return result;
648 if (consdiffmgr_find_consensus(&result, flav,
649 FALLBACK_COMPRESS_METHOD) == CONSDIFF_AVAILABLE) {
650 tor_assert_nonfatal(result);
651 *compression_used_out = FALLBACK_COMPRESS_METHOD;
652 return result;
655 return NULL;
658 /** Try to find the best supported compression method possible from a given
659 * <b>compression_methods</b>. Return NO_METHOD if no mutually supported
660 * compression method could be found. */
661 static compress_method_t
662 find_best_compression_method(unsigned compression_methods, int stream)
664 unsigned u;
665 compress_method_t *methods;
666 size_t length;
668 if (stream) {
669 methods = srv_meth_pref_streaming_compression;
670 length = ARRAY_LENGTH(srv_meth_pref_streaming_compression);
671 } else {
672 methods = srv_meth_pref_precompressed;
673 length = ARRAY_LENGTH(srv_meth_pref_precompressed);
676 for (u = 0; u < length; ++u) {
677 compress_method_t method = methods[u];
678 if (compression_methods & (1u<<method))
679 return method;
682 return NO_METHOD;
685 /** Check if any of the digests in <b>digests</b> matches the latest consensus
686 * flavor (given in <b>flavor</b>) that we have available. */
687 static int
688 digest_list_contains_best_consensus(consensus_flavor_t flavor,
689 const smartlist_t *digests)
691 const networkstatus_t *ns = NULL;
693 if (digests == NULL)
694 return 0;
696 ns = networkstatus_get_latest_consensus_by_flavor(flavor);
698 if (ns == NULL)
699 return 0;
701 SMARTLIST_FOREACH_BEGIN(digests, const uint8_t *, digest) {
702 if (tor_memeq(ns->digest_sha3_as_signed, digest, DIGEST256_LEN))
703 return 1;
704 } SMARTLIST_FOREACH_END(digest);
706 return 0;
709 /** Encodes the results of parsing a consensus request to figure out what
710 * consensus, and possibly what diffs, the user asked for. */
711 typedef struct {
712 /** name of the flavor to retrieve. */
713 char *flavor;
714 /** flavor to retrive, as enum. */
715 consensus_flavor_t flav;
716 /** plus-separated list of authority fingerprints; see
717 * client_likes_consensus(). Aliases the URL in the request passed to
718 * parse_consensus_request(). */
719 const char *want_fps;
720 /** Optionally, a smartlist of sha3 digests-as-signed of the consensuses
721 * to return a diff from. */
722 smartlist_t *diff_from_digests;
723 /** If true, never send a full consensus. If there is no diff, send
724 * a 404 instead. */
725 int diff_only;
726 } parsed_consensus_request_t;
728 /** Remove all data held in <b>req</b>. Do not free <b>req</b> itself, since
729 * it is stack-allocated. */
730 static void
731 parsed_consensus_request_clear(parsed_consensus_request_t *req)
733 if (!req)
734 return;
735 tor_free(req->flavor);
736 if (req->diff_from_digests) {
737 SMARTLIST_FOREACH(req->diff_from_digests, uint8_t *, d, tor_free(d));
738 smartlist_free(req->diff_from_digests);
740 memset(req, 0, sizeof(parsed_consensus_request_t));
744 * Parse the URL and relevant headers of <b>args</b> for a current-consensus
745 * request to learn what flavor of consensus we want, what keys it must be
746 * signed with, and what diffs we would accept (or demand) instead. Return 0
747 * on success and -1 on failure.
749 static int
750 parse_consensus_request(parsed_consensus_request_t *out,
751 const get_handler_args_t *args)
753 const char *url = args->url;
754 memset(out, 0, sizeof(parsed_consensus_request_t));
755 out->flav = FLAV_NS;
757 const char CONSENSUS_URL_PREFIX[] = "/tor/status-vote/current/consensus/";
758 const char CONSENSUS_FLAVORED_PREFIX[] =
759 "/tor/status-vote/current/consensus-";
761 /* figure out the flavor if any, and who we wanted to sign the thing */
762 const char *after_flavor = NULL;
764 if (!strcmpstart(url, CONSENSUS_FLAVORED_PREFIX)) {
765 const char *f, *cp;
766 f = url + strlen(CONSENSUS_FLAVORED_PREFIX);
767 cp = strchr(f, '/');
768 if (cp) {
769 after_flavor = cp+1;
770 out->flavor = tor_strndup(f, cp-f);
771 } else {
772 out->flavor = tor_strdup(f);
774 int flav = networkstatus_parse_flavor_name(out->flavor);
775 if (flav < 0)
776 flav = FLAV_NS;
777 out->flav = flav;
778 } else {
779 if (!strcmpstart(url, CONSENSUS_URL_PREFIX))
780 after_flavor = url+strlen(CONSENSUS_URL_PREFIX);
783 /* see whether we've been asked explicitly for a diff from an older
784 * consensus. (The user might also have said that a diff would be okay,
785 * via X-Or-Diff-From-Consensus */
786 const char DIFF_COMPONENT[] = "diff/";
787 char *diff_hash_in_url = NULL;
788 if (after_flavor && !strcmpstart(after_flavor, DIFF_COMPONENT)) {
789 after_flavor += strlen(DIFF_COMPONENT);
790 const char *cp = strchr(after_flavor, '/');
791 if (cp) {
792 diff_hash_in_url = tor_strndup(after_flavor, cp-after_flavor);
793 out->want_fps = cp+1;
794 } else {
795 diff_hash_in_url = tor_strdup(after_flavor);
796 out->want_fps = NULL;
798 } else {
799 out->want_fps = after_flavor;
802 if (diff_hash_in_url) {
803 uint8_t diff_from[DIGEST256_LEN];
804 out->diff_from_digests = smartlist_new();
805 out->diff_only = 1;
806 int ok = !parse_one_diff_hash(diff_from, diff_hash_in_url, "URL",
807 "rejecting");
808 tor_free(diff_hash_in_url);
809 if (ok) {
810 smartlist_add(out->diff_from_digests,
811 tor_memdup(diff_from, DIGEST256_LEN));
812 } else {
813 return -1;
815 } else {
816 parse_or_diff_from_header(&out->diff_from_digests, args->headers);
819 return 0;
822 /** Helper function for GET /tor/status-vote/current/consensus
824 static int
825 handle_get_current_consensus(dir_connection_t *conn,
826 const get_handler_args_t *args)
828 const compress_method_t compress_method =
829 find_best_compression_method(args->compression_supported, 0);
830 const time_t if_modified_since = args->if_modified_since;
831 int clear_spool = 0;
833 /* v3 network status fetch. */
834 long lifetime = NETWORKSTATUS_CACHE_LIFETIME;
836 time_t now = time(NULL);
837 parsed_consensus_request_t req;
839 if (parse_consensus_request(&req, args) < 0) {
840 write_short_http_response(conn, 404, "Couldn't parse request");
841 goto done;
844 if (digest_list_contains_best_consensus(req.flav,
845 req.diff_from_digests)) {
846 write_short_http_response(conn, 304, "Not modified");
847 geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED);
848 goto done;
851 struct consensus_cache_entry_t *cached_consensus = NULL;
853 compress_method_t compression_used = NO_METHOD;
854 if (req.diff_from_digests) {
855 cached_consensus = find_best_diff(req.diff_from_digests, req.flav,
856 args->compression_supported,
857 &compression_used);
860 if (req.diff_only && !cached_consensus) {
861 write_short_http_response(conn, 404, "No such diff available");
862 // XXXX warn_consensus_is_too_old(v, req.flavor, now);
863 geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
864 goto done;
867 if (! cached_consensus) {
868 cached_consensus = find_best_consensus(req.flav,
869 args->compression_supported,
870 &compression_used);
873 time_t fresh_until, valid_until;
874 int have_fresh_until = 0, have_valid_until = 0;
875 if (cached_consensus) {
876 have_fresh_until =
877 !consensus_cache_entry_get_fresh_until(cached_consensus, &fresh_until);
878 have_valid_until =
879 !consensus_cache_entry_get_valid_until(cached_consensus, &valid_until);
882 if (cached_consensus && have_valid_until &&
883 !networkstatus_valid_until_is_reasonably_live(valid_until, now)) {
884 write_short_http_response(conn, 404, "Consensus is too old");
885 warn_consensus_is_too_old(cached_consensus, req.flavor, now);
886 geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
887 goto done;
890 if (cached_consensus && req.want_fps &&
891 !client_likes_consensus(cached_consensus, req.want_fps)) {
892 write_short_http_response(conn, 404, "Consensus not signed by sufficient "
893 "number of requested authorities");
894 geoip_note_ns_response(GEOIP_REJECT_NOT_ENOUGH_SIGS);
895 goto done;
898 conn->spool = smartlist_new();
899 clear_spool = 1;
901 spooled_resource_t *spooled;
902 if (cached_consensus) {
903 spooled = spooled_resource_new_from_cache_entry(cached_consensus);
904 smartlist_add(conn->spool, spooled);
908 lifetime = (have_fresh_until && fresh_until > now) ? fresh_until - now : 0;
910 size_t size_guess = 0;
911 int n_expired = 0;
912 dirserv_spool_remove_missing_and_guess_size(conn, if_modified_since,
913 compress_method != NO_METHOD,
914 &size_guess,
915 &n_expired);
917 if (!smartlist_len(conn->spool) && !n_expired) {
918 write_short_http_response(conn, 404, "Not found");
919 geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
920 goto done;
921 } else if (!smartlist_len(conn->spool)) {
922 write_short_http_response(conn, 304, "Not modified");
923 geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED);
924 goto done;
927 if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
928 log_debug(LD_DIRSERV,
929 "Client asked for network status lists, but we've been "
930 "writing too many bytes lately. Sending 503 Dir busy.");
931 write_short_http_response(conn, 503, "Directory busy, try again later");
932 geoip_note_ns_response(GEOIP_REJECT_BUSY);
933 goto done;
936 tor_addr_t addr;
937 if (tor_addr_parse(&addr, (TO_CONN(conn))->address) >= 0) {
938 geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS,
939 &addr, NULL,
940 time(NULL));
941 geoip_note_ns_response(GEOIP_SUCCESS);
942 /* Note that a request for a network status has started, so that we
943 * can measure the download time later on. */
944 if (conn->dirreq_id)
945 geoip_start_dirreq(conn->dirreq_id, size_guess, DIRREQ_TUNNELED);
946 else
947 geoip_start_dirreq(TO_CONN(conn)->global_identifier, size_guess,
948 DIRREQ_DIRECT);
951 /* Use this header to tell caches that the response depends on the
952 * X-Or-Diff-From-Consensus header (or lack thereof). */
953 const char vary_header[] = "Vary: X-Or-Diff-From-Consensus\r\n";
955 clear_spool = 0;
957 // The compress_method might have been NO_METHOD, but we store the data
958 // compressed. Decompress them using `compression_used`. See fallback code in
959 // find_best_consensus() and find_best_diff().
960 write_http_response_headers(conn, -1,
961 compress_method == NO_METHOD ?
962 NO_METHOD : compression_used,
963 vary_header,
964 smartlist_len(conn->spool) == 1 ? lifetime : 0);
966 if (compress_method == NO_METHOD && smartlist_len(conn->spool))
967 conn->compress_state = tor_compress_new(0, compression_used,
968 HIGH_COMPRESSION);
970 /* Prime the connection with some data. */
971 const int initial_flush_result = connection_dirserv_flushed_some(conn);
972 tor_assert_nonfatal(initial_flush_result == 0);
973 goto done;
975 done:
976 parsed_consensus_request_clear(&req);
977 if (clear_spool) {
978 dir_conn_clear_spool(conn);
980 return 0;
983 /** Helper function for GET /tor/status-vote/{current,next}/...
985 static int
986 handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
988 const char *url = args->url;
990 ssize_t body_len = 0;
991 ssize_t estimated_len = 0;
992 int lifetime = 60; /* XXXX?? should actually use vote intervals. */
993 /* This smartlist holds strings that we can compress on the fly. */
994 smartlist_t *items = smartlist_new();
995 /* This smartlist holds cached_dir_t objects that have a precompressed
996 * deflated version. */
997 smartlist_t *dir_items = smartlist_new();
998 dirvote_dirreq_get_status_vote(url, items, dir_items);
999 if (!smartlist_len(dir_items) && !smartlist_len(items)) {
1000 write_short_http_response(conn, 404, "Not found");
1001 goto vote_done;
1004 /* We're sending items from at most one kind of source */
1005 tor_assert_nonfatal(smartlist_len(items) == 0 ||
1006 smartlist_len(dir_items) == 0);
1008 int streaming;
1009 unsigned mask;
1010 if (smartlist_len(items)) {
1011 /* We're taking strings and compressing them on the fly. */
1012 streaming = 1;
1013 mask = ~0u;
1014 } else {
1015 /* We're taking cached_dir_t objects. We only have them uncompressed
1016 * or deflated. */
1017 streaming = 0;
1018 mask = (1u<<NO_METHOD) | (1u<<ZLIB_METHOD);
1020 const compress_method_t compress_method = find_best_compression_method(
1021 args->compression_supported&mask, streaming);
1023 SMARTLIST_FOREACH(dir_items, cached_dir_t *, d,
1024 body_len += compress_method != NO_METHOD ?
1025 d->dir_compressed_len : d->dir_len);
1026 estimated_len += body_len;
1027 SMARTLIST_FOREACH(items, const char *, item, {
1028 size_t ln = strlen(item);
1029 if (compress_method != NO_METHOD) {
1030 estimated_len += ln/2;
1031 } else {
1032 body_len += ln; estimated_len += ln;
1036 if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
1037 write_short_http_response(conn, 503, "Directory busy, try again later");
1038 goto vote_done;
1040 write_http_response_header(conn, body_len ? body_len : -1,
1041 compress_method,
1042 lifetime);
1044 if (smartlist_len(items)) {
1045 if (compress_method != NO_METHOD) {
1046 conn->compress_state = tor_compress_new(1, compress_method,
1047 choose_compression_level(estimated_len));
1048 SMARTLIST_FOREACH(items, const char *, c,
1049 connection_buf_add_compress(c, strlen(c), conn, 0));
1050 connection_buf_add_compress("", 0, conn, 1);
1051 } else {
1052 SMARTLIST_FOREACH(items, const char *, c,
1053 connection_buf_add(c, strlen(c), TO_CONN(conn)));
1055 } else {
1056 SMARTLIST_FOREACH(dir_items, cached_dir_t *, d,
1057 connection_buf_add(compress_method != NO_METHOD ?
1058 d->dir_compressed : d->dir,
1059 compress_method != NO_METHOD ?
1060 d->dir_compressed_len : d->dir_len,
1061 TO_CONN(conn)));
1063 vote_done:
1064 smartlist_free(items);
1065 smartlist_free(dir_items);
1066 goto done;
1068 done:
1069 return 0;
1072 /** Helper function for GET /tor/micro/d/...
1074 static int
1075 handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args)
1077 const char *url = args->url;
1078 const compress_method_t compress_method =
1079 find_best_compression_method(args->compression_supported, 1);
1080 int clear_spool = 1;
1082 conn->spool = smartlist_new();
1084 dir_split_resource_into_spoolable(url+strlen("/tor/micro/d/"),
1085 DIR_SPOOL_MICRODESC,
1086 conn->spool, NULL,
1087 DSR_DIGEST256|DSR_BASE64|DSR_SORT_UNIQ);
1089 size_t size_guess = 0;
1090 dirserv_spool_remove_missing_and_guess_size(conn, 0,
1091 compress_method != NO_METHOD,
1092 &size_guess, NULL);
1093 if (smartlist_len(conn->spool) == 0) {
1094 write_short_http_response(conn, 404, "Not found");
1095 goto done;
1097 if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
1098 log_info(LD_DIRSERV,
1099 "Client asked for server descriptors, but we've been "
1100 "writing too many bytes lately. Sending 503 Dir busy.");
1101 write_short_http_response(conn, 503, "Directory busy, try again later");
1102 goto done;
1105 clear_spool = 0;
1106 write_http_response_header(conn, -1,
1107 compress_method,
1108 MICRODESC_CACHE_LIFETIME);
1110 if (compress_method != NO_METHOD)
1111 conn->compress_state = tor_compress_new(1, compress_method,
1112 choose_compression_level(size_guess));
1114 const int initial_flush_result = connection_dirserv_flushed_some(conn);
1115 tor_assert_nonfatal(initial_flush_result == 0);
1116 goto done;
1119 done:
1120 if (clear_spool) {
1121 dir_conn_clear_spool(conn);
1123 return 0;
1126 /** Helper function for GET /tor/{server,extra}/...
1128 static int
1129 handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args)
1131 const char *url = args->url;
1132 const compress_method_t compress_method =
1133 find_best_compression_method(args->compression_supported, 1);
1134 const or_options_t *options = get_options();
1135 int clear_spool = 1;
1136 if (!strcmpstart(url,"/tor/server/") ||
1137 (!options->BridgeAuthoritativeDir &&
1138 !options->BridgeRelay && !strcmpstart(url,"/tor/extra/"))) {
1139 int res;
1140 const char *msg = NULL;
1141 int cache_lifetime = 0;
1142 int is_extra = !strcmpstart(url,"/tor/extra/");
1143 url += is_extra ? strlen("/tor/extra/") : strlen("/tor/server/");
1144 dir_spool_source_t source;
1145 time_t publish_cutoff = 0;
1146 if (!strcmpstart(url, "d/")) {
1147 source =
1148 is_extra ? DIR_SPOOL_EXTRA_BY_DIGEST : DIR_SPOOL_SERVER_BY_DIGEST;
1149 } else {
1150 source =
1151 is_extra ? DIR_SPOOL_EXTRA_BY_FP : DIR_SPOOL_SERVER_BY_FP;
1152 /* We only want to apply a publish cutoff when we're requesting
1153 * resources by fingerprint. */
1154 publish_cutoff = time(NULL) - ROUTER_MAX_AGE_TO_PUBLISH;
1157 conn->spool = smartlist_new();
1158 res = dirserv_get_routerdesc_spool(conn->spool, url,
1159 source,
1160 connection_dir_is_encrypted(conn),
1161 &msg);
1163 if (!strcmpstart(url, "all")) {
1164 cache_lifetime = FULL_DIR_CACHE_LIFETIME;
1165 } else if (smartlist_len(conn->spool) == 1) {
1166 cache_lifetime = ROUTERDESC_BY_DIGEST_CACHE_LIFETIME;
1169 size_t size_guess = 0;
1170 int n_expired = 0;
1171 dirserv_spool_remove_missing_and_guess_size(conn, publish_cutoff,
1172 compress_method != NO_METHOD,
1173 &size_guess, &n_expired);
1175 /* If we are the bridge authority and the descriptor is a bridge
1176 * descriptor, remember that we served this descriptor for desc stats. */
1177 /* XXXX it's a bit of a kludge to have this here. */
1178 if (get_options()->BridgeAuthoritativeDir &&
1179 source == DIR_SPOOL_SERVER_BY_FP) {
1180 SMARTLIST_FOREACH_BEGIN(conn->spool, spooled_resource_t *, spooled) {
1181 const routerinfo_t *router =
1182 router_get_by_id_digest((const char *)spooled->digest);
1183 /* router can be NULL here when the bridge auth is asked for its own
1184 * descriptor. */
1185 if (router && router->purpose == ROUTER_PURPOSE_BRIDGE)
1186 rep_hist_note_desc_served(router->cache_info.identity_digest);
1187 } SMARTLIST_FOREACH_END(spooled);
1190 if (res < 0 || size_guess == 0 || smartlist_len(conn->spool) == 0) {
1191 if (msg == NULL)
1192 msg = "Not found";
1193 write_short_http_response(conn, 404, msg);
1194 } else {
1195 if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
1196 log_info(LD_DIRSERV,
1197 "Client asked for server descriptors, but we've been "
1198 "writing too many bytes lately. Sending 503 Dir busy.");
1199 write_short_http_response(conn, 503,
1200 "Directory busy, try again later");
1201 dir_conn_clear_spool(conn);
1202 goto done;
1204 write_http_response_header(conn, -1, compress_method, cache_lifetime);
1205 if (compress_method != NO_METHOD)
1206 conn->compress_state = tor_compress_new(1, compress_method,
1207 choose_compression_level(size_guess));
1208 clear_spool = 0;
1209 /* Prime the connection with some data. */
1210 int initial_flush_result = connection_dirserv_flushed_some(conn);
1211 tor_assert_nonfatal(initial_flush_result == 0);
1213 goto done;
1215 done:
1216 if (clear_spool)
1217 dir_conn_clear_spool(conn);
1218 return 0;
1221 /** Helper function for GET /tor/keys/...
1223 static int
1224 handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
1226 const char *url = args->url;
1227 const compress_method_t compress_method =
1228 find_best_compression_method(args->compression_supported, 1);
1229 const time_t if_modified_since = args->if_modified_since;
1231 smartlist_t *certs = smartlist_new();
1232 ssize_t len = -1;
1233 if (!strcmp(url, "/tor/keys/all")) {
1234 authority_cert_get_all(certs);
1235 } else if (!strcmp(url, "/tor/keys/authority")) {
1236 authority_cert_t *cert = get_my_v3_authority_cert();
1237 if (cert)
1238 smartlist_add(certs, cert);
1239 } else if (!strcmpstart(url, "/tor/keys/fp/")) {
1240 smartlist_t *fps = smartlist_new();
1241 dir_split_resource_into_fingerprints(url+strlen("/tor/keys/fp/"),
1242 fps, NULL,
1243 DSR_HEX|DSR_SORT_UNIQ);
1244 SMARTLIST_FOREACH(fps, char *, d, {
1245 authority_cert_t *c = authority_cert_get_newest_by_id(d);
1246 if (c) smartlist_add(certs, c);
1247 tor_free(d);
1249 smartlist_free(fps);
1250 } else if (!strcmpstart(url, "/tor/keys/sk/")) {
1251 smartlist_t *fps = smartlist_new();
1252 dir_split_resource_into_fingerprints(url+strlen("/tor/keys/sk/"),
1253 fps, NULL,
1254 DSR_HEX|DSR_SORT_UNIQ);
1255 SMARTLIST_FOREACH(fps, char *, d, {
1256 authority_cert_t *c = authority_cert_get_by_sk_digest(d);
1257 if (c) smartlist_add(certs, c);
1258 tor_free(d);
1260 smartlist_free(fps);
1261 } else if (!strcmpstart(url, "/tor/keys/fp-sk/")) {
1262 smartlist_t *fp_sks = smartlist_new();
1263 dir_split_resource_into_fingerprint_pairs(url+strlen("/tor/keys/fp-sk/"),
1264 fp_sks);
1265 SMARTLIST_FOREACH(fp_sks, fp_pair_t *, pair, {
1266 authority_cert_t *c = authority_cert_get_by_digests(pair->first,
1267 pair->second);
1268 if (c) smartlist_add(certs, c);
1269 tor_free(pair);
1271 smartlist_free(fp_sks);
1272 } else {
1273 write_short_http_response(conn, 400, "Bad request");
1274 goto keys_done;
1276 if (!smartlist_len(certs)) {
1277 write_short_http_response(conn, 404, "Not found");
1278 goto keys_done;
1280 SMARTLIST_FOREACH(certs, authority_cert_t *, c,
1281 if (c->cache_info.published_on < if_modified_since)
1282 SMARTLIST_DEL_CURRENT(certs, c));
1283 if (!smartlist_len(certs)) {
1284 write_short_http_response(conn, 304, "Not modified");
1285 goto keys_done;
1287 len = 0;
1288 SMARTLIST_FOREACH(certs, authority_cert_t *, c,
1289 len += c->cache_info.signed_descriptor_len);
1291 if (global_write_bucket_low(TO_CONN(conn),
1292 compress_method != NO_METHOD ? len/2 : len,
1293 2)) {
1294 write_short_http_response(conn, 503, "Directory busy, try again later");
1295 goto keys_done;
1298 write_http_response_header(conn,
1299 compress_method != NO_METHOD ? -1 : len,
1300 compress_method,
1301 60*60);
1302 if (compress_method != NO_METHOD) {
1303 conn->compress_state = tor_compress_new(1, compress_method,
1304 choose_compression_level(len));
1305 SMARTLIST_FOREACH(certs, authority_cert_t *, c,
1306 connection_buf_add_compress(
1307 c->cache_info.signed_descriptor_body,
1308 c->cache_info.signed_descriptor_len,
1309 conn, 0));
1310 connection_buf_add_compress("", 0, conn, 1);
1311 } else {
1312 SMARTLIST_FOREACH(certs, authority_cert_t *, c,
1313 connection_buf_add(c->cache_info.signed_descriptor_body,
1314 c->cache_info.signed_descriptor_len,
1315 TO_CONN(conn)));
1317 keys_done:
1318 smartlist_free(certs);
1319 goto done;
1321 done:
1322 return 0;
1325 /** Helper function for GET /tor/rendezvous2/
1327 static int
1328 handle_get_hs_descriptor_v2(dir_connection_t *conn,
1329 const get_handler_args_t *args)
1331 const char *url = args->url;
1332 if (connection_dir_is_encrypted(conn)) {
1333 /* Handle v2 rendezvous descriptor fetch request. */
1334 const char *descp;
1335 const char *query = url + strlen("/tor/rendezvous2/");
1336 if (rend_valid_descriptor_id(query)) {
1337 log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'",
1338 safe_str(escaped(query)));
1339 switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) {
1340 case 1: /* valid */
1341 write_http_response_header(conn, strlen(descp), NO_METHOD, 0);
1342 connection_buf_add(descp, strlen(descp), TO_CONN(conn));
1343 break;
1344 case 0: /* well-formed but not present */
1345 write_short_http_response(conn, 404, "Not found");
1346 break;
1347 case -1: /* not well-formed */
1348 write_short_http_response(conn, 400, "Bad request");
1349 break;
1351 } else { /* not well-formed */
1352 write_short_http_response(conn, 400, "Bad request");
1354 goto done;
1355 } else {
1356 /* Not encrypted! */
1357 write_short_http_response(conn, 404, "Not found");
1359 done:
1360 return 0;
1363 /** Helper function for GET /tor/hs/3/<z>. Only for version 3.
1365 STATIC int
1366 handle_get_hs_descriptor_v3(dir_connection_t *conn,
1367 const get_handler_args_t *args)
1369 int retval;
1370 const char *desc_str = NULL;
1371 const char *pubkey_str = NULL;
1372 const char *url = args->url;
1374 /* Reject unencrypted dir connections */
1375 if (!connection_dir_is_encrypted(conn)) {
1376 write_short_http_response(conn, 404, "Not found");
1377 goto done;
1380 /* After the path prefix follows the base64 encoded blinded pubkey which we
1381 * use to get the descriptor from the cache. Skip the prefix and get the
1382 * pubkey. */
1383 tor_assert(!strcmpstart(url, "/tor/hs/3/"));
1384 pubkey_str = url + strlen("/tor/hs/3/");
1385 retval = hs_cache_lookup_as_dir(HS_VERSION_THREE,
1386 pubkey_str, &desc_str);
1387 if (retval <= 0 || desc_str == NULL) {
1388 write_short_http_response(conn, 404, "Not found");
1389 goto done;
1392 /* Found requested descriptor! Pass it to this nice client. */
1393 write_http_response_header(conn, strlen(desc_str), NO_METHOD, 0);
1394 connection_buf_add(desc_str, strlen(desc_str), TO_CONN(conn));
1396 done:
1397 return 0;
1400 /** Helper function for GET /tor/networkstatus-bridges
1402 static int
1403 handle_get_networkstatus_bridges(dir_connection_t *conn,
1404 const get_handler_args_t *args)
1406 const char *headers = args->headers;
1408 const or_options_t *options = get_options();
1409 if (options->BridgeAuthoritativeDir &&
1410 options->BridgePassword_AuthDigest_ &&
1411 connection_dir_is_encrypted(conn)) {
1412 char *status;
1413 char digest[DIGEST256_LEN];
1415 char *header = http_get_header(headers, "Authorization: Basic ");
1416 if (header)
1417 crypto_digest256(digest, header, strlen(header), DIGEST_SHA256);
1419 /* now make sure the password is there and right */
1420 if (!header ||
1421 tor_memneq(digest,
1422 options->BridgePassword_AuthDigest_, DIGEST256_LEN)) {
1423 write_short_http_response(conn, 404, "Not found");
1424 tor_free(header);
1425 goto done;
1427 tor_free(header);
1429 /* all happy now. send an answer. */
1430 status = networkstatus_getinfo_by_purpose("bridge", time(NULL));
1431 size_t dlen = strlen(status);
1432 write_http_response_header(conn, dlen, NO_METHOD, 0);
1433 connection_buf_add(status, dlen, TO_CONN(conn));
1434 tor_free(status);
1435 goto done;
1437 done:
1438 return 0;
1441 /** Helper function for GET robots.txt or /tor/robots.txt */
1442 static int
1443 handle_get_robots(dir_connection_t *conn, const get_handler_args_t *args)
1445 (void)args;
1447 const char robots[] = "User-agent: *\r\nDisallow: /\r\n";
1448 size_t len = strlen(robots);
1449 write_http_response_header(conn, len, NO_METHOD, ROBOTS_CACHE_LIFETIME);
1450 connection_buf_add(robots, len, TO_CONN(conn));
1452 return 0;
1455 /* Given the <b>url</b> from a POST request, try to extract the version number
1456 * using the provided <b>prefix</b>. The version should be after the prefix and
1457 * ending with the separator "/". For instance:
1458 * /tor/hs/3/publish
1460 * On success, <b>end_pos</b> points to the position right after the version
1461 * was found. On error, it is set to NULL.
1463 * Return version on success else negative value. */
1464 STATIC int
1465 parse_hs_version_from_post(const char *url, const char *prefix,
1466 const char **end_pos)
1468 int ok;
1469 unsigned long version;
1470 const char *start;
1471 char *end = NULL;
1473 tor_assert(url);
1474 tor_assert(prefix);
1475 tor_assert(end_pos);
1477 /* Check if the prefix does start the url. */
1478 if (strcmpstart(url, prefix)) {
1479 goto err;
1481 /* Move pointer to the end of the prefix string. */
1482 start = url + strlen(prefix);
1483 /* Try this to be the HS version and if we are still at the separator, next
1484 * will be move to the right value. */
1485 version = tor_parse_long(start, 10, 0, INT_MAX, &ok, &end);
1486 if (!ok) {
1487 goto err;
1490 *end_pos = end;
1491 return (int) version;
1492 err:
1493 *end_pos = NULL;
1494 return -1;
1497 /* Handle the POST request for a hidden service descripror. The request is in
1498 * <b>url</b>, the body of the request is in <b>body</b>. Return 200 on success
1499 * else return 400 indicating a bad request. */
1500 STATIC int
1501 handle_post_hs_descriptor(const char *url, const char *body)
1503 int version;
1504 const char *end_pos;
1506 tor_assert(url);
1507 tor_assert(body);
1509 version = parse_hs_version_from_post(url, "/tor/hs/", &end_pos);
1510 if (version < 0) {
1511 goto err;
1514 /* We have a valid version number, now make sure it's a publish request. Use
1515 * the end position just after the version and check for the command. */
1516 if (strcmpstart(end_pos, "/publish")) {
1517 goto err;
1520 switch (version) {
1521 case HS_VERSION_THREE:
1522 if (hs_cache_store_as_dir(body) < 0) {
1523 goto err;
1525 log_info(LD_REND, "Publish request for HS descriptor handled "
1526 "successfully.");
1527 break;
1528 default:
1529 /* Unsupported version, return a bad request. */
1530 goto err;
1533 return 200;
1534 err:
1535 /* Bad request. */
1536 return 400;
1539 /** Helper function: called when a dirserver gets a complete HTTP POST
1540 * request. Look for an uploaded server descriptor or rendezvous
1541 * service descriptor. On finding one, process it and write a
1542 * response into conn-\>outbuf. If the request is unrecognized, send a
1543 * 400. Always return 0. */
1544 MOCK_IMPL(STATIC int,
1545 directory_handle_command_post,(dir_connection_t *conn, const char *headers,
1546 const char *body, size_t body_len))
1548 char *url = NULL;
1549 const or_options_t *options = get_options();
1551 log_debug(LD_DIRSERV,"Received POST command.");
1553 conn->base_.state = DIR_CONN_STATE_SERVER_WRITING;
1555 if (!public_server_mode(options)) {
1556 log_info(LD_DIR, "Rejected dir post request from %s "
1557 "since we're not a public relay.", conn->base_.address);
1558 write_short_http_response(conn, 503, "Not acting as a public relay");
1559 goto done;
1562 if (parse_http_url(headers, &url) < 0) {
1563 write_short_http_response(conn, 400, "Bad request");
1564 return 0;
1566 log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url));
1568 /* Handle v2 rendezvous service publish request. */
1569 if (connection_dir_is_encrypted(conn) &&
1570 !strcmpstart(url,"/tor/rendezvous2/publish")) {
1571 if (rend_cache_store_v2_desc_as_dir(body) < 0) {
1572 log_warn(LD_REND, "Rejected v2 rend descriptor (body size %d) from %s.",
1573 (int)body_len, conn->base_.address);
1574 write_short_http_response(conn, 400,
1575 "Invalid v2 service descriptor rejected");
1576 } else {
1577 write_short_http_response(conn, 200, "Service descriptor (v2) stored");
1578 log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted");
1580 goto done;
1583 /* Handle HS descriptor publish request. */
1584 /* XXX: This should be disabled with a consensus param until we want to
1585 * the prop224 be deployed and thus use. */
1586 if (connection_dir_is_encrypted(conn) && !strcmpstart(url, "/tor/hs/")) {
1587 const char *msg = "HS descriptor stored successfully.";
1589 /* We most probably have a publish request for an HS descriptor. */
1590 int code = handle_post_hs_descriptor(url, body);
1591 if (code != 200) {
1592 msg = "Invalid HS descriptor. Rejected.";
1594 write_short_http_response(conn, code, msg);
1595 goto done;
1598 if (!authdir_mode(options)) {
1599 /* we just provide cached directories; we don't want to
1600 * receive anything. */
1601 write_short_http_response(conn, 400, "Nonauthoritative directory does not "
1602 "accept posted server descriptors");
1603 goto done;
1606 if (authdir_mode(options) &&
1607 !strcmp(url,"/tor/")) { /* server descriptor post */
1608 const char *msg = "[None]";
1609 uint8_t purpose = authdir_mode_bridge(options) ?
1610 ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL;
1611 was_router_added_t r = dirserv_add_multiple_descriptors(body, purpose,
1612 conn->base_.address, &msg);
1613 tor_assert(msg);
1615 if (r == ROUTER_ADDED_SUCCESSFULLY) {
1616 write_short_http_response(conn, 200, msg);
1617 } else if (WRA_WAS_OUTDATED(r)) {
1618 write_http_response_header_impl(conn, -1, NULL, NULL,
1619 "X-Descriptor-Not-New: Yes\r\n", -1);
1620 } else {
1621 log_info(LD_DIRSERV,
1622 "Rejected router descriptor or extra-info from %s "
1623 "(\"%s\").",
1624 conn->base_.address, msg);
1625 write_short_http_response(conn, 400, msg);
1627 goto done;
1630 if (authdir_mode_v3(options) &&
1631 !strcmp(url,"/tor/post/vote")) { /* v3 networkstatus vote */
1632 const char *msg = "OK";
1633 int status;
1634 if (dirvote_add_vote(body, &msg, &status)) {
1635 write_short_http_response(conn, status, "Vote stored");
1636 } else {
1637 tor_assert(msg);
1638 log_warn(LD_DIRSERV, "Rejected vote from %s (\"%s\").",
1639 conn->base_.address, msg);
1640 write_short_http_response(conn, status, msg);
1642 goto done;
1645 if (authdir_mode_v3(options) &&
1646 !strcmp(url,"/tor/post/consensus-signature")) { /* sigs on consensus. */
1647 const char *msg = NULL;
1648 if (dirvote_add_signatures(body, conn->base_.address, &msg)>=0) {
1649 write_short_http_response(conn, 200, msg?msg:"Signatures stored");
1650 } else {
1651 log_warn(LD_DIR, "Unable to store signatures posted by %s: %s",
1652 conn->base_.address, msg?msg:"???");
1653 write_short_http_response(conn, 400,
1654 msg?msg:"Unable to store signatures");
1656 goto done;
1659 /* we didn't recognize the url */
1660 write_short_http_response(conn, 404, "Not found");
1662 done:
1663 tor_free(url);
1664 return 0;
1667 /** If <b>headers</b> indicates that a proxy was involved, then rewrite
1668 * <b>conn</b>-\>address to describe our best guess of the address that
1669 * originated this HTTP request. */
1670 static void
1671 http_set_address_origin(const char *headers, connection_t *conn)
1673 char *fwd;
1675 fwd = http_get_header(headers, "Forwarded-For: ");
1676 if (!fwd)
1677 fwd = http_get_header(headers, "X-Forwarded-For: ");
1678 if (fwd) {
1679 tor_addr_t toraddr;
1680 if (tor_addr_parse(&toraddr,fwd) == -1 ||
1681 tor_addr_is_internal(&toraddr,0)) {
1682 log_debug(LD_DIR, "Ignoring local/internal IP %s", escaped(fwd));
1683 tor_free(fwd);
1684 return;
1687 tor_free(conn->address);
1688 conn->address = tor_strdup(fwd);
1689 tor_free(fwd);
1693 /** Called when a dirserver receives data on a directory connection;
1694 * looks for an HTTP request. If the request is complete, remove it
1695 * from the inbuf, try to process it; otherwise, leave it on the
1696 * buffer. Return a 0 on success, or -1 on error.
1699 directory_handle_command(dir_connection_t *conn)
1701 char *headers=NULL, *body=NULL;
1702 size_t body_len=0;
1703 int r;
1705 tor_assert(conn);
1706 tor_assert(conn->base_.type == CONN_TYPE_DIR);
1708 switch (connection_fetch_from_buf_http(TO_CONN(conn),
1709 &headers, MAX_HEADERS_SIZE,
1710 &body, &body_len, MAX_DIR_UL_SIZE, 0)) {
1711 case -1: /* overflow */
1712 log_warn(LD_DIRSERV,
1713 "Request too large from address '%s' to DirPort. Closing.",
1714 safe_str(conn->base_.address));
1715 return -1;
1716 case 0:
1717 log_debug(LD_DIRSERV,"command not all here yet.");
1718 return 0;
1719 /* case 1, fall through */
1722 http_set_address_origin(headers, TO_CONN(conn));
1723 // we should escape headers here as well,
1724 // but we can't call escaped() twice, as it uses the same buffer
1725 //log_debug(LD_DIRSERV,"headers %s, body %s.", headers, escaped(body));
1727 if (!strncasecmp(headers,"GET",3))
1728 r = directory_handle_command_get(conn, headers, body, body_len);
1729 else if (!strncasecmp(headers,"POST",4))
1730 r = directory_handle_command_post(conn, headers, body, body_len);
1731 else {
1732 log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
1733 "Got headers %s with unknown command. Closing.",
1734 escaped(headers));
1735 r = -1;
1738 tor_free(headers); tor_free(body);
1739 return r;