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