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