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
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
56 * "\%s [http[s]://]\%s HTTP/1..."
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.
64 parse_http_url(const char *headers
, char **url
)
67 if (parse_http_command(headers
, &command
, url
) < 0) {
70 if (strcmpstart(*url
, "/tor/")) {
72 tor_asprintf(&new_url
, "/tor%s%s",
73 *url
[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>.
86 write_short_http_response(dir_connection_t
*conn
, int status
,
87 const char *reason_phrase
)
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
);
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. */
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
,
126 char date
[RFC1123_TIME_LEN
+1];
127 time_t now
= approx_time();
128 buf_t
*buf
= buf_new_with_capacity(1024);
132 format_rfc1123_time(date
, now
);
134 buf_add_printf(buf
, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date
);
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
);
145 buf_add_printf(buf
, "Content-Encoding: %s\r\n", encoding
);
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
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
159 buf_add_string(buf
, "Pragma: no-cache\r\n");
162 buf_add_string(buf
, extra_headers
);
164 buf_add_string(buf
, "\r\n");
166 connection_buf_add_buf(TO_CONN(conn
), buf
);
170 /** As write_http_response_header_impl, but translates method into
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
,
179 compression_method_get_name(method
),
184 /** As write_http_response_headers, but assumes extra_headers is NULL */
186 write_http_response_header(dir_connection_t
*conn
, ssize_t length
,
187 compress_method_t method
,
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
[] = {
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
[] = {
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 << x is set in the bitfield. */
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
) {
232 } SMARTLIST_FOREACH_END(m
);
233 smartlist_free(methods
);
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.
252 client_likes_consensus(const struct consensus_cache_entry_t
*ent
,
253 const char *want_url
)
255 smartlist_t
*voters
= smartlist_new();
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
)) {
275 } SMARTLIST_FOREACH_END(digest
);
277 /* early exit, if we already have enough */
278 if (have
>= need_at_least
)
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
;
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
311 unsigned compression_supported
;
312 /** If nonzero, the time included an if-modified-since header with this
314 time_t if_modified_since
;
315 /** String containing the requested URL or resource. */
317 /** String containing the HTTP 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
{
334 int (*handler
)(dir_connection_t
*conn
, const get_handler_args_t
*args
);
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
},
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
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. */
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");
403 if ((header
= http_get_header(headers
, "If-Modified-Since: "))) {
405 if (parse_http_time(header
, &tm
) == 0) {
406 if (tor_timegm(&tm
, &if_modified_since
)<0) {
407 if_modified_since
= 0;
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. */
416 log_debug(LD_DIRSERV
,"rewritten url as '%s'.", escaped(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
);
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
;
443 args
.headers
= headers
;
444 args
.if_modified_since
= if_modified_since
;
445 args
.compression_supported
= compression_methods_supported
;
448 for (i
= 0; url_table
[i
].string
; ++i
) {
450 if (url_table
[i
].is_prefix
) {
451 match
= !strcmpstart(url
, url_table
[i
].string
);
453 match
= !strcmp(url
, url_table
[i
].string
);
456 result
= url_table
[i
].handler(conn
, &args
);
461 /* we didn't recognize the url */
462 write_short_http_response(conn
, 404, "Not found");
470 /** Helper function for GET / or GET /tor/
473 handle_get_frontpage(dir_connection_t
*conn
, const get_handler_args_t
*args
)
475 (void) args
; /* unused */
476 const char *frontpage
= get_dirportfrontpage();
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
));
490 write_short_http_response(conn
, 404, "Not found");
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.
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
),
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;
516 if (consensus_cache_entry_get_valid_after(consensus
, &valid_time
))
518 dupes
= rate_limit_log(&warned
[1], now
);
520 if (consensus_cache_entry_get_valid_until(consensus
, &valid_time
))
522 dupes
= rate_limit_log(&warned
[0], now
);
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
: "",
532 is_too_new
? "new" : "old",
533 is_too_new
? "after" : "until",
535 is_too_new
? "before it was valid" : "after it expired",
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
548 parse_one_diff_hash(uint8_t *digest
, const char *hex
, const char *location
,
551 if (base16_decode((char*)digest
, DIGEST256_LEN
, hex
, strlen(hex
)) ==
555 log_fn(LOG_PROTOCOL_WARN
, LD_DIR
,
556 "%s contained bogus digest %s; %s.",
557 location
, escaped(hex
), action
);
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
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
);
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",
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
);
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
) {
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
;
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
;
634 } SMARTLIST_FOREACH_END(diff_from
);
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
;
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
)))
657 if (consdiffmgr_find_consensus(&result
, flav
,
658 method
) == CONSDIFF_AVAILABLE
) {
659 tor_assert_nonfatal(result
);
660 *compression_used_out
= method
;
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
;
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
)
682 compress_method_t
*methods
;
686 methods
= srv_meth_pref_streaming_compression
;
687 length
= ARRAY_LENGTH(srv_meth_pref_streaming_compression
);
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
))
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. */
705 digest_list_contains_best_consensus(consensus_flavor_t flavor
,
706 const smartlist_t
*digests
)
708 const networkstatus_t
*ns
= NULL
;
713 ns
= networkstatus_get_latest_consensus_by_flavor(flavor
);
718 SMARTLIST_FOREACH_BEGIN(digests
, const uint8_t *, digest
) {
719 if (tor_memeq(ns
->digest_sha3_as_signed
, digest
, DIGEST256_LEN
))
721 } SMARTLIST_FOREACH_END(digest
);
726 /** Encodes the results of parsing a consensus request to figure out what
727 * consensus, and possibly what diffs, the user asked for. */
729 /** name of the flavor to retrieve. */
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
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. */
748 parsed_consensus_request_clear(parsed_consensus_request_t
*req
)
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.
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
));
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
)) {
783 f
= url
+ strlen(CONSENSUS_FLAVORED_PREFIX
);
787 out
->flavor
= tor_strndup(f
, cp
-f
);
789 out
->flavor
= tor_strdup(f
);
791 int flav
= networkstatus_parse_flavor_name(out
->flavor
);
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
, '/');
809 diff_hash_in_url
= tor_strndup(after_flavor
, cp
-after_flavor
);
810 out
->want_fps
= cp
+1;
812 diff_hash_in_url
= tor_strdup(after_flavor
);
813 out
->want_fps
= NULL
;
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();
823 int ok
= !parse_one_diff_hash(diff_from
, diff_hash_in_url
, "URL",
825 tor_free(diff_hash_in_url
);
827 smartlist_add(out
->diff_from_digests
,
828 tor_memdup(diff_from
, DIGEST256_LEN
));
833 parse_or_diff_from_header(&out
->diff_from_digests
, args
->headers
);
839 /** Helper function for GET /tor/status-vote/current/consensus
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
;
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");
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
);
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
,
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
);
883 if (! cached_consensus
) {
884 cached_consensus
= find_best_consensus(req
.flav
,
885 args
->compression_supported
,
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
) {
893 !consensus_cache_entry_get_valid_after(cached_consensus
, &valid_after
);
895 !consensus_cache_entry_get_fresh_until(cached_consensus
, &fresh_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
);
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
);
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
);
925 conn
->spool
= smartlist_new();
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;
939 dirserv_spool_remove_missing_and_guess_size(conn
, if_modified_since
,
940 compress_method
!= NO_METHOD
,
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
);
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
);
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
);
964 if (tor_addr_parse(&addr
, (TO_CONN(conn
))->address
) >= 0) {
965 geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS
,
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. */
972 geoip_start_dirreq(conn
->dirreq_id
, size_guess
, DIRREQ_TUNNELED
);
974 geoip_start_dirreq(TO_CONN(conn
)->global_identifier
, size_guess
,
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";
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
,
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
,
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);
1003 parsed_consensus_request_clear(&req
);
1005 dir_conn_clear_spool(conn
);
1010 /** Helper function for GET /tor/status-vote/{current,next}/...
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");
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);
1037 if (smartlist_len(items
)) {
1038 /* We're taking strings and compressing them on the fly. */
1042 /* We're taking cached_dir_t objects. We only have them uncompressed
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;
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");
1067 write_http_response_header(conn
, body_len
? body_len
: -1,
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);
1079 SMARTLIST_FOREACH(items
, const char *, c
,
1080 connection_buf_add(c
, strlen(c
), TO_CONN(conn
)));
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
,
1091 smartlist_free(items
);
1092 smartlist_free(dir_items
);
1099 /** Helper function for GET /tor/micro/d/...
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
,
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
,
1120 if (smartlist_len(conn
->spool
) == 0) {
1121 write_short_http_response(conn
, 404, "Not found");
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");
1133 write_http_response_header(conn
, -1,
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);
1148 dir_conn_clear_spool(conn
);
1153 /** Helper function for GET /tor/{server,extra}/...
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/"))) {
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/")) {
1175 is_extra
? DIR_SPOOL_EXTRA_BY_DIGEST
: DIR_SPOOL_SERVER_BY_DIGEST
;
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
,
1187 connection_dir_is_encrypted(conn
),
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;
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
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) {
1220 write_short_http_response(conn
, 404, msg
);
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
);
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
));
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);
1244 dir_conn_clear_spool(conn
);
1248 /** Helper function for GET /tor/keys/...
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();
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();
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/"),
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
);
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/"),
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
);
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/"),
1292 SMARTLIST_FOREACH(fp_sks
, fp_pair_t
*, pair
, {
1293 authority_cert_t
*c
= authority_cert_get_by_digests(pair
->first
,
1295 if (c
) smartlist_add(certs
, c
);
1298 smartlist_free(fp_sks
);
1300 write_short_http_response(conn
, 400, "Bad request");
1303 if (!smartlist_len(certs
)) {
1304 write_short_http_response(conn
, 404, "Not found");
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");
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
,
1321 write_short_http_response(conn
, 503, "Directory busy, try again later");
1325 write_http_response_header(conn
,
1326 compress_method
!= NO_METHOD
? -1 : len
,
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
,
1337 connection_buf_add_compress("", 0, conn
, 1);
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
,
1345 smartlist_free(certs
);
1352 /** Helper function for GET /tor/rendezvous2/
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. */
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
)) {
1368 write_http_response_header(conn
, strlen(descp
), NO_METHOD
, 0);
1369 connection_buf_add(descp
, strlen(descp
), TO_CONN(conn
));
1371 case 0: /* well-formed but not present */
1372 write_short_http_response(conn
, 404, "Not found");
1374 case -1: /* not well-formed */
1375 write_short_http_response(conn
, 400, "Bad request");
1378 } else { /* not well-formed */
1379 write_short_http_response(conn
, 400, "Bad request");
1383 /* Not encrypted! */
1384 write_short_http_response(conn
, 404, "Not found");
1390 /** Helper function for GET /tor/hs/3/<z>. Only for version 3.
1393 handle_get_hs_descriptor_v3(dir_connection_t
*conn
,
1394 const get_handler_args_t
*args
)
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");
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
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");
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
));
1427 /** Helper function for GET /tor/networkstatus-bridges
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
)) {
1440 char digest
[DIGEST256_LEN
];
1442 char *header
= http_get_header(headers
, "Authorization: Basic ");
1444 crypto_digest256(digest
, header
, strlen(header
), DIGEST_SHA256
);
1446 /* now make sure the password is there and right */
1449 options
->BridgePassword_AuthDigest_
, DIGEST256_LEN
)) {
1450 write_short_http_response(conn
, 404, "Not found");
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
));
1468 /** Helper function for GET the bandwidth file used for the next vote */
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);
1493 log_debug(LD_DIR
, "Not compressing bandwidth file.");
1494 connection_buf_add(bandwidth
, len
, TO_CONN(conn
));
1496 tor_free(bandwidth
);
1500 write_short_http_response(conn
, 404, "Not found");
1504 /** Helper function for GET robots.txt or /tor/robots.txt */
1506 handle_get_robots(dir_connection_t
*conn
, const get_handler_args_t
*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
));
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:
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. */
1528 parse_hs_version_from_post(const char *url
, const char *prefix
,
1529 const char **end_pos
)
1532 unsigned long version
;
1538 tor_assert(end_pos
);
1540 /* Check if the prefix does start the url. */
1541 if (strcmpstart(url
, prefix
)) {
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
);
1554 return (int) version
;
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. */
1564 handle_post_hs_descriptor(const char *url
, const char *body
)
1567 const char *end_pos
;
1572 version
= parse_hs_version_from_post(url
, "/tor/hs/", &end_pos
);
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")) {
1584 case HS_VERSION_THREE
:
1585 if (hs_cache_store_as_dir(body
) < 0) {
1588 log_info(LD_REND
, "Publish request for HS descriptor handled "
1592 /* Unsupported version, return a bad request. */
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
))
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");
1625 if (parse_http_url(headers
, &url
) < 0) {
1626 write_short_http_response(conn
, 400, "Bad request");
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");
1640 write_short_http_response(conn
, 200, "Service descriptor (v2) stored");
1641 log_info(LD_REND
, "Handled v2 rendezvous descriptor post: accepted");
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
);
1655 msg
= "Invalid HS descriptor. Rejected.";
1657 write_short_http_response(conn
, code
, msg
);
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");
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
);
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);
1684 log_info(LD_DIRSERV
,
1685 "Rejected router descriptor or extra-info from %s "
1687 conn
->base_
.address
, msg
);
1688 write_short_http_response(conn
, 400, msg
);
1693 if (authdir_mode_v3(options
) &&
1694 !strcmp(url
,"/tor/post/vote")) { /* v3 networkstatus vote */
1695 const char *msg
= "OK";
1697 if (dirvote_add_vote(body
, &msg
, &status
)) {
1698 write_short_http_response(conn
, status
, "Vote stored");
1701 log_warn(LD_DIRSERV
, "Rejected vote from %s (\"%s\").",
1702 conn
->base_
.address
, msg
);
1703 write_short_http_response(conn
, status
, msg
);
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");
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");
1722 /* we didn't recognize the url */
1723 write_short_http_response(conn
, 404, "Not found");
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. */
1734 http_set_address_origin(const char *headers
, connection_t
*conn
)
1738 fwd
= http_get_header(headers
, "Forwarded-For: ");
1740 fwd
= http_get_header(headers
, "X-Forwarded-For: ");
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
));
1750 tor_free(conn
->address
);
1751 conn
->address
= tor_strdup(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
;
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
));
1780 log_debug(LD_DIRSERV
,"command not all here yet.");
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
);
1795 log_fn(LOG_PROTOCOL_WARN
, LD_PROTOCOL
,
1796 "Got headers %s with unknown command. Closing.",
1801 tor_free(headers
); tor_free(body
);