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 : #include "core/or/or.h"
7 :
8 : #include "app/config/config.h"
9 : #include "core/mainloop/connection.h"
10 : #include "core/or/circuitlist.h"
11 : #include "core/or/connection_edge.h"
12 : #include "core/or/connection_or.h"
13 : #include "core/or/channeltls.h"
14 : #include "feature/dircache/dircache.h"
15 : #include "feature/dircache/dirserv.h"
16 : #include "feature/dirclient/dirclient.h"
17 : #include "feature/dircommon/directory.h"
18 : #include "feature/dircommon/fp_pair.h"
19 : #include "feature/stats/geoip_stats.h"
20 : #include "lib/compress/compress.h"
21 :
22 : #include "core/or/circuit_st.h"
23 : #include "core/or/or_circuit_st.h"
24 : #include "core/or/edge_connection_st.h"
25 : #include "core/or/or_connection_st.h"
26 : #include "feature/dircommon/dir_connection_st.h"
27 : #include "feature/nodelist/routerinfo_st.h"
28 :
29 : /**
30 : * \file directory.c
31 : * \brief Code to send and fetch information from directory authorities and
32 : * caches via HTTP.
33 : *
34 : * Directory caches and authorities use dirserv.c to generate the results of a
35 : * query and stream them to the connection; clients use routerparse.c to parse
36 : * them.
37 : *
38 : * Every directory request has a dir_connection_t on the client side and on
39 : * the server side. In most cases, the dir_connection_t object is a linked
40 : * connection, tunneled through an edge_connection_t so that it can be a
41 : * stream on the Tor network. The only non-tunneled connections are those
42 : * that are used to upload material (descriptors and votes) to authorities.
43 : * Among tunneled connections, some use one-hop circuits, and others use
44 : * multi-hop circuits for anonymity.
45 : *
46 : * Directory requests are launched by calling
47 : * directory_initiate_request(). This
48 : * launch the connection, will construct an HTTP request with
49 : * directory_send_command(), send the and wait for a response. The client
50 : * later handles the response with connection_dir_client_reached_eof(),
51 : * which passes the information received to another part of Tor.
52 : *
53 : * On the server side, requests are read in directory_handle_command(),
54 : * which dispatches first on the request type (GET or POST), and then on
55 : * the URL requested. GET requests are processed with a table-based
56 : * dispatcher in url_table[]. The process of handling larger GET requests
57 : * is complicated because we need to avoid allocating a copy of all the
58 : * data to be sent to the client in one huge buffer. Instead, we spool the
59 : * data into the buffer using logic in connection_dirserv_flushed_some() in
60 : * dirserv.c. (TODO: If we extended buf.c to have a zero-copy
61 : * reference-based buffer type, we could remove most of that code, at the
62 : * cost of a bit more reference counting.)
63 : **/
64 :
65 : /* In-points to directory.c:
66 : *
67 : * - directory_post_to_dirservers(), called from
68 : * router_upload_dir_desc_to_dirservers() in router.c
69 : * upload_service_descriptor() in rendservice.c
70 : * - directory_get_from_dirserver(), called from
71 : * run_scheduled_events() in main.c
72 : * do_hup() in main.c
73 : * - connection_dir_process_inbuf(), called from
74 : * connection_process_inbuf() in connection.c
75 : * - connection_dir_finished_flushing(), called from
76 : * connection_finished_flushing() in connection.c
77 : * - connection_dir_finished_connecting(), called from
78 : * connection_finished_connecting() in connection.c
79 : */
80 :
81 : /**
82 : * Cast a `connection_t *` to a `dir_connection_t *`.
83 : *
84 : * Exit with an assertion failure if the input is not a
85 : * `dir_connection_t`.
86 : **/
87 : dir_connection_t *
88 325 : TO_DIR_CONN(connection_t *c)
89 : {
90 325 : tor_assert(c->magic == DIR_CONNECTION_MAGIC);
91 325 : return DOWNCAST(dir_connection_t, c);
92 : }
93 :
94 : /**
95 : * Cast a `const connection_t *` to a `const dir_connection_t *`.
96 : *
97 : * Exit with an assertion failure if the input is not a
98 : * `dir_connection_t`.
99 : **/
100 : const dir_connection_t *
101 0 : CONST_TO_DIR_CONN(const connection_t *c)
102 : {
103 0 : return TO_DIR_CONN((connection_t *)c);
104 : }
105 :
106 : /** Return false if the directory purpose <b>dir_purpose</b>
107 : * does not require an anonymous (three-hop) connection.
108 : *
109 : * Return true 1) by default, 2) if all directory actions have
110 : * specifically been configured to be over an anonymous connection,
111 : * or 3) if the router is a bridge */
112 : int
113 18 : purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose,
114 : const char *resource)
115 : {
116 18 : if (get_options()->AllDirActionsPrivate)
117 : return 1;
118 :
119 18 : if (router_purpose == ROUTER_PURPOSE_BRIDGE) {
120 3 : if (dir_purpose == DIR_PURPOSE_FETCH_SERVERDESC
121 3 : && resource && !strcmp(resource, "authority.z")) {
122 : /* We are asking a bridge for its own descriptor. That doesn't need
123 : anonymity. */
124 : return 0;
125 : }
126 : /* Assume all other bridge stuff needs anonymity. */
127 2 : return 1; /* if no circuits yet, this might break bootstrapping, but it's
128 : * needed to be safe. */
129 : }
130 :
131 15 : switch (dir_purpose)
132 : {
133 : case DIR_PURPOSE_UPLOAD_DIR:
134 : case DIR_PURPOSE_UPLOAD_VOTE:
135 : case DIR_PURPOSE_UPLOAD_SIGNATURES:
136 : case DIR_PURPOSE_FETCH_STATUS_VOTE:
137 : case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES:
138 : case DIR_PURPOSE_FETCH_CONSENSUS:
139 : case DIR_PURPOSE_FETCH_CERTIFICATE:
140 : case DIR_PURPOSE_FETCH_SERVERDESC:
141 : case DIR_PURPOSE_FETCH_EXTRAINFO:
142 : case DIR_PURPOSE_FETCH_MICRODESC:
143 : return 0;
144 0 : case DIR_PURPOSE_HAS_FETCHED_HSDESC:
145 : case DIR_PURPOSE_FETCH_HSDESC:
146 : case DIR_PURPOSE_UPLOAD_HSDESC:
147 0 : return 1;
148 1 : case DIR_PURPOSE_SERVER:
149 : default:
150 1 : log_warn(LD_BUG, "Called with dir_purpose=%d, router_purpose=%d",
151 : dir_purpose, router_purpose);
152 1 : tor_assert_nonfatal_unreached();
153 1 : return 1; /* Assume it needs anonymity; better safe than sorry. */
154 : }
155 : }
156 :
157 : /** Return a newly allocated string describing <b>auth</b>. Only describes
158 : * authority features. */
159 : char *
160 6 : authdir_type_to_string(dirinfo_type_t auth)
161 : {
162 6 : char *result;
163 6 : smartlist_t *lst = smartlist_new();
164 6 : if (auth & V3_DIRINFO)
165 2 : smartlist_add(lst, (void*)"V3");
166 6 : if (auth & BRIDGE_DIRINFO)
167 2 : smartlist_add(lst, (void*)"Bridge");
168 6 : if (smartlist_len(lst)) {
169 3 : result = smartlist_join_strings(lst, ", ", 0, NULL);
170 : } else {
171 3 : result = tor_strdup("[Not an authority]");
172 : }
173 6 : smartlist_free(lst);
174 6 : return result;
175 : }
176 :
177 : /** Return true iff anything we say on <b>conn</b> is being encrypted before
178 : * we send it to the client/server. */
179 : int
180 35 : connection_dir_is_encrypted(const dir_connection_t *conn)
181 : {
182 : /* Right now it's sufficient to see if conn is or has been linked, since
183 : * the only thing it could be linked to is an edge connection on a
184 : * circuit, and the only way it could have been unlinked is at the edge
185 : * connection getting closed.
186 : */
187 35 : return TO_CONN(conn)->linked;
188 : }
189 :
190 : /** Return true iff the given directory connection <b>dir_conn</b> is
191 : * anonymous, that is, it is on a circuit via a public relay and not directly
192 : * from a client or bridge.
193 : *
194 : * For client circuits via relays: true for 2-hop+ paths.
195 : * For client circuits via bridges: true for 3-hop+ paths.
196 : *
197 : * This first test if the connection is encrypted since it is a strong
198 : * requirement for anonymity. */
199 : bool
200 5 : connection_dir_is_anonymous(const dir_connection_t *dir_conn)
201 : {
202 5 : const connection_t *conn, *linked_conn;
203 5 : const edge_connection_t *edge_conn;
204 5 : const circuit_t *circ;
205 :
206 5 : tor_assert(dir_conn);
207 :
208 5 : if (!connection_dir_is_encrypted(dir_conn)) {
209 : return false;
210 : }
211 :
212 : /*
213 : * Buckle up, we'll do a deep dive into the connection in order to get the
214 : * final connection channel of that connection in order to figure out if
215 : * this is a client or relay link.
216 : *
217 : * We go: dir_conn -> linked_conn -> edge_conn -> on_circuit -> p_chan.
218 : */
219 :
220 5 : conn = TO_CONN(dir_conn);
221 5 : linked_conn = conn->linked_conn;
222 :
223 : /* The dir connection should be connected to an edge connection. It can not
224 : * be closed or marked for close. */
225 5 : if (linked_conn == NULL || linked_conn->magic != EDGE_CONNECTION_MAGIC ||
226 5 : conn->linked_conn_is_closed || conn->linked_conn->marked_for_close) {
227 0 : log_debug(LD_DIR, "Directory connection is not anonymous: "
228 : "not linked to edge");
229 0 : return false;
230 : }
231 :
232 5 : edge_conn = CONST_TO_EDGE_CONN(linked_conn);
233 5 : circ = edge_conn->on_circuit;
234 :
235 : /* Can't be a circuit we initiated and without a circuit, no channel. */
236 5 : if (circ == NULL || CIRCUIT_IS_ORIGIN(circ)) {
237 0 : log_debug(LD_DIR, "Directory connection is not anonymous: "
238 : "not on OR circuit");
239 0 : return false;
240 : }
241 :
242 : /* It is possible that the circuit was closed because one of the channel was
243 : * closed or a DESTROY cell was received. Either way, this connection can
244 : * not continue so return that it is not anonymous since we can not know for
245 : * sure if it is. */
246 5 : if (circ->marked_for_close) {
247 0 : log_debug(LD_DIR, "Directory connection is not anonymous: "
248 : "circuit marked for close");
249 0 : return false;
250 : }
251 :
252 : /* Get the previous channel to learn if it is a client or relay link. We
253 : * BUG() because if the circuit is not mark for close, we ought to have a
254 : * p_chan else we have a code flow issue. */
255 5 : if (BUG(CONST_TO_OR_CIRCUIT(circ)->p_chan == NULL)) {
256 0 : log_debug(LD_DIR, "Directory connection is not anonymous: "
257 : "no p_chan on circuit");
258 0 : return false;
259 : }
260 :
261 : /* Will be true if the channel is an unauthenticated peer which is only true
262 : * for clients and bridges. */
263 5 : return !channel_is_client(CONST_TO_OR_CIRCUIT(circ)->p_chan);
264 : }
265 :
266 : /** Parse an HTTP request line at the start of a headers string. On failure,
267 : * return -1. On success, set *<b>command_out</b> to a copy of the HTTP
268 : * command ("get", "post", etc), set *<b>url_out</b> to a copy of the URL, and
269 : * return 0. */
270 : int
271 78 : parse_http_command(const char *headers, char **command_out, char **url_out)
272 : {
273 78 : const char *command, *end_of_command;
274 78 : char *s, *start, *tmp;
275 :
276 78 : s = (char *)eat_whitespace_no_nl(headers);
277 78 : if (!*s) return -1;
278 77 : command = s;
279 77 : s = (char *)find_whitespace(s); /* get past GET/POST */
280 77 : if (!*s) return -1;
281 77 : end_of_command = s;
282 77 : s = (char *)eat_whitespace_no_nl(s);
283 77 : if (!*s) return -1;
284 77 : start = s; /* this is the URL, assuming it's valid */
285 77 : s = (char *)find_whitespace(start);
286 77 : if (!*s) return -1;
287 :
288 : /* tolerate the http[s] proxy style of putting the hostname in the url */
289 76 : if (s-start >= 4 && !strcmpstart(start,"http")) {
290 0 : tmp = start + 4;
291 0 : if (*tmp == 's')
292 0 : tmp++;
293 0 : if (s-tmp >= 3 && !strcmpstart(tmp,"://")) {
294 0 : tmp = strchr(tmp+3, '/');
295 0 : if (tmp && tmp < s) {
296 0 : log_debug(LD_DIR,"Skipping over 'http[s]://hostname/' string");
297 0 : start = tmp;
298 : }
299 : }
300 : }
301 :
302 : /* Check if the header is well formed (next sequence
303 : * should be HTTP/1.X\r\n). Assumes we're supporting 1.0? */
304 : {
305 76 : unsigned minor_ver;
306 76 : char ch;
307 76 : char *e = (char *)eat_whitespace_no_nl(s);
308 76 : if (2 != tor_sscanf(e, "HTTP/1.%u%c", &minor_ver, &ch)) {
309 7 : return -1;
310 : }
311 70 : if (ch != '\r')
312 : return -1;
313 : }
314 :
315 69 : *url_out = tor_memdup_nulterm(start, s-start);
316 69 : *command_out = tor_memdup_nulterm(command, end_of_command - command);
317 69 : return 0;
318 : }
319 :
320 : /** Return a copy of the first HTTP header in <b>headers</b> whose key is
321 : * <b>which</b>. The key should be given with a terminating colon and space;
322 : * this function copies everything after, up to but not including the
323 : * following \\r\\n. */
324 : char *
325 133 : http_get_header(const char *headers, const char *which)
326 : {
327 133 : const char *cp = headers;
328 532 : while (cp) {
329 401 : if (!strcasecmpstart(cp, which)) {
330 2 : char *eos;
331 2 : cp += strlen(which);
332 2 : if ((eos = strchr(cp,'\r')))
333 2 : return tor_strndup(cp, eos-cp);
334 : else
335 0 : return tor_strdup(cp);
336 : }
337 399 : cp = strchr(cp, '\n');
338 399 : if (cp)
339 268 : ++cp;
340 : }
341 : return NULL;
342 : }
343 : /** Parse an HTTP response string <b>headers</b> of the form
344 : * \verbatim
345 : * "HTTP/1.\%d \%d\%s\r\n...".
346 : * \endverbatim
347 : *
348 : * If it's well-formed, assign the status code to *<b>code</b> and
349 : * return 0. Otherwise, return -1.
350 : *
351 : * On success: If <b>date</b> is provided, set *date to the Date
352 : * header in the http headers, or 0 if no such header is found. If
353 : * <b>compression</b> is provided, set *<b>compression</b> to the
354 : * compression method given in the Content-Encoding header, or 0 if no
355 : * such header is found, or -1 if the value of the header is not
356 : * recognized. If <b>reason</b> is provided, strdup the reason string
357 : * into it.
358 : */
359 : int
360 0 : parse_http_response(const char *headers, int *code, time_t *date,
361 : compress_method_t *compression, char **reason)
362 : {
363 0 : unsigned n1, n2;
364 0 : char datestr[RFC1123_TIME_LEN+1];
365 0 : smartlist_t *parsed_headers;
366 0 : tor_assert(headers);
367 0 : tor_assert(code);
368 :
369 0 : while (TOR_ISSPACE(*headers)) headers++; /* tolerate leading whitespace */
370 :
371 0 : if (tor_sscanf(headers, "HTTP/1.%u %u", &n1, &n2) < 2 ||
372 0 : (n1 != 0 && n1 != 1) ||
373 0 : (n2 < 100 || n2 >= 600)) {
374 0 : log_warn(LD_HTTP,"Failed to parse header %s",escaped(headers));
375 0 : return -1;
376 : }
377 0 : *code = n2;
378 :
379 0 : parsed_headers = smartlist_new();
380 0 : smartlist_split_string(parsed_headers, headers, "\n",
381 : SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
382 0 : if (reason) {
383 0 : smartlist_t *status_line_elements = smartlist_new();
384 0 : tor_assert(smartlist_len(parsed_headers));
385 0 : smartlist_split_string(status_line_elements,
386 0 : smartlist_get(parsed_headers, 0),
387 : " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
388 0 : tor_assert(smartlist_len(status_line_elements) <= 3);
389 0 : if (smartlist_len(status_line_elements) == 3) {
390 0 : *reason = smartlist_get(status_line_elements, 2);
391 0 : smartlist_set(status_line_elements, 2, NULL); /* Prevent free */
392 : }
393 0 : SMARTLIST_FOREACH(status_line_elements, char *, cp, tor_free(cp));
394 0 : smartlist_free(status_line_elements);
395 : }
396 0 : if (date) {
397 0 : *date = 0;
398 0 : SMARTLIST_FOREACH(parsed_headers, const char *, s,
399 : if (!strcmpstart(s, "Date: ")) {
400 : strlcpy(datestr, s+6, sizeof(datestr));
401 : /* This will do nothing on failure, so we don't need to check
402 : the result. We shouldn't warn, since there are many other valid
403 : date formats besides the one we use. */
404 : parse_rfc1123_time(datestr, date);
405 : break;
406 : });
407 : }
408 0 : if (compression) {
409 0 : const char *enc = NULL;
410 0 : SMARTLIST_FOREACH(parsed_headers, const char *, s,
411 : if (!strcmpstart(s, "Content-Encoding: ")) {
412 : enc = s+18; break;
413 : });
414 :
415 0 : if (enc == NULL)
416 0 : *compression = NO_METHOD;
417 : else {
418 0 : *compression = compression_method_get_by_name(enc);
419 :
420 0 : if (*compression == UNKNOWN_METHOD)
421 0 : log_info(LD_HTTP, "Unrecognized content encoding: %s. Trying to deal.",
422 : escaped(enc));
423 : }
424 : }
425 0 : SMARTLIST_FOREACH(parsed_headers, char *, s, tor_free(s));
426 0 : smartlist_free(parsed_headers);
427 :
428 0 : return 0;
429 : }
430 :
431 : /** If any directory object is arriving, and it's over 10MB large, we're
432 : * getting DoS'd. (As of 0.1.2.x, raw directories are about 1MB, and we never
433 : * ask for more than 96 router descriptors at a time.)
434 : */
435 : #define MAX_DIRECTORY_OBJECT_SIZE (10*(1<<20))
436 :
437 : #define MAX_VOTE_DL_SIZE (MAX_DIRECTORY_OBJECT_SIZE * 5)
438 :
439 : /** Read handler for directory connections. (That's connections <em>to</em>
440 : * directory servers and connections <em>at</em> directory servers.)
441 : */
442 : int
443 0 : connection_dir_process_inbuf(dir_connection_t *conn)
444 : {
445 0 : size_t max_size;
446 0 : tor_assert(conn);
447 0 : tor_assert(conn->base_.type == CONN_TYPE_DIR);
448 :
449 : /* Directory clients write, then read data until they receive EOF;
450 : * directory servers read data until they get an HTTP command, then
451 : * write their response (when it's finished flushing, they mark for
452 : * close).
453 : */
454 :
455 : /* If we're on the dirserver side, look for a command. */
456 0 : if (conn->base_.state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) {
457 0 : if (directory_handle_command(conn) < 0) {
458 0 : connection_mark_for_close(TO_CONN(conn));
459 0 : return -1;
460 : }
461 : return 0;
462 : }
463 :
464 0 : max_size =
465 0 : (TO_CONN(conn)->purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) ?
466 0 : MAX_VOTE_DL_SIZE : MAX_DIRECTORY_OBJECT_SIZE;
467 :
468 0 : if (connection_get_inbuf_len(TO_CONN(conn)) > max_size) {
469 0 : log_warn(LD_HTTP,
470 : "Too much data received from %s: "
471 : "denial of service attempt, or you need to upgrade?",
472 : connection_describe(TO_CONN(conn)));
473 0 : connection_mark_for_close(TO_CONN(conn));
474 0 : return -1;
475 : }
476 :
477 0 : if (!conn->base_.inbuf_reached_eof)
478 0 : log_debug(LD_HTTP,"Got data, not eof. Leaving on inbuf.");
479 : return 0;
480 : }
481 :
482 : /** Called when we're about to finally unlink and free a directory connection:
483 : * perform necessary accounting and cleanup */
484 : void
485 8 : connection_dir_about_to_close(dir_connection_t *dir_conn)
486 : {
487 8 : connection_t *conn = TO_CONN(dir_conn);
488 :
489 8 : if (conn->state < DIR_CONN_STATE_CLIENT_FINISHED) {
490 : /* It's a directory connection and connecting or fetching
491 : * failed: forget about this router, and maybe try again. */
492 3 : connection_dir_client_request_failed(dir_conn);
493 : }
494 :
495 8 : connection_dir_client_refetch_hsdesc_if_needed(dir_conn);
496 8 : }
497 :
498 : /** Write handler for directory connections; called when all data has
499 : * been flushed. Close the connection or wait for a response as
500 : * appropriate.
501 : */
502 : int
503 0 : connection_dir_finished_flushing(dir_connection_t *conn)
504 : {
505 0 : tor_assert(conn);
506 0 : tor_assert(conn->base_.type == CONN_TYPE_DIR);
507 :
508 0 : if (conn->base_.marked_for_close)
509 : return 0;
510 :
511 : /* Note that we have finished writing the directory response. For direct
512 : * connections this means we're done; for tunneled connections it's only
513 : * an intermediate step. */
514 0 : if (conn->dirreq_id)
515 0 : geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED,
516 : DIRREQ_FLUSHING_DIR_CONN_FINISHED);
517 : else
518 0 : geoip_change_dirreq_state(TO_CONN(conn)->global_identifier,
519 : DIRREQ_DIRECT,
520 : DIRREQ_FLUSHING_DIR_CONN_FINISHED);
521 0 : switch (conn->base_.state) {
522 : case DIR_CONN_STATE_CONNECTING:
523 : case DIR_CONN_STATE_CLIENT_SENDING:
524 0 : log_debug(LD_DIR,"client finished sending command.");
525 0 : conn->base_.state = DIR_CONN_STATE_CLIENT_READING;
526 0 : return 0;
527 0 : case DIR_CONN_STATE_SERVER_WRITING:
528 0 : if (conn->spool) {
529 0 : log_warn(LD_BUG, "Emptied a dirserv buffer, but it's still spooling!");
530 0 : connection_mark_for_close(TO_CONN(conn));
531 : } else {
532 0 : log_debug(LD_DIRSERV, "Finished writing server response. Closing.");
533 0 : connection_mark_for_close(TO_CONN(conn));
534 : }
535 : return 0;
536 0 : default:
537 0 : log_warn(LD_BUG,"called in unexpected state %d.",
538 : conn->base_.state);
539 0 : tor_fragile_assert();
540 : return -1;
541 : }
542 : return 0;
543 : }
544 :
545 : /** Connected handler for directory connections: begin sending data to the
546 : * server, and return 0.
547 : * Only used when connections don't immediately connect. */
548 : int
549 0 : connection_dir_finished_connecting(dir_connection_t *conn)
550 : {
551 0 : tor_assert(conn);
552 0 : tor_assert(conn->base_.type == CONN_TYPE_DIR);
553 0 : tor_assert(conn->base_.state == DIR_CONN_STATE_CONNECTING);
554 :
555 0 : log_debug(LD_HTTP,"Dir connection to %s established.",
556 : connection_describe_peer(TO_CONN(conn)));
557 :
558 : /* start flushing conn */
559 0 : conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
560 0 : return 0;
561 : }
562 :
563 : /** Helper. Compare two fp_pair_t objects, and return negative, 0, or
564 : * positive as appropriate. */
565 : static int
566 5 : compare_pairs_(const void **a, const void **b)
567 : {
568 5 : const fp_pair_t *fp1 = *a, *fp2 = *b;
569 5 : int r;
570 5 : if ((r = fast_memcmp(fp1->first, fp2->first, DIGEST_LEN)))
571 : return r;
572 : else
573 2 : return fast_memcmp(fp1->second, fp2->second, DIGEST_LEN);
574 : }
575 :
576 : /** Divide a string <b>res</b> of the form FP1-FP2+FP3-FP4...[.z], where each
577 : * FP is a hex-encoded fingerprint, into a sequence of distinct sorted
578 : * fp_pair_t. Skip malformed pairs. On success, return 0 and add those
579 : * fp_pair_t into <b>pairs_out</b>. On failure, return -1. */
580 : int
581 3 : dir_split_resource_into_fingerprint_pairs(const char *res,
582 : smartlist_t *pairs_out)
583 : {
584 3 : smartlist_t *pairs_tmp = smartlist_new();
585 3 : smartlist_t *pairs_result = smartlist_new();
586 :
587 3 : smartlist_split_string(pairs_tmp, res, "+", 0, 0);
588 3 : if (smartlist_len(pairs_tmp)) {
589 3 : char *last = smartlist_get(pairs_tmp,smartlist_len(pairs_tmp)-1);
590 3 : size_t last_len = strlen(last);
591 3 : if (last_len > 2 && !strcmp(last+last_len-2, ".z")) {
592 1 : last[last_len-2] = '\0';
593 : }
594 : }
595 8 : SMARTLIST_FOREACH_BEGIN(pairs_tmp, char *, cp) {
596 5 : if (strlen(cp) != HEX_DIGEST_LEN*2+1) {
597 1 : log_info(LD_DIR,
598 : "Skipping digest pair %s with non-standard length.", escaped(cp));
599 4 : } else if (cp[HEX_DIGEST_LEN] != '-') {
600 0 : log_info(LD_DIR,
601 : "Skipping digest pair %s with missing dash.", escaped(cp));
602 : } else {
603 4 : fp_pair_t pair;
604 4 : if (base16_decode(pair.first, DIGEST_LEN,
605 4 : cp, HEX_DIGEST_LEN) != DIGEST_LEN ||
606 4 : base16_decode(pair.second,DIGEST_LEN,
607 4 : cp+HEX_DIGEST_LEN+1, HEX_DIGEST_LEN) != DIGEST_LEN) {
608 0 : log_info(LD_DIR, "Skipping non-decodable digest pair %s", escaped(cp));
609 : } else {
610 4 : smartlist_add(pairs_result, tor_memdup(&pair, sizeof(pair)));
611 : }
612 : }
613 5 : tor_free(cp);
614 5 : } SMARTLIST_FOREACH_END(cp);
615 3 : smartlist_free(pairs_tmp);
616 :
617 : /* Uniq-and-sort */
618 3 : smartlist_sort(pairs_result, compare_pairs_);
619 3 : smartlist_uniq(pairs_result, compare_pairs_, tor_free_);
620 :
621 3 : smartlist_add_all(pairs_out, pairs_result);
622 3 : smartlist_free(pairs_result);
623 3 : return 0;
624 : }
625 :
626 : /** Given a directory <b>resource</b> request, containing zero
627 : * or more strings separated by plus signs, followed optionally by ".z", store
628 : * the strings, in order, into <b>fp_out</b>. If <b>compressed_out</b> is
629 : * non-NULL, set it to 1 if the resource ends in ".z", else set it to 0.
630 : *
631 : * If (flags & DSR_HEX), then delete all elements that aren't hex digests, and
632 : * decode the rest. If (flags & DSR_BASE64), then use "-" rather than "+" as
633 : * a separator, delete all the elements that aren't base64-encoded digests,
634 : * and decode the rest. If (flags & DSR_DIGEST256), these digests should be
635 : * 256 bits long; else they should be 160.
636 : *
637 : * If (flags & DSR_SORT_UNIQ), then sort the list and remove all duplicates.
638 : */
639 : int
640 38 : dir_split_resource_into_fingerprints(const char *resource,
641 : smartlist_t *fp_out, int *compressed_out,
642 : int flags)
643 : {
644 38 : const int decode_hex = flags & DSR_HEX;
645 38 : const int decode_base64 = flags & DSR_BASE64;
646 38 : const int digests_are_256 = flags & DSR_DIGEST256;
647 38 : const int sort_uniq = flags & DSR_SORT_UNIQ;
648 :
649 38 : const int digest_len = digests_are_256 ? DIGEST256_LEN : DIGEST_LEN;
650 76 : const int hex_digest_len = digests_are_256 ?
651 38 : HEX_DIGEST256_LEN : HEX_DIGEST_LEN;
652 76 : const int base64_digest_len = digests_are_256 ?
653 38 : BASE64_DIGEST256_LEN : BASE64_DIGEST_LEN;
654 38 : smartlist_t *fp_tmp = smartlist_new();
655 :
656 38 : tor_assert(!(decode_hex && decode_base64));
657 38 : tor_assert(fp_out);
658 :
659 61 : smartlist_split_string(fp_tmp, resource, decode_base64?"-":"+", 0, 0);
660 38 : if (compressed_out)
661 0 : *compressed_out = 0;
662 38 : if (smartlist_len(fp_tmp)) {
663 38 : char *last = smartlist_get(fp_tmp,smartlist_len(fp_tmp)-1);
664 38 : size_t last_len = strlen(last);
665 38 : if (last_len > 2 && !strcmp(last+last_len-2, ".z")) {
666 0 : last[last_len-2] = '\0';
667 0 : if (compressed_out)
668 0 : *compressed_out = 1;
669 : }
670 : }
671 38 : if (decode_hex || decode_base64) {
672 35 : const size_t encoded_len = decode_hex ? hex_digest_len : base64_digest_len;
673 35 : int i;
674 35 : char *cp, *d = NULL;
675 92 : for (i = 0; i < smartlist_len(fp_tmp); ++i) {
676 57 : cp = smartlist_get(fp_tmp, i);
677 57 : if (strlen(cp) != encoded_len) {
678 15 : log_info(LD_DIR,
679 : "Skipping digest %s with non-standard length.", escaped(cp));
680 15 : smartlist_del_keeporder(fp_tmp, i--);
681 15 : goto again;
682 : }
683 42 : d = tor_malloc_zero(digest_len);
684 84 : if (decode_hex ?
685 33 : (base16_decode(d, digest_len, cp, hex_digest_len) != digest_len) :
686 9 : (base64_decode(d, digest_len, cp, base64_digest_len)
687 : != digest_len)) {
688 0 : log_info(LD_DIR, "Skipping non-decodable digest %s", escaped(cp));
689 0 : smartlist_del_keeporder(fp_tmp, i--);
690 0 : goto again;
691 : }
692 42 : smartlist_set(fp_tmp, i, d);
693 42 : d = NULL;
694 57 : again:
695 57 : tor_free(cp);
696 57 : tor_free(d);
697 : }
698 : }
699 38 : if (sort_uniq) {
700 21 : if (decode_hex || decode_base64) {
701 20 : if (digests_are_256) {
702 4 : smartlist_sort_digests256(fp_tmp);
703 4 : smartlist_uniq_digests256(fp_tmp);
704 : } else {
705 16 : smartlist_sort_digests(fp_tmp);
706 16 : smartlist_uniq_digests(fp_tmp);
707 : }
708 : } else {
709 1 : smartlist_sort_strings(fp_tmp);
710 1 : smartlist_uniq_strings(fp_tmp);
711 : }
712 : }
713 38 : smartlist_add_all(fp_out, fp_tmp);
714 38 : smartlist_free(fp_tmp);
715 38 : return 0;
716 : }
|