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
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
55 * "\%s [http[s]://]\%s HTTP/1..."
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.
63 parse_http_url(const char *headers
, char **url
)
66 if (parse_http_command(headers
, &command
, url
) < 0) {
69 if (strcmpstart(*url
, "/tor/")) {
71 tor_asprintf(&new_url
, "/tor%s%s",
72 *url
[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>.
85 write_short_http_response(dir_connection_t
*conn
, int status
,
86 const char *reason_phrase
)
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
);
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. */
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
,
125 char date
[RFC1123_TIME_LEN
+1];
126 time_t now
= time(NULL
);
127 buf_t
*buf
= buf_new_with_capacity(1024);
131 format_rfc1123_time(date
, now
);
133 buf_add_printf(buf
, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date
);
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
);
144 buf_add_printf(buf
, "Content-Encoding: %s\r\n", encoding
);
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
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
158 buf_add_string(buf
, "Pragma: no-cache\r\n");
161 buf_add_string(buf
, extra_headers
);
163 buf_add_string(buf
, "\r\n");
165 connection_buf_add_buf(TO_CONN(conn
), 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. */
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
);
178 if (method
== NO_METHOD
)
179 doctype
= "text/plain";
181 doctype
= "application/octet-stream";
182 write_http_response_header_impl(conn
, length
,
189 /** As write_http_response_headers, but assumes extra_headers is NULL */
191 write_http_response_header(dir_connection_t
*conn
, ssize_t length
,
192 compress_method_t method
,
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
[] = {
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
[] = {
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 << x is set in the bitfield. */
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
) {
237 } SMARTLIST_FOREACH_END(m
);
238 smartlist_free(methods
);
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.
257 client_likes_consensus(const struct consensus_cache_entry_t
*ent
,
258 const char *want_url
)
260 smartlist_t
*voters
= smartlist_new();
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
)) {
280 } SMARTLIST_FOREACH_END(digest
);
282 /* early exit, if we already have enough */
283 if (have
>= need_at_least
)
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
;
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
316 unsigned compression_supported
;
317 /** If nonzero, the time included an if-modified-since header with this
319 time_t if_modified_since
;
320 /** String containing the requested URL or resource. */
322 /** String containing the HTTP 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
{
339 int (*handler
)(dir_connection_t
*conn
, const get_handler_args_t
*args
);
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
},
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
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. */
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");
405 if ((header
= http_get_header(headers
, "If-Modified-Since: "))) {
407 if (parse_http_time(header
, &tm
) == 0) {
408 if (tor_timegm(&tm
, &if_modified_since
)<0) {
409 if_modified_since
= 0;
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. */
418 log_debug(LD_DIRSERV
,"rewritten url as '%s'.", escaped(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
);
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
;
445 args
.headers
= headers
;
446 args
.if_modified_since
= if_modified_since
;
447 args
.compression_supported
= compression_methods_supported
;
450 for (i
= 0; url_table
[i
].string
; ++i
) {
452 if (url_table
[i
].is_prefix
) {
453 match
= !strcmpstart(url
, url_table
[i
].string
);
455 match
= !strcmp(url
, url_table
[i
].string
);
458 result
= url_table
[i
].handler(conn
, &args
);
463 /* we didn't recognize the url */
464 write_short_http_response(conn
, 404, "Not found");
472 /** Helper function for GET / or GET /tor/
475 handle_get_frontpage(dir_connection_t
*conn
, const get_handler_args_t
*args
)
477 (void) args
; /* unused */
478 const char *frontpage
= get_dirportfrontpage();
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
));
492 write_short_http_response(conn
, 404, "Not found");
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.
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];
511 if (consensus_cache_entry_get_valid_until(consensus
, &valid_until
))
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
);
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
531 parse_one_diff_hash(uint8_t *digest
, const char *hex
, const char *location
,
534 if (base16_decode((char*)digest
, DIGEST256_LEN
, hex
, strlen(hex
)) ==
538 log_fn(LOG_PROTOCOL_WARN
, LD_DIR
,
539 "%s contained bogus digest %s; %s.",
540 location
, escaped(hex
), action
);
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
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
);
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",
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
);
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
) {
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
;
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
;
617 } SMARTLIST_FOREACH_END(diff_from
);
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
;
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
)))
640 if (consdiffmgr_find_consensus(&result
, flav
,
641 method
) == CONSDIFF_AVAILABLE
) {
642 tor_assert_nonfatal(result
);
643 *compression_used_out
= method
;
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
;
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
)
665 compress_method_t
*methods
;
669 methods
= srv_meth_pref_streaming_compression
;
670 length
= ARRAY_LENGTH(srv_meth_pref_streaming_compression
);
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
))
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. */
688 digest_list_contains_best_consensus(consensus_flavor_t flavor
,
689 const smartlist_t
*digests
)
691 const networkstatus_t
*ns
= NULL
;
696 ns
= networkstatus_get_latest_consensus_by_flavor(flavor
);
701 SMARTLIST_FOREACH_BEGIN(digests
, const uint8_t *, digest
) {
702 if (tor_memeq(ns
->digest_sha3_as_signed
, digest
, DIGEST256_LEN
))
704 } SMARTLIST_FOREACH_END(digest
);
709 /** Encodes the results of parsing a consensus request to figure out what
710 * consensus, and possibly what diffs, the user asked for. */
712 /** name of the flavor to retrieve. */
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
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. */
731 parsed_consensus_request_clear(parsed_consensus_request_t
*req
)
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.
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
));
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
)) {
766 f
= url
+ strlen(CONSENSUS_FLAVORED_PREFIX
);
770 out
->flavor
= tor_strndup(f
, cp
-f
);
772 out
->flavor
= tor_strdup(f
);
774 int flav
= networkstatus_parse_flavor_name(out
->flavor
);
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
, '/');
792 diff_hash_in_url
= tor_strndup(after_flavor
, cp
-after_flavor
);
793 out
->want_fps
= cp
+1;
795 diff_hash_in_url
= tor_strdup(after_flavor
);
796 out
->want_fps
= NULL
;
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();
806 int ok
= !parse_one_diff_hash(diff_from
, diff_hash_in_url
, "URL",
808 tor_free(diff_hash_in_url
);
810 smartlist_add(out
->diff_from_digests
,
811 tor_memdup(diff_from
, DIGEST256_LEN
));
816 parse_or_diff_from_header(&out
->diff_from_digests
, args
->headers
);
822 /** Helper function for GET /tor/status-vote/current/consensus
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
;
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");
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
);
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
,
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
);
867 if (! cached_consensus
) {
868 cached_consensus
= find_best_consensus(req
.flav
,
869 args
->compression_supported
,
873 time_t fresh_until
, valid_until
;
874 int have_fresh_until
= 0, have_valid_until
= 0;
875 if (cached_consensus
) {
877 !consensus_cache_entry_get_fresh_until(cached_consensus
, &fresh_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
);
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
);
898 conn
->spool
= smartlist_new();
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;
912 dirserv_spool_remove_missing_and_guess_size(conn
, if_modified_since
,
913 compress_method
!= NO_METHOD
,
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
);
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
);
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
);
937 if (tor_addr_parse(&addr
, (TO_CONN(conn
))->address
) >= 0) {
938 geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS
,
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. */
945 geoip_start_dirreq(conn
->dirreq_id
, size_guess
, DIRREQ_TUNNELED
);
947 geoip_start_dirreq(TO_CONN(conn
)->global_identifier
, size_guess
,
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";
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
,
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
,
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);
976 parsed_consensus_request_clear(&req
);
978 dir_conn_clear_spool(conn
);
983 /** Helper function for GET /tor/status-vote/{current,next}/...
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");
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);
1010 if (smartlist_len(items
)) {
1011 /* We're taking strings and compressing them on the fly. */
1015 /* We're taking cached_dir_t objects. We only have them uncompressed
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;
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");
1040 write_http_response_header(conn
, body_len
? body_len
: -1,
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);
1052 SMARTLIST_FOREACH(items
, const char *, c
,
1053 connection_buf_add(c
, strlen(c
), TO_CONN(conn
)));
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
,
1064 smartlist_free(items
);
1065 smartlist_free(dir_items
);
1072 /** Helper function for GET /tor/micro/d/...
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
,
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
,
1093 if (smartlist_len(conn
->spool
) == 0) {
1094 write_short_http_response(conn
, 404, "Not found");
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");
1106 write_http_response_header(conn
, -1,
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);
1121 dir_conn_clear_spool(conn
);
1126 /** Helper function for GET /tor/{server,extra}/...
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/"))) {
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/")) {
1148 is_extra
? DIR_SPOOL_EXTRA_BY_DIGEST
: DIR_SPOOL_SERVER_BY_DIGEST
;
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
,
1160 connection_dir_is_encrypted(conn
),
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;
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
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) {
1193 write_short_http_response(conn
, 404, msg
);
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
);
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
));
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);
1217 dir_conn_clear_spool(conn
);
1221 /** Helper function for GET /tor/keys/...
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();
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();
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/"),
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
);
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/"),
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
);
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/"),
1265 SMARTLIST_FOREACH(fp_sks
, fp_pair_t
*, pair
, {
1266 authority_cert_t
*c
= authority_cert_get_by_digests(pair
->first
,
1268 if (c
) smartlist_add(certs
, c
);
1271 smartlist_free(fp_sks
);
1273 write_short_http_response(conn
, 400, "Bad request");
1276 if (!smartlist_len(certs
)) {
1277 write_short_http_response(conn
, 404, "Not found");
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");
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
,
1294 write_short_http_response(conn
, 503, "Directory busy, try again later");
1298 write_http_response_header(conn
,
1299 compress_method
!= NO_METHOD
? -1 : len
,
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
,
1310 connection_buf_add_compress("", 0, conn
, 1);
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
,
1318 smartlist_free(certs
);
1325 /** Helper function for GET /tor/rendezvous2/
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. */
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
)) {
1341 write_http_response_header(conn
, strlen(descp
), NO_METHOD
, 0);
1342 connection_buf_add(descp
, strlen(descp
), TO_CONN(conn
));
1344 case 0: /* well-formed but not present */
1345 write_short_http_response(conn
, 404, "Not found");
1347 case -1: /* not well-formed */
1348 write_short_http_response(conn
, 400, "Bad request");
1351 } else { /* not well-formed */
1352 write_short_http_response(conn
, 400, "Bad request");
1356 /* Not encrypted! */
1357 write_short_http_response(conn
, 404, "Not found");
1363 /** Helper function for GET /tor/hs/3/<z>. Only for version 3.
1366 handle_get_hs_descriptor_v3(dir_connection_t
*conn
,
1367 const get_handler_args_t
*args
)
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");
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
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");
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
));
1400 /** Helper function for GET /tor/networkstatus-bridges
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
)) {
1413 char digest
[DIGEST256_LEN
];
1415 char *header
= http_get_header(headers
, "Authorization: Basic ");
1417 crypto_digest256(digest
, header
, strlen(header
), DIGEST_SHA256
);
1419 /* now make sure the password is there and right */
1422 options
->BridgePassword_AuthDigest_
, DIGEST256_LEN
)) {
1423 write_short_http_response(conn
, 404, "Not found");
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
));
1441 /** Helper function for GET robots.txt or /tor/robots.txt */
1443 handle_get_robots(dir_connection_t
*conn
, const get_handler_args_t
*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
));
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:
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. */
1465 parse_hs_version_from_post(const char *url
, const char *prefix
,
1466 const char **end_pos
)
1469 unsigned long version
;
1475 tor_assert(end_pos
);
1477 /* Check if the prefix does start the url. */
1478 if (strcmpstart(url
, prefix
)) {
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
);
1491 return (int) version
;
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. */
1501 handle_post_hs_descriptor(const char *url
, const char *body
)
1504 const char *end_pos
;
1509 version
= parse_hs_version_from_post(url
, "/tor/hs/", &end_pos
);
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")) {
1521 case HS_VERSION_THREE
:
1522 if (hs_cache_store_as_dir(body
) < 0) {
1525 log_info(LD_REND
, "Publish request for HS descriptor handled "
1529 /* Unsupported version, return a bad request. */
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
))
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");
1562 if (parse_http_url(headers
, &url
) < 0) {
1563 write_short_http_response(conn
, 400, "Bad request");
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");
1577 write_short_http_response(conn
, 200, "Service descriptor (v2) stored");
1578 log_info(LD_REND
, "Handled v2 rendezvous descriptor post: accepted");
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
);
1592 msg
= "Invalid HS descriptor. Rejected.";
1594 write_short_http_response(conn
, code
, msg
);
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");
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
);
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);
1621 log_info(LD_DIRSERV
,
1622 "Rejected router descriptor or extra-info from %s "
1624 conn
->base_
.address
, msg
);
1625 write_short_http_response(conn
, 400, msg
);
1630 if (authdir_mode_v3(options
) &&
1631 !strcmp(url
,"/tor/post/vote")) { /* v3 networkstatus vote */
1632 const char *msg
= "OK";
1634 if (dirvote_add_vote(body
, &msg
, &status
)) {
1635 write_short_http_response(conn
, status
, "Vote stored");
1638 log_warn(LD_DIRSERV
, "Rejected vote from %s (\"%s\").",
1639 conn
->base_
.address
, msg
);
1640 write_short_http_response(conn
, status
, msg
);
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");
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");
1659 /* we didn't recognize the url */
1660 write_short_http_response(conn
, 404, "Not found");
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. */
1671 http_set_address_origin(const char *headers
, connection_t
*conn
)
1675 fwd
= http_get_header(headers
, "Forwarded-For: ");
1677 fwd
= http_get_header(headers
, "X-Forwarded-For: ");
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
));
1687 tor_free(conn
->address
);
1688 conn
->address
= tor_strdup(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
;
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
));
1717 log_debug(LD_DIRSERV
,"command not all here yet.");
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
);
1732 log_fn(LOG_PROTOCOL_WARN
, LD_PROTOCOL
,
1733 "Got headers %s with unknown command. Closing.",
1738 tor_free(headers
); tor_free(body
);