LCOV - code coverage report
Current view: top level - feature/dircommon - directory.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 167 281 59.4 %
Date: 2021-11-24 03:28:48 Functions: 11 16 68.8 %

          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             : }

Generated by: LCOV version 1.14