Normalize space: add one between every control keyword and control clause.
[tor.git] / src / or / directory.c
blob45327d5dfec291c13986e3ba5b7dde42eb9411b3
1 /* Copyright 2001-2004 Roger Dingledine.
2 * Copyright 2004 Roger Dingledine, Nick Mathewson. */
3 /* See LICENSE for licensing information */
5 /* See LICENSE for licensing information */
6 /* $Id$ */
8 #include "or.h"
10 /**
11 * \file directory.c
12 * \brief Implement directory HTTP protocol.
13 **/
15 /* In-points to directory.c:
17 * - directory_post_to_dirservers(), called from
18 * router_upload_dir_desc_to_dirservers() in router.c
19 * upload_service_descriptor() in rendservice.c
20 * - directory_get_from_dirserver(), called from
21 * rend_client_refetch_renddesc() in rendclient.c
22 * run_scheduled_events() in main.c
23 * do_hup() in main.c
24 * - connection_dir_process_inbuf(), called from
25 * connection_process_inbuf() in connection.c
26 * - connection_dir_finished_flushing(), called from
27 * connection_finished_flushing() in connection.c
28 * - connection_dir_finished_connecting(), called from
29 * connection_finished_connecting() in connection.c
32 static void
33 directory_initiate_command_router(routerinfo_t *router, uint8_t purpose,
34 const char *resource,
35 const char *payload, size_t payload_len);
36 static void
37 directory_initiate_command_trusted_dir(trusted_dir_server_t *dirserv,
38 uint8_t purpose, const char *resource,
39 const char *payload, size_t payload_len);
40 static void
41 directory_initiate_command(const char *address, uint32_t addr, uint16_t port,
42 const char *platform,
43 const char *digest, uint8_t purpose,
44 const char *resource,
45 const char *payload, size_t payload_len);
47 static void
48 directory_send_command(connection_t *conn, const char *platform,
49 int purpose, const char *resource,
50 const char *payload, size_t payload_len);
51 static int directory_handle_command(connection_t *conn);
53 /********* START VARIABLES **********/
55 static struct addr_policy_t *dir_policy = NULL;
57 #if 0 /* commented out for now, since for now what clients send is
58 different from what servers want to receive */
59 /** URL for publishing rendezvous descriptors. */
60 char rend_publish_string[] = "/tor/rendezvous/publish";
61 /** Prefix for downloading rendezvous descriptors. */
62 char rend_fetch_url[] = "/tor/rendezvous/";
63 #endif
65 #define MAX_HEADERS_SIZE 50000
66 #define MAX_BODY_SIZE 500000
68 #define ALLOW_DIRECTORY_TIME_SKEW 30*60
70 /********* END VARIABLES ************/
72 /** Parse get_options()->DirPolicy, and put the processed version in
73 * &dir_policy. Ignore port specifiers.
75 void
76 parse_dir_policy(void)
78 struct addr_policy_t *n;
79 if (dir_policy) {
80 addr_policy_free(dir_policy);
81 dir_policy = NULL;
83 config_parse_addr_policy(get_options()->DirPolicy, &dir_policy);
84 /* ports aren't used. */
85 for (n=dir_policy; n; n = n->next) {
86 n->prt_min = 1;
87 n->prt_max = 65535;
91 /** Return 1 if <b>addr</b> is permitted to connect to our dir port,
92 * based on <b>dir_policy</b>. Else return 0.
94 int dir_policy_permits_address(uint32_t addr)
96 int a;
98 if (!dir_policy) /* 'no dir policy' means 'accept' */
99 return 1;
100 a = router_compare_addr_to_addr_policy(addr, 1, dir_policy);
101 if (a==-1)
102 return 0;
103 else if (a==0)
104 return 1;
105 tor_assert(a==1);
106 log_fn(LOG_WARN, "Got unexpected 'maybe' answer from dir policy");
107 return 0;
110 /** Start a connection to every known directory server, using
111 * connection purpose 'purpose' and uploading the payload 'payload'
112 * (length 'payload_len'). The purpose should be one of
113 * 'DIR_PURPOSE_UPLOAD_DIR' or 'DIR_PURPOSE_UPLOAD_RENDDESC'.
115 void
116 directory_post_to_dirservers(uint8_t purpose, const char *payload,
117 size_t payload_len)
119 smartlist_t *dirservers;
120 char buf[16];
122 router_get_trusted_dir_servers(&dirservers);
123 tor_assert(dirservers);
124 /* This tries dirservers which we believe to be down, but ultimately, that's
125 * harmless, and we may as well err on the side of getting things uploaded.
127 SMARTLIST_FOREACH(dirservers, trusted_dir_server_t *, ds,
129 /* Pay attention to fascistfirewall when we're uploading a
130 * router descriptor, but not when uploading a service
131 * descriptor -- those use Tor. */
132 if (get_options()->FascistFirewall && purpose == DIR_PURPOSE_UPLOAD_DIR &&
133 !get_options()->HttpProxy) {
134 tor_snprintf(buf,sizeof(buf),"%d",ds->dir_port);
135 if (!smartlist_string_isin(get_options()->FirewallPorts, buf))
136 continue;
138 directory_initiate_command_trusted_dir(ds, purpose, NULL,
139 payload, payload_len);
143 /** Start a connection to a random running directory server, using
144 * connection purpose 'purpose' requesting 'resource'. The purpose
145 * should be one of 'DIR_PURPOSE_FETCH_DIR',
146 * 'DIR_PURPOSE_FETCH_RENDDESC', 'DIR_PURPOSE_FETCH_RUNNING_LIST.'
148 void
149 directory_get_from_dirserver(uint8_t purpose, const char *resource)
151 routerinfo_t *r = NULL;
152 trusted_dir_server_t *ds = NULL;
153 int fascistfirewall = get_options()->FascistFirewall;
155 if (purpose == DIR_PURPOSE_FETCH_DIR) {
156 if (advertised_server_mode()) {
157 /* only ask authdirservers, and don't ask myself */
158 ds = router_pick_trusteddirserver(1, fascistfirewall);
159 } else {
160 /* anybody with a non-zero dirport will do */
161 r = router_pick_directory_server(1, fascistfirewall);
162 if (!r) {
163 log_fn(LOG_INFO, "No router found for directory; falling back to dirserver list");
164 ds = router_pick_trusteddirserver(1, fascistfirewall);
167 } else if (purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
168 /* right now, running-routers isn't cached, so ask a trusted directory */
169 ds = router_pick_trusteddirserver(0, fascistfirewall);
170 } else { // (purpose == DIR_PURPOSE_FETCH_RENDDESC)
171 /* only ask authdirservers, any of them will do */
172 /* Never use fascistfirewall; we're going via Tor. */
173 ds = router_pick_trusteddirserver(0, 0);
176 if (r)
177 directory_initiate_command_router(r, purpose, resource, NULL, 0);
178 else if (ds)
179 directory_initiate_command_trusted_dir(ds, purpose, resource, NULL, 0);
180 else
181 log_fn(LOG_WARN,"No running dirservers known. Not trying. (purpose %d)", purpose);
184 /** Launch a new connection to the directory server <b>router</b> to upload or
185 * download a service or rendezvous descriptor. <b>purpose</b> determines what
186 * kind of directory connection we're launching, and must be one of
187 * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC}.
189 * When uploading, <b>payload</b> and <b>payload_len</b> determine the content
190 * of the HTTP post. Otherwise, <b>payload</b> should be NULL.
192 * When fetching a rendezvous descriptor, <b>resource</b> is the service ID we
193 * want to fetch.
195 static void
196 directory_initiate_command_router(routerinfo_t *router, uint8_t purpose,
197 const char *resource,
198 const char *payload, size_t payload_len)
200 directory_initiate_command(router->address, router->addr, router->dir_port,
201 router->platform, router->identity_digest,
202 purpose, resource, payload, payload_len);
205 /** As directory_initiate_command_router, but send the command to a trusted
206 * directory server <b>dirserv</b>. **/
207 static void
208 directory_initiate_command_trusted_dir(trusted_dir_server_t *dirserv,
209 uint8_t purpose, const char *resource,
210 const char *payload, size_t payload_len)
212 directory_initiate_command(dirserv->address, dirserv->addr,dirserv->dir_port,
213 NULL, dirserv->digest, purpose, resource, payload, payload_len);
216 /** Helper for directory_initiate_command(router|trusted_dir): send the
217 * command to a server whose address is <b>address</b>, whose IP is
218 * <b>addr</b>, whose directory port is <b>dir_port</b>, whose tor version is
219 * <b>platform</b>, and whose identity key digest is <b>digest</b>. The
220 * <b>platform</b> argument is optional; the others are required. */
221 static void
222 directory_initiate_command(const char *address, uint32_t addr,
223 uint16_t dir_port, const char *platform,
224 const char *digest, uint8_t purpose,
225 const char *resource,
226 const char *payload, size_t payload_len)
228 connection_t *conn;
230 tor_assert(address);
231 tor_assert(addr);
232 tor_assert(dir_port);
233 tor_assert(digest);
235 switch (purpose) {
236 case DIR_PURPOSE_FETCH_DIR:
237 log_fn(LOG_DEBUG,"initiating directory fetch");
238 break;
239 case DIR_PURPOSE_FETCH_RENDDESC:
240 log_fn(LOG_DEBUG,"initiating hidden-service descriptor fetch");
241 break;
242 case DIR_PURPOSE_UPLOAD_DIR:
243 log_fn(LOG_DEBUG,"initiating server descriptor upload");
244 break;
245 case DIR_PURPOSE_UPLOAD_RENDDESC:
246 log_fn(LOG_DEBUG,"initiating hidden-service descriptor upload");
247 break;
248 case DIR_PURPOSE_FETCH_RUNNING_LIST:
249 log_fn(LOG_DEBUG,"initiating running-routers fetch");
250 break;
251 default:
252 log_fn(LOG_ERR, "Unrecognized directory connection purpose.");
253 tor_assert(0);
256 conn = connection_new(CONN_TYPE_DIR);
258 /* set up conn so it's got all the data we need to remember */
259 conn->addr = addr;
260 conn->port = dir_port;
262 if (get_options()->HttpProxy) {
263 addr = get_options()->HttpProxyAddr;
264 dir_port = get_options()->HttpProxyPort;
267 conn->address = tor_strdup(address);
268 /* conn->nickname = tor_strdup(router->nickname); */
269 /* tor_assert(router->identity_pkey); */
270 /* conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); */
271 /* crypto_pk_get_digest(conn->identity_pkey, conn->identity_digest); */
272 memcpy(conn->identity_digest, digest, DIGEST_LEN);
274 conn->purpose = purpose;
276 /* give it an initial state */
277 conn->state = DIR_CONN_STATE_CONNECTING;
279 if (purpose == DIR_PURPOSE_FETCH_DIR ||
280 purpose == DIR_PURPOSE_UPLOAD_DIR ||
281 purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
282 /* then we want to connect directly */
283 switch (connection_connect(conn, conn->address, addr, dir_port)) {
284 case -1:
285 router_mark_as_down(conn->identity_digest); /* don't try him again */
286 if (purpose == DIR_PURPOSE_FETCH_DIR &&
287 !all_trusted_directory_servers_down()) {
288 log_fn(LOG_INFO,"Giving up on dirserver '%s'; trying another.", conn->address);
289 directory_get_from_dirserver(purpose, NULL);
291 connection_free(conn);
292 return;
293 case 1:
294 conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
295 /* fall through */
296 case 0:
297 /* queue the command on the outbuf */
298 directory_send_command(conn, platform, purpose, resource,
299 payload, payload_len);
300 connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
301 /* writable indicates finish, readable indicates broken link,
302 error indicates broken link in windowsland. */
304 } else { /* we want to connect via tor */
305 /* make an AP connection
306 * populate it and add it at the right state
307 * socketpair and hook up both sides
309 conn->s = connection_ap_make_bridge(conn->address, conn->port);
310 if (conn->s < 0) {
311 log_fn(LOG_WARN,"Making AP bridge to dirserver failed.");
312 connection_mark_for_close(conn);
313 return;
316 conn->state = DIR_CONN_STATE_CLIENT_SENDING;
317 connection_add(conn);
318 /* queue the command on the outbuf */
319 directory_send_command(conn, platform, purpose, resource,
320 payload, payload_len);
321 connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
325 /** Queue an appropriate HTTP command on conn-\>outbuf. The other args
326 * are as in directory_initiate_command.
328 static void
329 directory_send_command(connection_t *conn, const char *platform,
330 int purpose, const char *resource,
331 const char *payload, size_t payload_len) {
332 char tmp[8192];
333 char proxystring[128];
334 char hoststring[128];
335 char url[128];
336 int use_newer = 0;
337 const char *httpcommand = NULL;
339 tor_assert(conn);
340 tor_assert(conn->type == CONN_TYPE_DIR);
342 /* If we don't know the platform, assume it's up-to-date. */
343 use_newer = platform ? tor_version_as_new_as(platform, "0.0.9pre1"):1;
345 if (conn->port == 80) {
346 strlcpy(hoststring, conn->address, sizeof(hoststring));
347 } else {
348 tor_snprintf(hoststring, sizeof(hoststring),"%s:%d",conn->address, conn->port);
350 if (get_options()->HttpProxy) {
351 tor_snprintf(proxystring, sizeof(proxystring),"http://%s", hoststring);
352 } else {
353 proxystring[0] = 0;
356 switch (purpose) {
357 case DIR_PURPOSE_FETCH_DIR:
358 tor_assert(!resource);
359 tor_assert(!payload);
360 log_fn(LOG_DEBUG, "Asking for %scompressed directory from server running %s",
361 use_newer?"":"un", platform?platform:"<unknown version>");
362 httpcommand = "GET";
363 strlcpy(url, use_newer ? "/tor/dir.z" : "/", sizeof(url));
364 break;
365 case DIR_PURPOSE_FETCH_RUNNING_LIST:
366 tor_assert(!resource);
367 tor_assert(!payload);
368 httpcommand = "GET";
369 strlcpy(url, use_newer ? "/tor/running-routers" : "/running-routers", sizeof(url));
370 break;
371 case DIR_PURPOSE_UPLOAD_DIR:
372 tor_assert(!resource);
373 tor_assert(payload);
374 httpcommand = "POST";
375 strlcpy(url, use_newer ? "/tor/" : "/", sizeof(url));
376 break;
377 case DIR_PURPOSE_FETCH_RENDDESC:
378 tor_assert(resource);
379 tor_assert(!payload);
381 /* this must be true or we wouldn't be doing the lookup */
382 tor_assert(strlen(resource) <= REND_SERVICE_ID_LEN);
383 /* This breaks the function abstraction. */
384 strlcpy(conn->rend_query, resource, sizeof(conn->rend_query));
386 httpcommand = "GET";
387 tor_snprintf(url, sizeof(url), "%s/rendezvous/%s", use_newer ? "/tor" : "", resource);
389 break;
390 case DIR_PURPOSE_UPLOAD_RENDDESC:
391 tor_assert(!resource);
392 tor_assert(payload);
393 httpcommand = "POST";
394 tor_snprintf(url, sizeof(url), "%s/rendezvous/publish", use_newer ? "/tor" : "");
395 break;
398 tor_snprintf(tmp, sizeof(tmp), "%s %s%s HTTP/1.0\r\nContent-Length: %lu\r\nHost: %s\r\n\r\n",
399 httpcommand,
400 proxystring,
401 url,
402 payload ? (unsigned long)payload_len : 0,
403 hoststring);
404 connection_write_to_buf(tmp, strlen(tmp), conn);
406 if (payload) {
407 /* then send the payload afterwards too */
408 connection_write_to_buf(payload, payload_len, conn);
412 /** Parse an HTTP request string <b>headers</b> of the form
413 * "\%s [http[s]://]\%s HTTP/1..."
414 * If it's well-formed, strdup the second \%s into *<b>url</b>, and
415 * null-terminate it. If the url doesn't start with "/tor/", rewrite it
416 * so it does. Return 0.
417 * Otherwise, return -1.
419 static int
420 parse_http_url(char *headers, char **url)
422 char *s, *start, *tmp;
424 s = (char *)eat_whitespace_no_nl(headers);
425 if (!*s) return -1;
426 s = (char *)find_whitespace(s); /* get past GET/POST */
427 if (!*s) return -1;
428 s = (char *)eat_whitespace_no_nl(s);
429 if (!*s) return -1;
430 start = s; /* this is it, assuming it's valid */
431 s = (char *)find_whitespace(start);
432 if (!*s) return -1;
434 /* tolerate the http[s] proxy style of putting the hostname in the url */
435 if (s-start >= 4 && !strcmpstart(start,"http")) {
436 tmp = start + 4;
437 if (*tmp == 's')
438 tmp++;
439 if (s-tmp >= 3 && !strcmpstart(tmp,"://")) {
440 tmp = strchr(tmp+3, '/');
441 if (tmp && tmp < s) {
442 log_fn(LOG_DEBUG,"Skipping over 'http[s]://hostname' string");
443 start = tmp;
448 if (s-start < 5 || strcmpstart(start,"/tor/")) { /* need to rewrite it */
449 *url = tor_malloc(s - start + 5);
450 strlcpy(*url,"/tor", s-start+5);
451 strlcat((*url)+4, start, s-start+1);
452 } else {
453 *url = tor_strndup(start, s-start);
455 return 0;
458 /** Parse an HTTP response string <b>headers</b> of the form
459 * "HTTP/1.\%d \%d\%s\r\n...".
460 * If it's well-formed, assign *<b>code</b>, point and return 0.
461 * If <b>date</b> is provided, set *date to the Date header in the
462 * http headers, or 0 if no such header is found.
463 * Otherwise, return -1.
465 static int
466 parse_http_response(const char *headers, int *code, time_t *date,
467 int *compression)
469 int n1, n2;
470 char datestr[RFC1123_TIME_LEN+1];
471 smartlist_t *parsed_headers;
472 tor_assert(headers);
473 tor_assert(code);
475 while (isspace((int)*headers)) headers++; /* tolerate leading whitespace */
477 if (sscanf(headers, "HTTP/1.%d %d", &n1, &n2) < 2 ||
478 (n1 != 0 && n1 != 1) ||
479 (n2 < 100 || n2 >= 600)) {
480 log_fn(LOG_WARN,"Failed to parse header '%s'",headers);
481 return -1;
483 *code = n2;
485 parsed_headers = smartlist_create();
486 smartlist_split_string(parsed_headers, headers, "\n",
487 SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
488 if (date) {
489 *date = 0;
490 SMARTLIST_FOREACH(parsed_headers, const char *, s,
491 if (!strcmpstart(s, "Date: ")) {
492 strlcpy(datestr, s+6, sizeof(datestr));
493 /* This will do nothing on failure, so we don't need to check
494 the result. We shouldn't warn, since there are many other valid
495 date formats besides the one we use. */
496 parse_rfc1123_time(datestr, date);
497 break;
500 if (compression) {
501 const char *enc = NULL;
502 SMARTLIST_FOREACH(parsed_headers, const char *, s,
503 if (!strcmpstart(s, "Content-Encoding: ")) {
504 enc = s+18; break;
506 if (!enc || !strcmp(enc, "identity")) {
507 *compression = 0;
508 } else if (!strcmp(enc, "deflate") || !strcmp(enc, "x-deflate")) {
509 *compression = ZLIB_METHOD;
510 } else if (!strcmp(enc, "gzip") || !strcmp(enc, "x-gzip")) {
511 *compression = GZIP_METHOD;
512 } else {
513 log_fn(LOG_WARN, "Unrecognized content encoding: '%s'", enc);
514 *compression = 0;
517 SMARTLIST_FOREACH(parsed_headers, char *, s, tor_free(s));
518 smartlist_free(parsed_headers);
520 return 0;
523 /** We are a client, and we've finished reading the server's
524 * response. Parse and it and act appropriately.
526 * Return -1 if an error has occurred, or 0 normally. The caller
527 * will take care of marking the connection for close.
529 static int
530 connection_dir_client_reached_eof(connection_t *conn)
532 char *body;
533 char *headers;
534 size_t body_len=0;
535 int status_code;
536 time_t now, date_header=0;
537 int delta;
538 int compression;
540 switch (fetch_from_buf_http(conn->inbuf,
541 &headers, MAX_HEADERS_SIZE,
542 &body, &body_len, MAX_DIR_SIZE)) {
543 case -1: /* overflow */
544 log_fn(LOG_WARN,"'fetch' response too large. Failing.");
545 return -1;
546 case 0:
547 log_fn(LOG_INFO,"'fetch' response not all here, but we're at eof. Closing.");
548 return -1;
549 /* case 1, fall through */
552 if (parse_http_response(headers, &status_code, &date_header,
553 &compression) < 0) {
554 log_fn(LOG_WARN,"Unparseable headers. Closing.");
555 tor_free(body); tor_free(headers);
556 return -1;
558 if (date_header > 0) {
559 now = time(NULL);
560 delta = now-date_header;
561 if (abs(delta)>ALLOW_DIRECTORY_TIME_SKEW) {
562 log_fn(LOG_WARN, "Received directory with skewed time: we are %d minutes %s, or the directory is %d minutes %s.",
563 abs(delta)/60, delta>0 ? "ahead" : "behind",
564 abs(delta)/60, delta>0 ? "behind" : "ahead");
565 } else {
566 log_fn(LOG_INFO, "Time on received directory is within tolerance; we are %d seconds skewed. (That's okay.)", delta);
570 if (compression != 0) {
571 char *new_body;
572 size_t new_len;
573 if (tor_gzip_uncompress(&new_body, &new_len, body, body_len, compression)) {
574 log_fn(LOG_WARN, "Unable to decompress HTTP body.");
575 tor_free(body); tor_free(headers);
576 return -1;
578 tor_free(body);
579 body = new_body;
580 body_len = new_len;
583 if (conn->purpose == DIR_PURPOSE_FETCH_DIR) {
584 /* fetch/process the directory to learn about new routers. */
585 log_fn(LOG_INFO,"Received directory (size %d):\n%s", (int)body_len, body);
586 if (status_code == 503 || body_len == 0) {
587 log_fn(LOG_INFO,"Empty directory. Ignoring.");
588 tor_free(body); tor_free(headers);
589 return 0;
591 if (status_code != 200) {
592 log_fn(LOG_WARN,"Received http status code %d from dirserver. Failing.",
593 status_code);
594 tor_free(body); tor_free(headers);
595 return -1;
597 if (router_load_routerlist_from_directory(body, NULL, 1) < 0) {
598 log_fn(LOG_WARN,"I failed to parse the directory I fetched from %s:%d. Ignoring.", conn->address, conn->port);
599 } else {
600 log_fn(LOG_INFO,"updated routers.");
602 directory_has_arrived(time(NULL)); /* do things we've been waiting to do */
605 if (conn->purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
606 running_routers_t *rrs;
607 routerlist_t *rl;
608 /* just update our list of running routers, if this list is new info */
609 log_fn(LOG_INFO,"Received running-routers list (size %d):\n%s", (int)body_len, body);
610 if (status_code != 200) {
611 log_fn(LOG_WARN,"Received http status code %d from dirserver. Failing.",
612 status_code);
613 tor_free(body); tor_free(headers);
614 return -1;
616 if (!(rrs = router_parse_runningrouters(body))) {
617 log_fn(LOG_WARN, "Can't parse runningrouters list");
618 tor_free(body); tor_free(headers);
619 return -1;
621 router_get_routerlist(&rl);
622 if (rl)
623 routerlist_update_from_runningrouters(rl,rrs);
624 running_routers_free(rrs);
627 if (conn->purpose == DIR_PURPOSE_UPLOAD_DIR) {
628 switch (status_code) {
629 case 200:
630 log_fn(LOG_INFO,"eof (status 200) after uploading server descriptor: finished.");
631 break;
632 case 400:
633 log_fn(LOG_WARN,"http status 400 (bad request) response from dirserver. Malformed server descriptor?");
634 break;
635 case 403:
636 log_fn(LOG_WARN,"http status 403 (unapproved server) response from dirserver. Is your clock skewed? Have you mailed us your identity fingerprint? Are you using the right key? See README.");
637 break;
638 default:
639 log_fn(LOG_WARN,"http status %d response unrecognized.", status_code);
640 break;
644 if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC) {
645 log_fn(LOG_INFO,"Received rendezvous descriptor (size %d, status code %d)",
646 (int)body_len, status_code);
647 switch (status_code) {
648 case 200:
649 if (rend_cache_store(body, body_len) < 0) {
650 log_fn(LOG_WARN,"Failed to store rendezvous descriptor.");
651 /* alice's ap_stream will notice when connection_mark_for_close
652 * cleans it up */
653 } else {
654 /* success. notify pending connections about this. */
655 rend_client_desc_fetched(conn->rend_query, 1);
656 conn->purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
658 break;
659 case 404:
660 /* not there. pending connections will be notified when
661 * connection_mark_for_close cleans it up. */
662 break;
663 case 400:
664 log_fn(LOG_WARN,"http status 400 (bad request). Dirserver didn't like our rendezvous query?");
665 break;
669 if (conn->purpose == DIR_PURPOSE_UPLOAD_RENDDESC) {
670 switch (status_code) {
671 case 200:
672 log_fn(LOG_INFO,"eof (status 200) after uploading rendezvous descriptor: finished.");
673 break;
674 case 400:
675 log_fn(LOG_WARN,"http status 400 (bad request) response from dirserver. Malformed rendezvous descriptor?");
676 break;
677 default:
678 log_fn(LOG_WARN,"http status %d response unrecognized.", status_code);
679 break;
682 tor_free(body); tor_free(headers);
683 return 0;
686 int connection_dir_reached_eof(connection_t *conn) {
687 int retval;
688 if (conn->state != DIR_CONN_STATE_CLIENT_READING) {
689 log_fn(LOG_INFO,"conn reached eof, not reading. Closing.");
690 connection_close_immediate(conn); /* it was an error; give up on flushing */
691 connection_mark_for_close(conn);
692 return -1;
695 retval = connection_dir_client_reached_eof(conn);
696 connection_mark_for_close(conn);
697 return retval;
700 /** Read handler for directory connections. (That's connections <em>to</em>
701 * directory servers and connections <em>at</em> directory servers.)
703 int connection_dir_process_inbuf(connection_t *conn) {
705 tor_assert(conn);
706 tor_assert(conn->type == CONN_TYPE_DIR);
708 /* Directory clients write, then read data until they receive EOF;
709 * directory servers read data until they get an HTTP command, then
710 * write their response (when it's finished flushing, they mark for
711 * close).
714 /* If we're on the dirserver side, look for a command. */
715 if (conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) {
716 if (directory_handle_command(conn) < 0) {
717 connection_mark_for_close(conn);
718 return -1;
720 return 0;
723 /* XXX for READ states, might want to make sure inbuf isn't too big */
725 log_fn(LOG_DEBUG,"Got data, not eof. Leaving on inbuf.");
726 return 0;
729 static char answer200[] = "HTTP/1.0 200 OK\r\n\r\n";
730 static char answer400[] = "HTTP/1.0 400 Bad request\r\n\r\n";
731 static char answer403[] = "HTTP/1.0 403 Unapproved server\r\n\r\n";
732 static char answer404[] = "HTTP/1.0 404 Not found\r\n\r\n";
733 static char answer503[] = "HTTP/1.0 503 Directory unavailable\r\n\r\n";
735 /** Helper function: called when a dirserver gets a complete HTTP GET
736 * request. Look for a request for a directory or for a rendezvous
737 * service descriptor. On finding one, write a response into
738 * conn-\>outbuf. If the request is unrecognized, send a 400.
739 * Always return 0. */
740 static int
741 directory_handle_command_get(connection_t *conn, char *headers,
742 char *body, size_t body_len)
744 size_t dlen;
745 const char *cp;
746 char *url;
747 char tmp[8192];
748 char date[RFC1123_TIME_LEN+1];
750 log_fn(LOG_DEBUG,"Received GET command.");
752 conn->state = DIR_CONN_STATE_SERVER_WRITING;
754 if (parse_http_url(headers, &url) < 0) {
755 connection_write_to_buf(answer400, strlen(answer400), conn);
756 return 0;
758 log_fn(LOG_INFO,"rewritten url as '%s'.", url);
760 if (!strcmp(url,"/tor/") || !strcmp(url,"/tor/dir.z")) { /* directory fetch */
761 int deflated = !strcmp(url,"/tor/dir.z");
762 dlen = dirserv_get_directory(&cp, deflated);
764 tor_free(url);
766 if (dlen == 0) {
767 log_fn(LOG_WARN,"My directory is empty. Closing.");
768 connection_write_to_buf(answer503, strlen(answer503), conn);
769 return 0;
772 log_fn(LOG_DEBUG,"Dumping %sdirectory to client.",
773 deflated?"deflated ":"");
774 format_rfc1123_time(date, time(NULL));
775 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",
776 date,
777 (int)dlen,
778 deflated?"deflate":"identity");
779 connection_write_to_buf(tmp, strlen(tmp), conn);
780 connection_write_to_buf(cp, dlen, conn);
781 return 0;
784 if (!strcmp(url,"/tor/running-routers") ||
785 !strcmp(url,"/tor/running-routers.z")) { /* running-routers fetch */
786 int deflated = !strcmp(url,"/tor/dir.z");
787 tor_free(url);
788 if (!authdir_mode(get_options())) {
789 /* For now, we don't cache running-routers. Reject. */
790 connection_write_to_buf(answer400, strlen(answer400), conn);
791 return 0;
793 dlen = dirserv_get_runningrouters(&cp, deflated);
794 if (!dlen) { /* we failed to create/cache cp */
795 connection_write_to_buf(answer503, strlen(answer503), conn);
796 return 0;
799 format_rfc1123_time(date, time(NULL));
800 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",
801 date,
802 (int)dlen,
803 deflated?"deflate":"identity");
804 connection_write_to_buf(tmp, strlen(tmp), conn);
805 connection_write_to_buf(cp, strlen(cp), conn);
806 return 0;
809 if (!strcmpstart(url,"/tor/rendezvous/")) {
810 /* rendezvous descriptor fetch */
811 const char *descp;
812 size_t desc_len;
814 if (!authdir_mode(get_options())) {
815 /* We don't hand out rend descs. In fact, it could be a security
816 * risk, since rend_cache_lookup_desc() below would provide it
817 * if we're gone to the site recently, and 404 if we haven't.
819 * Reject. */
820 connection_write_to_buf(answer400, strlen(answer400), conn);
821 tor_free(url);
822 return 0;
824 switch (rend_cache_lookup_desc(url+strlen("/tor/rendezvous/"), &descp, &desc_len)) {
825 case 1: /* valid */
826 format_rfc1123_time(date, time(NULL));
827 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",
828 date,
829 (int)desc_len); /* can't include descp here, because it's got nuls */
830 connection_write_to_buf(tmp, strlen(tmp), conn);
831 connection_write_to_buf(descp, desc_len, conn);
832 break;
833 case 0: /* well-formed but not present */
834 connection_write_to_buf(answer404, strlen(answer404), conn);
835 break;
836 case -1: /* not well-formed */
837 connection_write_to_buf(answer400, strlen(answer400), conn);
838 break;
840 tor_free(url);
841 return 0;
844 /* we didn't recognize the url */
845 connection_write_to_buf(answer404, strlen(answer404), conn);
846 tor_free(url);
847 return 0;
850 /** Helper function: called when a dirserver gets a complete HTTP POST
851 * request. Look for an uploaded server descriptor or rendezvous
852 * service descriptor. On finding one, process it and write a
853 * response into conn-\>outbuf. If the request is unrecognized, send a
854 * 400. Always return 0. */
855 static int
856 directory_handle_command_post(connection_t *conn, char *headers,
857 char *body, size_t body_len)
859 const char *cp;
860 char *url;
862 log_fn(LOG_DEBUG,"Received POST command.");
864 conn->state = DIR_CONN_STATE_SERVER_WRITING;
866 if (!authdir_mode(get_options())) {
867 /* we just provide cached directories; we don't want to
868 * receive anything. */
869 connection_write_to_buf(answer400, strlen(answer400), conn);
870 return 0;
873 if (parse_http_url(headers, &url) < 0) {
874 connection_write_to_buf(answer400, strlen(answer400), conn);
875 return 0;
877 log_fn(LOG_INFO,"rewritten url as '%s'.", url);
879 if (!strcmp(url,"/tor/")) { /* server descriptor post */
880 cp = body;
881 switch (dirserv_add_descriptor(&cp)) {
882 case -1:
883 /* malformed descriptor, or something wrong */
884 connection_write_to_buf(answer400, strlen(answer400), conn);
885 break;
886 case 0:
887 /* descriptor was well-formed but server has not been approved */
888 connection_write_to_buf(answer403, strlen(answer403), conn);
889 break;
890 case 1:
891 dirserv_get_directory(&cp, 0); /* rebuild and write to disk */
892 connection_write_to_buf(answer200, strlen(answer200), conn);
893 break;
895 tor_free(url);
896 return 0;
899 if (!strcmpstart(url,"/tor/rendezvous/publish")) {
900 /* rendezvous descriptor post */
901 if (rend_cache_store(body, body_len) < 0)
902 connection_write_to_buf(answer400, strlen(answer400), conn);
903 else
904 connection_write_to_buf(answer200, strlen(answer200), conn);
905 tor_free(url);
906 return 0;
909 /* we didn't recognize the url */
910 connection_write_to_buf(answer404, strlen(answer404), conn);
911 tor_free(url);
912 return 0;
915 /** Called when a dirserver receives data on a directory connection;
916 * looks for an HTTP request. If the request is complete, remove it
917 * from the inbuf, try to process it; otherwise, leave it on the
918 * buffer. Return a 0 on success, or -1 on error.
920 static int directory_handle_command(connection_t *conn) {
921 char *headers=NULL, *body=NULL;
922 size_t body_len=0;
923 int r;
925 tor_assert(conn);
926 tor_assert(conn->type == CONN_TYPE_DIR);
928 switch (fetch_from_buf_http(conn->inbuf,
929 &headers, MAX_HEADERS_SIZE,
930 &body, &body_len, MAX_BODY_SIZE)) {
931 case -1: /* overflow */
932 log_fn(LOG_WARN,"Invalid input. Closing.");
933 return -1;
934 case 0:
935 log_fn(LOG_DEBUG,"command not all here yet.");
936 return 0;
937 /* case 1, fall through */
940 log_fn(LOG_DEBUG,"headers '%s', body '%s'.", headers, body);
942 if (!strncasecmp(headers,"GET",3))
943 r = directory_handle_command_get(conn, headers, body, body_len);
944 else if (!strncasecmp(headers,"POST",4))
945 r = directory_handle_command_post(conn, headers, body, body_len);
946 else {
947 log_fn(LOG_WARN,"Got headers '%s' with unknown command. Closing.", headers);
948 r = -1;
951 tor_free(headers); tor_free(body);
952 return r;
955 /** Write handler for directory connections; called when all data has
956 * been flushed. Close the connection or wait for a response as
957 * appropriate.
959 int connection_dir_finished_flushing(connection_t *conn) {
961 tor_assert(conn);
962 tor_assert(conn->type == CONN_TYPE_DIR);
964 switch (conn->state) {
965 case DIR_CONN_STATE_CLIENT_SENDING:
966 log_fn(LOG_DEBUG,"client finished sending command.");
967 conn->state = DIR_CONN_STATE_CLIENT_READING;
968 connection_stop_writing(conn);
969 return 0;
970 case DIR_CONN_STATE_SERVER_WRITING:
971 log_fn(LOG_INFO,"Finished writing server response. Closing.");
972 connection_mark_for_close(conn);
973 return 0;
974 default:
975 log_fn(LOG_WARN,"BUG: called in unexpected state %d.", conn->state);
976 return -1;
978 return 0;
981 /** Connected handler for directory connections: begin sending data to the
982 * server */
983 int connection_dir_finished_connecting(connection_t *conn)
985 tor_assert(conn);
986 tor_assert(conn->type == CONN_TYPE_DIR);
987 tor_assert(conn->state == DIR_CONN_STATE_CONNECTING);
989 log_fn(LOG_INFO,"Dir connection to router %s:%u established.",
990 conn->address,conn->port);
992 conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
993 return 0;