1 /* Copyright 2001-2004 Roger Dingledine.
2 * Copyright 2004 Roger Dingledine, Nick Mathewson. */
3 /* See LICENSE for licensing information */
5 const char directory_c_id
[] = "$Id$";
11 * \brief Implement directory HTTP protocol.
14 /* In-points to directory.c:
16 * - directory_post_to_dirservers(), called from
17 * router_upload_dir_desc_to_dirservers() in router.c
18 * upload_service_descriptor() in rendservice.c
19 * - directory_get_from_dirserver(), called from
20 * rend_client_refetch_renddesc() in rendclient.c
21 * run_scheduled_events() in main.c
23 * - connection_dir_process_inbuf(), called from
24 * connection_process_inbuf() in connection.c
25 * - connection_dir_finished_flushing(), called from
26 * connection_finished_flushing() in connection.c
27 * - connection_dir_finished_connecting(), called from
28 * connection_finished_connecting() in connection.c
32 directory_initiate_command_router(routerinfo_t
*router
, uint8_t purpose
,
34 const char *payload
, size_t payload_len
);
36 directory_initiate_command_trusted_dir(trusted_dir_server_t
*dirserv
,
37 uint8_t purpose
, const char *resource
,
38 const char *payload
, size_t payload_len
);
40 directory_initiate_command(const char *address
, uint32_t addr
, uint16_t port
,
42 const char *digest
, uint8_t purpose
,
44 const char *payload
, size_t payload_len
);
47 directory_send_command(connection_t
*conn
, const char *platform
,
48 int purpose
, const char *resource
,
49 const char *payload
, size_t payload_len
);
50 static int directory_handle_command(connection_t
*conn
);
52 /********* START VARIABLES **********/
54 static addr_policy_t
*dir_policy
= NULL
;
56 #if 0 /* commented out for now, since for now what clients send is
57 different from what servers want to receive */
58 /** URL for publishing rendezvous descriptors. */
59 char rend_publish_string
[] = "/tor/rendezvous/publish";
60 /** Prefix for downloading rendezvous descriptors. */
61 char rend_fetch_url
[] = "/tor/rendezvous/";
64 #define MAX_HEADERS_SIZE 50000
65 #define MAX_BODY_SIZE 500000
67 #define ALLOW_DIRECTORY_TIME_SKEW 30*60
69 /********* END VARIABLES ************/
71 /** Parse get_options()->DirPolicy, and put the processed version in
72 * &dir_policy. Ignore port specifiers.
75 parse_dir_policy(void)
79 addr_policy_free(dir_policy
);
82 config_parse_addr_policy(get_options()->DirPolicy
, &dir_policy
);
83 /* ports aren't used. */
84 for (n
=dir_policy
; n
; n
= n
->next
) {
90 /** Return 1 if <b>addr</b> is permitted to connect to our dir port,
91 * based on <b>dir_policy</b>. Else return 0.
93 int dir_policy_permits_address(uint32_t addr
)
97 if (!dir_policy
) /* 'no dir policy' means 'accept' */
99 a
= router_compare_addr_to_addr_policy(addr
, 1, dir_policy
);
105 log_fn(LOG_WARN
, "Bug: got unexpected 'maybe' answer from dir policy");
109 /** Start a connection to every known directory server, using
110 * connection purpose 'purpose' and uploading the payload 'payload'
111 * (length 'payload_len'). The purpose should be one of
112 * 'DIR_PURPOSE_UPLOAD_DIR' or 'DIR_PURPOSE_UPLOAD_RENDDESC'.
115 directory_post_to_dirservers(uint8_t purpose
, const char *payload
,
118 smartlist_t
*dirservers
;
121 router_get_trusted_dir_servers(&dirservers
);
122 tor_assert(dirservers
);
123 /* This tries dirservers which we believe to be down, but ultimately, that's
124 * harmless, and we may as well err on the side of getting things uploaded.
126 SMARTLIST_FOREACH(dirservers
, trusted_dir_server_t
*, ds
,
128 /* Pay attention to fascistfirewall when we're uploading a
129 * router descriptor, but not when uploading a service
130 * descriptor -- those use Tor. */
131 if (get_options()->FascistFirewall
&& purpose
== DIR_PURPOSE_UPLOAD_DIR
&&
132 !get_options()->HttpProxy
) {
133 tor_snprintf(buf
,sizeof(buf
),"%d",ds
->dir_port
);
134 if (!smartlist_string_isin(get_options()->FirewallPorts
, buf
))
137 directory_initiate_command_trusted_dir(ds
, purpose
, NULL
,
138 payload
, payload_len
);
142 /** Start a connection to a random running directory server, using
143 * connection purpose 'purpose' requesting 'resource'. The purpose
144 * should be one of 'DIR_PURPOSE_FETCH_DIR',
145 * 'DIR_PURPOSE_FETCH_RENDDESC', 'DIR_PURPOSE_FETCH_RUNNING_LIST.'
146 * If <b>retry_if_no_servers</b>, then if all the possible servers seem
147 * down, mark them up and try again.
150 directory_get_from_dirserver(uint8_t purpose
, const char *resource
,
151 int retry_if_no_servers
)
153 routerinfo_t
*r
= NULL
;
154 trusted_dir_server_t
*ds
= NULL
;
155 int fascistfirewall
= get_options()->FascistFirewall
;
157 if (purpose
== DIR_PURPOSE_FETCH_DIR
||
158 purpose
== DIR_PURPOSE_FETCH_RUNNING_LIST
) {
159 if (advertised_server_mode()) {
160 /* only ask authdirservers, and don't ask myself */
161 ds
= router_pick_trusteddirserver(1, fascistfirewall
,
162 retry_if_no_servers
);
164 /* anybody with a non-zero dirport will do */
165 r
= router_pick_directory_server(1, fascistfirewall
,
166 purpose
==DIR_PURPOSE_FETCH_RUNNING_LIST
,
167 retry_if_no_servers
);
169 log_fn(LOG_INFO
, "No router found for %s; falling back to dirserver list",
170 purpose
== DIR_PURPOSE_FETCH_RUNNING_LIST
171 ? "status list" : "directory");
172 ds
= router_pick_trusteddirserver(1, fascistfirewall
,
173 retry_if_no_servers
);
176 } else { // (purpose == DIR_PURPOSE_FETCH_RENDDESC)
177 /* only ask authdirservers, any of them will do */
178 /* Never use fascistfirewall; we're going via Tor. */
179 ds
= router_pick_trusteddirserver(0, 0, retry_if_no_servers
);
183 directory_initiate_command_router(r
, purpose
, resource
, NULL
, 0);
185 directory_initiate_command_trusted_dir(ds
, purpose
, resource
, NULL
, 0);
187 log_fn(LOG_WARN
,"No running dirservers known. Not trying. (purpose %d)", purpose
);
191 /** Launch a new connection to the directory server <b>router</b> to upload or
192 * download a service or rendezvous descriptor. <b>purpose</b> determines what
193 * kind of directory connection we're launching, and must be one of
194 * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC}.
196 * When uploading, <b>payload</b> and <b>payload_len</b> determine the content
197 * of the HTTP post. Otherwise, <b>payload</b> should be NULL.
199 * When fetching a rendezvous descriptor, <b>resource</b> is the service ID we
203 directory_initiate_command_router(routerinfo_t
*router
, uint8_t purpose
,
204 const char *resource
,
205 const char *payload
, size_t payload_len
)
207 directory_initiate_command(router
->address
, router
->addr
, router
->dir_port
,
208 router
->platform
, router
->identity_digest
,
209 purpose
, resource
, payload
, payload_len
);
212 /** As directory_initiate_command_router, but send the command to a trusted
213 * directory server <b>dirserv</b>. **/
215 directory_initiate_command_trusted_dir(trusted_dir_server_t
*dirserv
,
216 uint8_t purpose
, const char *resource
,
217 const char *payload
, size_t payload_len
)
219 directory_initiate_command(dirserv
->address
, dirserv
->addr
,dirserv
->dir_port
,
220 NULL
, dirserv
->digest
, purpose
, resource
, payload
, payload_len
);
223 /** Called when we are unable to complete our connection to a
224 * directory server: Mark the router as down and try again if possible.
227 connection_dir_connect_failed(connection_t
*conn
)
229 router_mark_as_down(conn
->identity_digest
); /* don't try him again */
230 if (conn
->purpose
== DIR_PURPOSE_FETCH_DIR
||
231 conn
->purpose
== DIR_PURPOSE_FETCH_RUNNING_LIST
) {
232 log_fn(LOG_INFO
, "Giving up on directory server at '%s'; retrying",
234 directory_get_from_dirserver(conn
->purpose
, NULL
,
235 0 /* don't retry_if_no_servers */);
239 /** Helper for directory_initiate_command(router|trusted_dir): send the
240 * command to a server whose address is <b>address</b>, whose IP is
241 * <b>addr</b>, whose directory port is <b>dir_port</b>, whose tor version is
242 * <b>platform</b>, and whose identity key digest is <b>digest</b>. The
243 * <b>platform</b> argument is optional; the others are required. */
245 directory_initiate_command(const char *address
, uint32_t addr
,
246 uint16_t dir_port
, const char *platform
,
247 const char *digest
, uint8_t purpose
,
248 const char *resource
,
249 const char *payload
, size_t payload_len
)
255 tor_assert(dir_port
);
259 case DIR_PURPOSE_FETCH_DIR
:
260 log_fn(LOG_DEBUG
,"initiating directory fetch");
262 case DIR_PURPOSE_FETCH_RENDDESC
:
263 log_fn(LOG_DEBUG
,"initiating hidden-service descriptor fetch");
265 case DIR_PURPOSE_UPLOAD_DIR
:
266 log_fn(LOG_DEBUG
,"initiating server descriptor upload");
268 case DIR_PURPOSE_UPLOAD_RENDDESC
:
269 log_fn(LOG_DEBUG
,"initiating hidden-service descriptor upload");
271 case DIR_PURPOSE_FETCH_RUNNING_LIST
:
272 log_fn(LOG_DEBUG
,"initiating running-routers fetch");
275 log_fn(LOG_ERR
, "Unrecognized directory connection purpose.");
279 conn
= connection_new(CONN_TYPE_DIR
);
281 /* set up conn so it's got all the data we need to remember */
283 conn
->port
= dir_port
;
285 if (get_options()->HttpProxy
) {
286 addr
= get_options()->HttpProxyAddr
;
287 dir_port
= get_options()->HttpProxyPort
;
290 conn
->address
= tor_strdup(address
);
291 /* conn->nickname = tor_strdup(router->nickname); */
292 /* tor_assert(router->identity_pkey); */
293 /* conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); */
294 /* crypto_pk_get_digest(conn->identity_pkey, conn->identity_digest); */
295 memcpy(conn
->identity_digest
, digest
, DIGEST_LEN
);
297 conn
->purpose
= purpose
;
299 /* give it an initial state */
300 conn
->state
= DIR_CONN_STATE_CONNECTING
;
302 if (purpose
== DIR_PURPOSE_FETCH_DIR
||
303 purpose
== DIR_PURPOSE_UPLOAD_DIR
||
304 purpose
== DIR_PURPOSE_FETCH_RUNNING_LIST
) {
305 /* then we want to connect directly */
306 switch (connection_connect(conn
, conn
->address
, addr
, dir_port
)) {
308 connection_dir_connect_failed(conn
);
309 connection_free(conn
);
312 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING
; /* start flushing conn */
315 /* queue the command on the outbuf */
316 directory_send_command(conn
, platform
, purpose
, resource
,
317 payload
, payload_len
);
318 connection_watch_events(conn
, POLLIN
| POLLOUT
| POLLERR
);
319 /* writable indicates finish, readable indicates broken link,
320 error indicates broken link in windowsland. */
322 } else { /* we want to connect via tor */
323 /* make an AP connection
324 * populate it and add it at the right state
325 * socketpair and hook up both sides
327 conn
->s
= connection_ap_make_bridge(conn
->address
, conn
->port
);
329 log_fn(LOG_WARN
,"Making AP bridge to dirserver failed.");
330 connection_mark_for_close(conn
);
334 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING
;
335 connection_add(conn
);
336 /* queue the command on the outbuf */
337 directory_send_command(conn
, platform
, purpose
, resource
,
338 payload
, payload_len
);
339 connection_watch_events(conn
, POLLIN
| POLLOUT
| POLLERR
);
343 /** Queue an appropriate HTTP command on conn-\>outbuf. The other args
344 * are as in directory_initiate_command.
347 directory_send_command(connection_t
*conn
, const char *platform
,
348 int purpose
, const char *resource
,
349 const char *payload
, size_t payload_len
) {
351 char proxystring
[128];
352 char hoststring
[128];
355 const char *httpcommand
= NULL
;
358 tor_assert(conn
->type
== CONN_TYPE_DIR
);
360 /* If we don't know the platform, assume it's up-to-date. */
361 use_newer
= platform
? tor_version_as_new_as(platform
, "0.0.9pre1"):1;
363 if (conn
->port
== 80) {
364 strlcpy(hoststring
, conn
->address
, sizeof(hoststring
));
366 tor_snprintf(hoststring
, sizeof(hoststring
),"%s:%d",conn
->address
, conn
->port
);
368 if (get_options()->HttpProxy
) {
369 tor_snprintf(proxystring
, sizeof(proxystring
),"http://%s", hoststring
);
375 case DIR_PURPOSE_FETCH_DIR
:
376 tor_assert(!resource
);
377 tor_assert(!payload
);
378 log_fn(LOG_DEBUG
, "Asking for %scompressed directory from server running %s",
379 use_newer
?"":"un", platform
?platform
:"<unknown version>");
381 strlcpy(url
, use_newer
? "/tor/dir.z" : "/", sizeof(url
));
383 case DIR_PURPOSE_FETCH_RUNNING_LIST
:
384 tor_assert(!resource
);
385 tor_assert(!payload
);
387 strlcpy(url
, use_newer
? "/tor/running-routers" : "/running-routers", sizeof(url
));
389 case DIR_PURPOSE_UPLOAD_DIR
:
390 tor_assert(!resource
);
392 httpcommand
= "POST";
393 strlcpy(url
, use_newer
? "/tor/" : "/", sizeof(url
));
395 case DIR_PURPOSE_FETCH_RENDDESC
:
396 tor_assert(resource
);
397 tor_assert(!payload
);
399 /* this must be true or we wouldn't be doing the lookup */
400 tor_assert(strlen(resource
) <= REND_SERVICE_ID_LEN
);
401 /* This breaks the function abstraction. */
402 strlcpy(conn
->rend_query
, resource
, sizeof(conn
->rend_query
));
405 tor_snprintf(url
, sizeof(url
), "%s/rendezvous/%s", use_newer
? "/tor" : "", resource
);
408 case DIR_PURPOSE_UPLOAD_RENDDESC
:
409 tor_assert(!resource
);
411 httpcommand
= "POST";
412 tor_snprintf(url
, sizeof(url
), "%s/rendezvous/publish", use_newer
? "/tor" : "");
416 tor_snprintf(tmp
, sizeof(tmp
), "%s %s%s HTTP/1.0\r\nContent-Length: %lu\r\nHost: %s\r\n\r\n",
420 payload
? (unsigned long)payload_len
: 0,
422 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
425 /* then send the payload afterwards too */
426 connection_write_to_buf(payload
, payload_len
, conn
);
430 /** Parse an HTTP request string <b>headers</b> of the form
431 * "\%s [http[s]://]\%s HTTP/1..."
432 * If it's well-formed, strdup the second \%s into *<b>url</b>, and
433 * null-terminate it. If the url doesn't start with "/tor/", rewrite it
434 * so it does. Return 0.
435 * Otherwise, return -1.
438 parse_http_url(char *headers
, char **url
)
440 char *s
, *start
, *tmp
;
442 s
= (char *)eat_whitespace_no_nl(headers
);
444 s
= (char *)find_whitespace(s
); /* get past GET/POST */
446 s
= (char *)eat_whitespace_no_nl(s
);
448 start
= s
; /* this is it, assuming it's valid */
449 s
= (char *)find_whitespace(start
);
452 /* tolerate the http[s] proxy style of putting the hostname in the url */
453 if (s
-start
>= 4 && !strcmpstart(start
,"http")) {
457 if (s
-tmp
>= 3 && !strcmpstart(tmp
,"://")) {
458 tmp
= strchr(tmp
+3, '/');
459 if (tmp
&& tmp
< s
) {
460 log_fn(LOG_DEBUG
,"Skipping over 'http[s]://hostname' string");
466 if (s
-start
< 5 || strcmpstart(start
,"/tor/")) { /* need to rewrite it */
467 *url
= tor_malloc(s
- start
+ 5);
468 strlcpy(*url
,"/tor", s
-start
+5);
469 strlcat((*url
)+4, start
, s
-start
+1);
471 *url
= tor_strndup(start
, s
-start
);
476 /** Parse an HTTP response string <b>headers</b> of the form
477 * "HTTP/1.\%d \%d\%s\r\n...".
478 * If it's well-formed, assign *<b>code</b>, point and return 0.
479 * If <b>date</b> is provided, set *date to the Date header in the
480 * http headers, or 0 if no such header is found.
481 * Otherwise, return -1.
484 parse_http_response(const char *headers
, int *code
, time_t *date
,
488 char datestr
[RFC1123_TIME_LEN
+1];
489 smartlist_t
*parsed_headers
;
493 while (TOR_ISSPACE(*headers
)) headers
++; /* tolerate leading whitespace */
495 if (sscanf(headers
, "HTTP/1.%d %d", &n1
, &n2
) < 2 ||
496 (n1
!= 0 && n1
!= 1) ||
497 (n2
< 100 || n2
>= 600)) {
498 log_fn(LOG_WARN
,"Failed to parse header '%s'",headers
);
503 parsed_headers
= smartlist_create();
504 smartlist_split_string(parsed_headers
, headers
, "\n",
505 SPLIT_SKIP_SPACE
|SPLIT_IGNORE_BLANK
, -1);
508 SMARTLIST_FOREACH(parsed_headers
, const char *, s
,
509 if (!strcmpstart(s
, "Date: ")) {
510 strlcpy(datestr
, s
+6, sizeof(datestr
));
511 /* This will do nothing on failure, so we don't need to check
512 the result. We shouldn't warn, since there are many other valid
513 date formats besides the one we use. */
514 parse_rfc1123_time(datestr
, date
);
519 const char *enc
= NULL
;
520 SMARTLIST_FOREACH(parsed_headers
, const char *, s
,
521 if (!strcmpstart(s
, "Content-Encoding: ")) {
524 if (!enc
|| !strcmp(enc
, "identity")) {
526 } else if (!strcmp(enc
, "deflate") || !strcmp(enc
, "x-deflate")) {
527 *compression
= ZLIB_METHOD
;
528 } else if (!strcmp(enc
, "gzip") || !strcmp(enc
, "x-gzip")) {
529 *compression
= GZIP_METHOD
;
531 log_fn(LOG_WARN
, "Unrecognized content encoding: '%s'", enc
);
535 SMARTLIST_FOREACH(parsed_headers
, char *, s
, tor_free(s
));
536 smartlist_free(parsed_headers
);
541 /** We are a client, and we've finished reading the server's
542 * response. Parse and it and act appropriately.
544 * Return -1 if an error has occurred, or 0 normally. The caller
545 * will take care of marking the connection for close.
548 connection_dir_client_reached_eof(connection_t
*conn
)
554 time_t now
, date_header
=0;
558 switch (fetch_from_buf_http(conn
->inbuf
,
559 &headers
, MAX_HEADERS_SIZE
,
560 &body
, &body_len
, MAX_DIR_SIZE
)) {
561 case -1: /* overflow */
562 log_fn(LOG_WARN
,"'fetch' response too large (server '%s'). Failing.", conn
->address
);
565 log_fn(LOG_INFO
,"'fetch' response not all here, but we're at eof. Closing.");
567 /* case 1, fall through */
570 if (parse_http_response(headers
, &status_code
, &date_header
,
572 log_fn(LOG_WARN
,"Unparseable headers (server '%s'). Closing.", conn
->address
);
573 tor_free(body
); tor_free(headers
);
576 if (date_header
> 0) {
578 delta
= now
-date_header
;
579 if (abs(delta
)>ALLOW_DIRECTORY_TIME_SKEW
) {
580 log_fn(LOG_WARN
, "Received directory with skewed time (server '%s'): we are %d minutes %s, or the directory is %d minutes %s.",
582 abs(delta
)/60, delta
>0 ? "ahead" : "behind",
583 abs(delta
)/60, delta
>0 ? "behind" : "ahead");
585 log_fn(LOG_INFO
, "Time on received directory is within tolerance; we are %d seconds skewed. (That's okay.)", delta
);
589 if (compression
!= 0) {
592 if (tor_gzip_uncompress(&new_body
, &new_len
, body
, body_len
, compression
)) {
593 log_fn(LOG_WARN
, "Unable to decompress HTTP body (server '%s').", conn
->address
);
594 tor_free(body
); tor_free(headers
);
602 if (conn
->purpose
== DIR_PURPOSE_FETCH_DIR
) {
603 /* fetch/process the directory to learn about new routers. */
604 log_fn(LOG_INFO
,"Received directory (size %d) from server '%s'",
605 (int)body_len
, conn
->address
);
606 if (status_code
== 503 || body_len
== 0) {
607 log_fn(LOG_INFO
,"Empty directory. Ignoring.");
608 tor_free(body
); tor_free(headers
);
611 if (status_code
!= 200) {
612 log_fn(LOG_WARN
,"Received http status code %d from server '%s'. Failing.",
613 status_code
, conn
->address
);
614 tor_free(body
); tor_free(headers
);
617 if (router_load_routerlist_from_directory(body
, NULL
, 1, 0) < 0) {
618 log_fn(LOG_WARN
,"I failed to parse the directory I fetched from %s:%d. Ignoring.", conn
->address
, conn
->port
);
620 log_fn(LOG_INFO
,"updated routers.");
622 directory_has_arrived(time(NULL
)); /* do things we've been waiting to do */
625 if (conn
->purpose
== DIR_PURPOSE_FETCH_RUNNING_LIST
) {
626 running_routers_t
*rrs
;
628 /* just update our list of running routers, if this list is new info */
629 log_fn(LOG_INFO
,"Received running-routers list (size %d)", (int)body_len
);
630 if (status_code
!= 200) {
631 log_fn(LOG_WARN
,"Received http status code %d from server '%s'. Failing.",
632 status_code
, conn
->address
);
633 tor_free(body
); tor_free(headers
);
636 if (!(rrs
= router_parse_runningrouters(body
, 1))) {
637 log_fn(LOG_WARN
, "Can't parse runningrouters list (server '%s')", conn
->address
);
638 tor_free(body
); tor_free(headers
);
641 router_get_routerlist(&rl
);
643 routerlist_update_from_runningrouters(rl
,rrs
);
644 running_routers_free(rrs
);
647 if (conn
->purpose
== DIR_PURPOSE_UPLOAD_DIR
) {
648 switch (status_code
) {
650 log_fn(LOG_INFO
,"eof (status 200) after uploading server descriptor: finished.");
653 log_fn(LOG_WARN
,"http status 400 (bad request) response from dirserver '%s'. Malformed server descriptor?", conn
->address
);
656 log_fn(LOG_WARN
,"http status 403 (unapproved server) response from dirserver '%s'. Is your clock skewed? Have you mailed us your key fingerprint? Are you using the right key? Are you using a private IP address? See http://tor.eff.org/doc/tor-doc.html#server.", conn
->address
);
659 log_fn(LOG_WARN
,"http status %d response unrecognized (server '%s').", status_code
, conn
->address
);
664 if (conn
->purpose
== DIR_PURPOSE_FETCH_RENDDESC
) {
665 log_fn(LOG_INFO
,"Received rendezvous descriptor (size %d, status code %d)",
666 (int)body_len
, status_code
);
667 switch (status_code
) {
669 if (rend_cache_store(body
, body_len
) < 0) {
670 log_fn(LOG_WARN
,"Failed to store rendezvous descriptor.");
671 /* alice's ap_stream will notice when connection_mark_for_close
674 /* success. notify pending connections about this. */
675 rend_client_desc_fetched(conn
->rend_query
, 1);
676 conn
->purpose
= DIR_PURPOSE_HAS_FETCHED_RENDDESC
;
680 /* not there. pending connections will be notified when
681 * connection_mark_for_close cleans it up. */
684 log_fn(LOG_WARN
,"http status 400 (bad request). Dirserver didn't like our rendezvous query?");
689 if (conn
->purpose
== DIR_PURPOSE_UPLOAD_RENDDESC
) {
690 switch (status_code
) {
692 log_fn(LOG_INFO
,"eof (status 200) after uploading rendezvous descriptor: finished.");
695 log_fn(LOG_WARN
,"http status 400 (bad request) response from dirserver. Malformed rendezvous descriptor?");
698 log_fn(LOG_WARN
,"http status %d response unrecognized.", status_code
);
702 tor_free(body
); tor_free(headers
);
706 int connection_dir_reached_eof(connection_t
*conn
) {
708 if (conn
->state
!= DIR_CONN_STATE_CLIENT_READING
) {
709 log_fn(LOG_INFO
,"conn reached eof, not reading. Closing.");
710 connection_close_immediate(conn
); /* it was an error; give up on flushing */
711 connection_mark_for_close(conn
);
715 retval
= connection_dir_client_reached_eof(conn
);
716 connection_mark_for_close(conn
);
720 /** Read handler for directory connections. (That's connections <em>to</em>
721 * directory servers and connections <em>at</em> directory servers.)
723 int connection_dir_process_inbuf(connection_t
*conn
) {
726 tor_assert(conn
->type
== CONN_TYPE_DIR
);
728 /* Directory clients write, then read data until they receive EOF;
729 * directory servers read data until they get an HTTP command, then
730 * write their response (when it's finished flushing, they mark for
734 /* If we're on the dirserver side, look for a command. */
735 if (conn
->state
== DIR_CONN_STATE_SERVER_COMMAND_WAIT
) {
736 if (directory_handle_command(conn
) < 0) {
737 connection_mark_for_close(conn
);
743 /* XXX for READ states, might want to make sure inbuf isn't too big */
745 log_fn(LOG_DEBUG
,"Got data, not eof. Leaving on inbuf.");
749 static char answer200
[] = "HTTP/1.0 200 OK\r\n\r\n";
750 static char answer400
[] = "HTTP/1.0 400 Bad request\r\n\r\n";
751 static char answer403
[] = "HTTP/1.0 403 Unapproved server\r\n\r\n";
752 static char answer404
[] = "HTTP/1.0 404 Not found\r\n\r\n";
753 static char answer503
[] = "HTTP/1.0 503 Directory unavailable\r\n\r\n";
755 /** Helper function: called when a dirserver gets a complete HTTP GET
756 * request. Look for a request for a directory or for a rendezvous
757 * service descriptor. On finding one, write a response into
758 * conn-\>outbuf. If the request is unrecognized, send a 400.
759 * Always return 0. */
761 directory_handle_command_get(connection_t
*conn
, char *headers
,
762 char *body
, size_t body_len
)
768 char date
[RFC1123_TIME_LEN
+1];
770 log_fn(LOG_DEBUG
,"Received GET command.");
772 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
774 if (parse_http_url(headers
, &url
) < 0) {
775 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
778 log_fn(LOG_INFO
,"rewritten url as '%s'.", url
);
780 if (!strcmp(url
,"/tor/") || !strcmp(url
,"/tor/dir.z")) { /* directory fetch */
781 int deflated
= !strcmp(url
,"/tor/dir.z");
782 dlen
= dirserv_get_directory(&cp
, deflated
);
787 log_fn(LOG_NOTICE
,"My directory is empty. Closing.");
788 connection_write_to_buf(answer503
, strlen(answer503
), conn
);
792 log_fn(LOG_DEBUG
,"Dumping %sdirectory to client.",
793 deflated
?"deflated ":"");
794 format_rfc1123_time(date
, time(NULL
));
795 tor_snprintf(tmp
, sizeof(tmp
), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: text/plain\r\nContent-Encoding: %s\r\n\r\n",
798 deflated
?"deflate":"identity");
799 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
800 connection_write_to_buf(cp
, dlen
, conn
);
804 if (!strcmp(url
,"/tor/running-routers") ||
805 !strcmp(url
,"/tor/running-routers.z")) { /* running-routers fetch */
806 int deflated
= !strcmp(url
,"/tor/dir.z");
808 dlen
= dirserv_get_runningrouters(&cp
, deflated
);
809 if (!dlen
) { /* we failed to create/cache cp */
810 connection_write_to_buf(answer503
, strlen(answer503
), conn
);
814 format_rfc1123_time(date
, time(NULL
));
815 tor_snprintf(tmp
, sizeof(tmp
), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: text/plain\r\nContent-Encoding: %s\r\n\r\n",
818 deflated
?"deflate":"identity");
819 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
820 connection_write_to_buf(cp
, strlen(cp
), conn
);
824 if (!strcmpstart(url
,"/tor/rendezvous/")) {
825 /* rendezvous descriptor fetch */
829 if (!authdir_mode(get_options())) {
830 /* We don't hand out rend descs. In fact, it could be a security
831 * risk, since rend_cache_lookup_desc() below would provide it
832 * if we're gone to the site recently, and 404 if we haven't.
835 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
839 switch (rend_cache_lookup_desc(url
+strlen("/tor/rendezvous/"), &descp
, &desc_len
)) {
841 format_rfc1123_time(date
, time(NULL
));
842 tor_snprintf(tmp
, sizeof(tmp
), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: application/octet-stream\r\n\r\n",
844 (int)desc_len
); /* can't include descp here, because it's got nuls */
845 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
846 connection_write_to_buf(descp
, desc_len
, conn
);
848 case 0: /* well-formed but not present */
849 connection_write_to_buf(answer404
, strlen(answer404
), conn
);
851 case -1: /* not well-formed */
852 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
859 /* we didn't recognize the url */
860 connection_write_to_buf(answer404
, strlen(answer404
), conn
);
865 /** Helper function: called when a dirserver gets a complete HTTP POST
866 * request. Look for an uploaded server descriptor or rendezvous
867 * service descriptor. On finding one, process it and write a
868 * response into conn-\>outbuf. If the request is unrecognized, send a
869 * 400. Always return 0. */
871 directory_handle_command_post(connection_t
*conn
, char *headers
,
872 char *body
, size_t body_len
)
877 log_fn(LOG_DEBUG
,"Received POST command.");
879 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
881 if (!authdir_mode(get_options())) {
882 /* we just provide cached directories; we don't want to
883 * receive anything. */
884 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
888 if (parse_http_url(headers
, &url
) < 0) {
889 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
892 log_fn(LOG_INFO
,"rewritten url as '%s'.", url
);
894 if (!strcmp(url
,"/tor/")) { /* server descriptor post */
896 switch (dirserv_add_descriptor(&cp
)) {
898 /* malformed descriptor, or something wrong */
899 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
902 /* descriptor was well-formed but server has not been approved */
903 connection_write_to_buf(answer403
, strlen(answer403
), conn
);
906 dirserv_get_directory(&cp
, 0); /* rebuild and write to disk */
907 connection_write_to_buf(answer200
, strlen(answer200
), conn
);
914 if (!strcmpstart(url
,"/tor/rendezvous/publish")) {
915 /* rendezvous descriptor post */
916 if (rend_cache_store(body
, body_len
) < 0)
917 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
919 connection_write_to_buf(answer200
, strlen(answer200
), conn
);
924 /* we didn't recognize the url */
925 connection_write_to_buf(answer404
, strlen(answer404
), conn
);
930 /** Called when a dirserver receives data on a directory connection;
931 * looks for an HTTP request. If the request is complete, remove it
932 * from the inbuf, try to process it; otherwise, leave it on the
933 * buffer. Return a 0 on success, or -1 on error.
935 static int directory_handle_command(connection_t
*conn
) {
936 char *headers
=NULL
, *body
=NULL
;
941 tor_assert(conn
->type
== CONN_TYPE_DIR
);
943 switch (fetch_from_buf_http(conn
->inbuf
,
944 &headers
, MAX_HEADERS_SIZE
,
945 &body
, &body_len
, MAX_BODY_SIZE
)) {
946 case -1: /* overflow */
947 log_fn(LOG_WARN
,"Invalid input. Closing.");
950 log_fn(LOG_DEBUG
,"command not all here yet.");
952 /* case 1, fall through */
955 log_fn(LOG_DEBUG
,"headers '%s', body '%s'.", headers
, body
);
957 if (!strncasecmp(headers
,"GET",3))
958 r
= directory_handle_command_get(conn
, headers
, body
, body_len
);
959 else if (!strncasecmp(headers
,"POST",4))
960 r
= directory_handle_command_post(conn
, headers
, body
, body_len
);
962 log_fn(LOG_WARN
,"Got headers '%s' with unknown command. Closing.", headers
);
966 tor_free(headers
); tor_free(body
);
970 /** Write handler for directory connections; called when all data has
971 * been flushed. Close the connection or wait for a response as
974 int connection_dir_finished_flushing(connection_t
*conn
) {
977 tor_assert(conn
->type
== CONN_TYPE_DIR
);
979 switch (conn
->state
) {
980 case DIR_CONN_STATE_CLIENT_SENDING
:
981 log_fn(LOG_DEBUG
,"client finished sending command.");
982 conn
->state
= DIR_CONN_STATE_CLIENT_READING
;
983 connection_stop_writing(conn
);
985 case DIR_CONN_STATE_SERVER_WRITING
:
986 log_fn(LOG_INFO
,"Finished writing server response. Closing.");
987 connection_mark_for_close(conn
);
990 log_fn(LOG_WARN
,"Bug: called in unexpected state %d.", conn
->state
);
996 /** Connected handler for directory connections: begin sending data to the
998 int connection_dir_finished_connecting(connection_t
*conn
)
1001 tor_assert(conn
->type
== CONN_TYPE_DIR
);
1002 tor_assert(conn
->state
== DIR_CONN_STATE_CONNECTING
);
1004 log_fn(LOG_INFO
,"Dir connection to router %s:%u established.",
1005 conn
->address
,conn
->port
);
1007 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING
; /* start flushing conn */