1 /* Copyright 2001,2002,2003 Roger Dingledine. */
2 /* See LICENSE for licensing information */
9 * \brief Implement directory HTTP protocol.
12 /* In-points to directory.c:
14 * - directory_post_to_dirservers(), called from
15 * router_upload_dir_desc_to_dirservers() in router.c
16 * upload_service_descriptor() in rendservice.c
17 * - directory_get_from_dirserver(), called from
18 * rend_client_refetch_renddesc() in rendclient.c
19 * run_scheduled_events() in main.c
21 * - connection_dir_process_inbuf(), called from
22 * connection_process_inbuf() in connection.c
23 * - connection_dir_finished_flushing(), called from
24 * connection_finished_flushing() in connection.c
25 * - connection_dir_finished_connecting(), called from
26 * connection_finished_connecting() in connection.c
30 directory_initiate_command(routerinfo_t
*router
, uint8_t purpose
,
31 const char *payload
, int payload_len
);
32 static void directory_send_command(connection_t
*conn
, int purpose
,
33 const char *payload
, int payload_len
);
34 static int directory_handle_command(connection_t
*conn
);
36 /********* START VARIABLES **********/
38 extern or_options_t options
; /* command-line and config-file options */
40 /** URL for publishing rendezvous descriptors. */
41 char rend_publish_string
[] = "/rendezvous/publish";
42 /** Prefix for downloading rendezvous descriptors. */
43 char rend_fetch_url
[] = "/rendezvous/";
45 #define MAX_HEADERS_SIZE 10000
46 #define MAX_BODY_SIZE 500000
48 /********* END VARIABLES ************/
50 /** Start a connection to every known directory server, using
51 * connection purpose 'purpose' and uploading the payload 'payload'
52 * (length 'payload_len'). The purpose should be one of
53 * 'DIR_PURPOSE_UPLOAD_DIR' or 'DIR_PURPOSE_UPLOAD_RENDDESC'.
56 directory_post_to_dirservers(uint8_t purpose
, const char *payload
,
63 router_get_routerlist(&rl
);
67 for(i
=0; i
< smartlist_len(rl
->routers
); i
++) {
68 router
= smartlist_get(rl
->routers
, i
);
69 if(router
->dir_port
> 0)
70 directory_initiate_command(router
, purpose
, payload
, payload_len
);
74 /** Start a connection to a random running directory server, using
75 * connection purpose 'purpose' requesting 'payload' (length
76 * 'payload_len'). The purpose should be one of
77 * 'DIR_PURPOSE_FETCH_DIR' or 'DIR_PURPOSE_FETCH_RENDDESC'.
80 directory_get_from_dirserver(uint8_t purpose
, const char *payload
,
83 directory_initiate_command(router_pick_directory_server(),
84 purpose
, payload
, payload_len
);
87 /** Launch a new connection to the directory server <b>router</b> to upload or
88 * download a service or rendezvous descriptor. <b>purpose</b> determines what
89 * kind of directory connection we're launching, and must be one of
90 * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC}.
92 * When uploading, <b>payload</b> and <b>payload_len</b> determine the content
93 * of the HTTP post. When fetching a rendezvous descriptor, <b>payload</b>
94 * and <b>payload_len</b> are the service ID we want to fetch.
97 directory_initiate_command(routerinfo_t
*router
, uint8_t purpose
,
98 const char *payload
, int payload_len
)
104 case DIR_PURPOSE_FETCH_DIR
:
105 log_fn(LOG_DEBUG
,"initiating directory fetch");
107 case DIR_PURPOSE_FETCH_RENDDESC
:
108 log_fn(LOG_DEBUG
,"initiating hidden-service descriptor fetch");
110 case DIR_PURPOSE_UPLOAD_DIR
:
111 log_fn(LOG_DEBUG
,"initiating server descriptor upload");
113 case DIR_PURPOSE_UPLOAD_RENDDESC
:
114 log_fn(LOG_DEBUG
,"initiating hidden-service descriptor upload");
117 log_fn(LOG_ERR
, "Unrecognized directory connection purpose.");
121 if (!router
) { /* i guess they didn't have one in mind for me to use */
122 log_fn(LOG_WARN
,"No running dirservers known. Not trying. (purpose %d)", purpose
);
126 conn
= connection_new(CONN_TYPE_DIR
);
128 /* set up conn so it's got all the data we need to remember */
129 conn
->addr
= router
->addr
;
130 conn
->port
= router
->dir_port
;
131 conn
->address
= tor_strdup(router
->address
);
132 conn
->nickname
= tor_strdup(router
->nickname
);
133 tor_assert(router
->identity_pkey
);
134 conn
->identity_pkey
= crypto_pk_dup_key(router
->identity_pkey
);
136 conn
->purpose
= purpose
;
138 /* give it an initial state */
139 conn
->state
= DIR_CONN_STATE_CONNECTING
;
141 if(purpose
== DIR_PURPOSE_FETCH_DIR
||
142 purpose
== DIR_PURPOSE_UPLOAD_DIR
) {
143 /* then we want to connect directly */
144 switch(connection_connect(conn
, conn
->address
, conn
->addr
, conn
->port
)) {
146 router_mark_as_down(conn
->nickname
); /* don't try him again */
147 connection_free(conn
);
150 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING
; /* start flushing conn */
153 /* queue the command on the outbuf */
154 directory_send_command(conn
, purpose
, payload
, payload_len
);
156 connection_watch_events(conn
, POLLIN
| POLLOUT
| POLLERR
);
157 /* writable indicates finish, readable indicates broken link,
158 error indicates broken link in windowsland. */
160 } else { /* we want to connect via tor */
161 /* make an AP connection
162 * populate it and add it at the right state
163 * socketpair and hook up both sides
165 conn
->s
= connection_ap_make_bridge(conn
->address
, conn
->port
);
167 log_fn(LOG_WARN
,"Making AP bridge to dirserver failed.");
168 connection_mark_for_close(conn
);
172 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING
;
173 connection_add(conn
);
174 /* queue the command on the outbuf */
175 directory_send_command(conn
, purpose
, payload
, payload_len
);
176 connection_watch_events(conn
, POLLIN
| POLLOUT
| POLLERR
);
180 /** Queue an appropriate HTTP command on conn-\>outbuf. The args
181 * <b>purpose</b>, <b>payload</b>, and <b>payload_len</b> are as in
182 * directory_initiate_command.
184 static void directory_send_command(connection_t
*conn
, int purpose
,
185 const char *payload
, int payload_len
) {
186 char fetchstring
[] = "GET / HTTP/1.0\r\n\r\n";
189 tor_assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
192 case DIR_PURPOSE_FETCH_DIR
:
193 tor_assert(payload
== NULL
);
194 connection_write_to_buf(fetchstring
, strlen(fetchstring
), conn
);
196 case DIR_PURPOSE_UPLOAD_DIR
:
198 snprintf(tmp
, sizeof(tmp
), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n",
200 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
201 connection_write_to_buf(payload
, payload_len
, conn
);
203 case DIR_PURPOSE_FETCH_RENDDESC
:
206 /* this must be true or we wouldn't be doing the lookup */
207 tor_assert(payload_len
<= REND_SERVICE_ID_LEN
);
208 /* This breaks the function abstraction. */
209 memcpy(conn
->rend_query
, payload
, payload_len
);
210 conn
->rend_query
[payload_len
] = 0;
212 snprintf(tmp
, sizeof(tmp
), "GET %s%s HTTP/1.0\r\n\r\n", rend_fetch_url
, payload
);
213 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
215 case DIR_PURPOSE_UPLOAD_RENDDESC
:
217 snprintf(tmp
, sizeof(tmp
),
218 "POST %s HTTP/1.0\r\nContent-Length: %d\r\n\r\n", rend_publish_string
, payload_len
);
219 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
220 /* could include nuls, need to write it separately */
221 connection_write_to_buf(payload
, payload_len
, conn
);
226 /** Parse an HTTP request string <b>headers</b> of the form "\%s \%s HTTP/1..."
227 * If it's well-formed, point *<b>url</b> to the second \%s,
228 * null-terminate it (this modifies headers!) and return 0.
229 * Otherwise, return -1.
232 parse_http_url(char *headers
, char **url
)
236 s
= (char *)eat_whitespace_no_nl(headers
);
238 s
= (char *)find_whitespace(s
); /* get past GET/POST */
240 s
= (char *)eat_whitespace_no_nl(s
);
242 tmp
= s
; /* this is it, assuming it's valid */
243 s
= (char *)find_whitespace(s
);
250 /** Parse an HTTP response string <b>headers</b> of the form
251 * "HTTP/1.\%d \%d\%s\r\n...".
252 * If it's well-formed, assign *<b>code</b>, point *<b>message</b> to the first
253 * non-space character after code if there is one and message is non-NULL
254 * (else leave it alone), and return 0.
255 * Otherwise, return -1.
258 parse_http_response(char *headers
, int *code
, char **message
)
261 tor_assert(headers
&& code
);
263 while(isspace((int)*headers
)) headers
++; /* tolerate leading whitespace */
265 if(sscanf(headers
, "HTTP/1.%d %d", &n1
, &n2
) < 2 ||
266 (n1
!= 0 && n1
!= 1) ||
267 (n2
< 100 || n2
>= 600)) {
268 log_fn(LOG_WARN
,"Failed to parse header '%s'",headers
);
273 /* XXX should set *message correctly */
278 /** Read handler for directory connections. (That's connections <em>to</em>
279 * directory servers and connections <em>at</em> directory servers.)
281 int connection_dir_process_inbuf(connection_t
*conn
) {
287 tor_assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
289 /* Directory clients write, then read data until they receive EOF;
290 * directory servers read data until they get an HTTP command, then
291 * write their response (when it's finished flushing, they mark for
294 if(conn
->inbuf_reached_eof
) {
295 if(conn
->state
!= DIR_CONN_STATE_CLIENT_READING
) {
296 log_fn(LOG_INFO
,"conn reached eof, not reading. Closing.");
297 connection_close_immediate(conn
); /* it was an error; give up on flushing */
298 connection_mark_for_close(conn
);
302 switch(fetch_from_buf_http(conn
->inbuf
,
303 &headers
, MAX_HEADERS_SIZE
,
304 &body
, &body_len
, MAX_DIR_SIZE
)) {
305 case -1: /* overflow */
306 log_fn(LOG_WARN
,"'fetch' response too large. Failing.");
307 connection_mark_for_close(conn
);
310 log_fn(LOG_INFO
,"'fetch' response not all here, but we're at eof. Closing.");
311 connection_mark_for_close(conn
);
313 /* case 1, fall through */
316 if(parse_http_response(headers
, &status_code
, NULL
) < 0) {
317 log_fn(LOG_WARN
,"Unparseable headers. Closing.");
318 free(body
); free(headers
);
319 connection_mark_for_close(conn
);
323 if(conn
->purpose
== DIR_PURPOSE_FETCH_DIR
) {
324 /* fetch/process the directory to learn about new routers. */
325 log_fn(LOG_INFO
,"Received directory (size %d):\n%s", body_len
, body
);
326 if(status_code
== 503 || body_len
== 0) {
327 log_fn(LOG_INFO
,"Empty directory. Ignoring.");
328 free(body
); free(headers
);
329 connection_mark_for_close(conn
);
332 if(status_code
!= 200) {
333 log_fn(LOG_WARN
,"Received http status code %d from dirserver. Failing.",
335 free(body
); free(headers
);
336 connection_mark_for_close(conn
);
339 if(router_set_routerlist_from_directory(body
, conn
->identity_pkey
) < 0){
340 log_fn(LOG_INFO
,"...but parsing failed. Ignoring.");
342 log_fn(LOG_INFO
,"updated routers.");
344 directory_has_arrived(); /* do things we've been waiting to do */
347 if(conn
->purpose
== DIR_PURPOSE_UPLOAD_DIR
) {
348 switch(status_code
) {
350 log_fn(LOG_INFO
,"eof (status 200) after uploading server descriptor: finished.");
353 log_fn(LOG_WARN
,"http status 400 (bad request) response from dirserver. Malformed server descriptor?");
356 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.");
360 log_fn(LOG_WARN
,"http status %d response unrecognized.", status_code
);
365 if(conn
->purpose
== DIR_PURPOSE_FETCH_RENDDESC
) {
366 log_fn(LOG_INFO
,"Received rendezvous descriptor (size %d, status code %d)",
367 body_len
, status_code
);
368 switch(status_code
) {
370 if(rend_cache_store(body
, body_len
) < 0) {
371 log_fn(LOG_WARN
,"Failed to store rendezvous descriptor.");
372 /* alice's ap_stream will notice when connection_mark_for_close
375 /* success. notify pending connections about this. */
376 rend_client_desc_fetched(conn
->rend_query
, 1);
377 conn
->purpose
= DIR_PURPOSE_HAS_FETCHED_RENDDESC
;
381 /* not there. pending connections will be notified when
382 * connection_mark_for_close cleans it up. */
385 log_fn(LOG_WARN
,"http status 400 (bad request). Dirserver didn't like our rendezvous query?");
390 if(conn
->purpose
== DIR_PURPOSE_UPLOAD_RENDDESC
) {
391 switch(status_code
) {
393 log_fn(LOG_INFO
,"eof (status 200) after uploading rendezvous descriptor: finished.");
396 log_fn(LOG_WARN
,"http status 400 (bad request) response from dirserver. Malformed rendezvous descriptor?");
399 log_fn(LOG_WARN
,"http status %d response unrecognized.", status_code
);
403 free(body
); free(headers
);
404 connection_mark_for_close(conn
);
406 } /* endif 'reached eof' */
408 /* If we're on the dirserver side, look for a command. */
409 if(conn
->state
== DIR_CONN_STATE_SERVER_COMMAND_WAIT
) {
410 if (directory_handle_command(conn
) < 0) {
411 connection_mark_for_close(conn
);
417 /* XXX for READ states, might want to make sure inbuf isn't too big */
419 log_fn(LOG_DEBUG
,"Got data, not eof. Leaving on inbuf.");
423 static char answer200
[] = "HTTP/1.0 200 OK\r\n\r\n";
424 static char answer400
[] = "HTTP/1.0 400 Bad request\r\n\r\n";
425 static char answer403
[] = "HTTP/1.0 403 Unapproved server\r\n\r\n";
426 static char answer404
[] = "HTTP/1.0 404 Not found\r\n\r\n";
427 static char answer503
[] = "HTTP/1.0 503 Directory unavailable\r\n\r\n";
429 /** Helper function: called when a dirserver gets a complete HTTP GET
430 * request. Look for a request for a directory or for a rendezvous
431 * service descriptor. On finding one, write a response into
432 * conn-\>outbuf. If the request is unrecognized, send a 404.
433 * Always return 0. */
435 directory_handle_command_get(connection_t
*conn
, char *headers
,
436 char *body
, int body_len
)
443 log_fn(LOG_DEBUG
,"Received GET command.");
445 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
447 if (parse_http_url(headers
, &url
) < 0) {
448 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
452 if(!strcmp(url
,"/")) { /* directory fetch */
453 dlen
= dirserv_get_directory(&cp
);
456 log_fn(LOG_WARN
,"My directory is empty. Closing.");
457 connection_write_to_buf(answer503
, strlen(answer503
), conn
);
461 log_fn(LOG_DEBUG
,"Dumping directory to client.");
462 snprintf(tmp
, sizeof(tmp
), "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/plain\r\n\r\n",
464 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
465 connection_write_to_buf(cp
, strlen(cp
), conn
);
469 if(!strncmp(url
,rend_fetch_url
,strlen(rend_fetch_url
))) {
470 /* rendezvous descriptor fetch */
474 switch(rend_cache_lookup_desc(url
+strlen(rend_fetch_url
), &descp
, &desc_len
)) {
476 snprintf(tmp
, sizeof(tmp
), "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: application/octet-stream\r\n\r\n",
477 desc_len
); /* can't include descp here, because it's got nuls */
478 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
479 connection_write_to_buf(descp
, desc_len
, conn
);
481 case 0: /* well-formed but not present */
482 connection_write_to_buf(answer404
, strlen(answer404
), conn
);
484 case -1: /* not well-formed */
485 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
491 /* we didn't recognize the url */
492 connection_write_to_buf(answer404
, strlen(answer404
), conn
);
496 /** Helper function: called when a dirserver gets a complete HTTP POST
497 * request. Look for an uploaded server descriptor or rendezvous
498 * service descriptor. On finding one, process it and write a
499 * response into conn-\>outbuf. If the request is unrecognized, send a
500 * 404. Always return 0. */
502 directory_handle_command_post(connection_t
*conn
, char *headers
,
503 char *body
, int body_len
)
508 log_fn(LOG_DEBUG
,"Received POST command.");
510 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
512 if (parse_http_url(headers
, &url
) < 0) {
513 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
516 log_fn(LOG_INFO
,"url '%s' posted to us.", url
);
518 if(!strcmp(url
,"/")) { /* server descriptor post */
520 switch(dirserv_add_descriptor(&cp
)) {
522 /* malformed descriptor, or something wrong */
523 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
526 /* descriptor was well-formed but server has not been approved */
527 connection_write_to_buf(answer403
, strlen(answer403
), conn
);
530 dirserv_get_directory(&cp
); /* rebuild and write to disk */
531 connection_write_to_buf(answer200
, strlen(answer200
), conn
);
537 if(!strncmp(url
,rend_publish_string
,strlen(rend_publish_string
))) {
538 /* rendezvous descriptor post */
539 if(rend_cache_store(body
, body_len
) < 0)
540 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
542 connection_write_to_buf(answer200
, strlen(answer200
), conn
);
546 /* we didn't recognize the url */
547 connection_write_to_buf(answer404
, strlen(answer404
), conn
);
551 /** Called when a dirserver receives data on a directory connection;
552 * looks for an HTTP request. If the request is complete, remove it
553 * from the inbuf, try to process it; otherwise, leave it on the
554 * buffer. Return a 0 on success, or -1 on error.
556 static int directory_handle_command(connection_t
*conn
) {
557 char *headers
=NULL
, *body
=NULL
;
561 tor_assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
563 switch(fetch_from_buf_http(conn
->inbuf
,
564 &headers
, MAX_HEADERS_SIZE
,
565 &body
, &body_len
, MAX_BODY_SIZE
)) {
566 case -1: /* overflow */
567 log_fn(LOG_WARN
,"input too large. Failing.");
570 log_fn(LOG_DEBUG
,"command not all here yet.");
572 /* case 1, fall through */
575 log_fn(LOG_DEBUG
,"headers '%s', body '%s'.", headers
, body
);
577 if(!strncasecmp(headers
,"GET",3))
578 r
= directory_handle_command_get(conn
, headers
, body
, body_len
);
579 else if (!strncasecmp(headers
,"POST",4))
580 r
= directory_handle_command_post(conn
, headers
, body
, body_len
);
582 log_fn(LOG_WARN
,"Got headers '%s' with unknown command. Closing.", headers
);
586 tor_free(headers
); tor_free(body
);
590 /** Write handler for directory connections; called when all data has
591 * been flushed. Close the connection or wait for a response as
594 int connection_dir_finished_flushing(connection_t
*conn
) {
596 tor_assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
598 switch(conn
->state
) {
599 case DIR_CONN_STATE_CLIENT_SENDING
:
600 log_fn(LOG_DEBUG
,"client finished sending command.");
601 conn
->state
= DIR_CONN_STATE_CLIENT_READING
;
602 connection_stop_writing(conn
);
604 case DIR_CONN_STATE_SERVER_WRITING
:
605 log_fn(LOG_INFO
,"Finished writing server response. Closing.");
606 connection_mark_for_close(conn
);
609 log_fn(LOG_WARN
,"BUG: called in unexpected state %d.", conn
->state
);
615 /** Connected handler for directory connections: begin sending data to the
617 int connection_dir_finished_connecting(connection_t
*conn
)
619 tor_assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
620 tor_assert(conn
->state
== DIR_CONN_STATE_CONNECTING
);
622 log_fn(LOG_INFO
,"Dir connection to router %s:%u established.",
623 conn
->address
,conn
->port
);
625 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING
; /* start flushing conn */