1 /* Copyright 2001,2002,2003 Roger Dingledine. */
2 /* See LICENSE for licensing information */
7 static void directory_send_command(connection_t
*conn
, int purpose
,
8 const char *payload
, int payload_len
);
9 static int directory_handle_command(connection_t
*conn
);
11 /********* START VARIABLES **********/
13 extern or_options_t options
; /* command-line and config-file options */
15 char rend_publish_string
[] = "/rendezvous/publish";
16 char rend_fetch_url
[] = "/rendezvous/";
18 #define MAX_HEADERS_SIZE 2048
19 #define MAX_BODY_SIZE 500000
21 /********* END VARIABLES ************/
23 void directory_initiate_command(routerinfo_t
*router
, int purpose
,
24 const char *payload
, int payload_len
) {
27 if(purpose
== DIR_PURPOSE_FETCH_DIR
)
28 log_fn(LOG_DEBUG
,"initiating directory fetch");
29 if(purpose
== DIR_PURPOSE_FETCH_RENDDESC
)
30 log_fn(LOG_DEBUG
,"initiating hidden-service descriptor fetch");
31 if(purpose
== DIR_PURPOSE_UPLOAD_DIR
)
32 log_fn(LOG_DEBUG
,"initiating server descriptor upload");
33 if(purpose
== DIR_PURPOSE_UPLOAD_RENDDESC
)
34 log_fn(LOG_DEBUG
,"initiating hidden-service descriptor upload");
36 if (!router
) { /* i guess they didn't have one in mind for me to use */
37 log_fn(LOG_WARN
,"No running dirservers known. Not trying. (purpose %d)", purpose
);
41 conn
= connection_new(CONN_TYPE_DIR
);
43 /* set up conn so it's got all the data we need to remember */
44 conn
->addr
= router
->addr
;
45 conn
->port
= router
->dir_port
;
46 conn
->address
= tor_strdup(router
->address
);
47 conn
->nickname
= tor_strdup(router
->nickname
);
48 tor_assert(router
->identity_pkey
);
49 conn
->identity_pkey
= crypto_pk_dup_key(router
->identity_pkey
);
51 conn
->purpose
= purpose
;
53 if(connection_add(conn
) < 0) { /* no space, forget it */
54 connection_free(conn
);
58 /* queue the command on the outbuf */
59 directory_send_command(conn
, purpose
, payload
, payload_len
);
61 /* give it an initial state */
62 conn
->state
= DIR_CONN_STATE_CONNECTING
;
64 if(purpose
== DIR_PURPOSE_FETCH_DIR
||
65 purpose
== DIR_PURPOSE_UPLOAD_DIR
) {
66 /* then we want to connect directly */
67 switch(connection_connect(conn
, conn
->address
, conn
->addr
, conn
->port
)) {
69 router_mark_as_down(conn
->nickname
); /* don't try him again */
70 connection_mark_for_close(conn
, 0);
73 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING
; /* start flushing conn */
76 connection_set_poll_socket(conn
);
77 connection_watch_events(conn
, POLLIN
| POLLOUT
| POLLERR
);
78 /* writable indicates finish, readable indicates broken link,
79 error indicates broken link in windowsland. */
81 } else { /* we want to connect via tor */
82 /* make an AP connection
83 * populate it and add it at the right state
84 * socketpair and hook up both sides
86 conn
->s
= connection_ap_make_bridge(conn
->address
, conn
->port
);
88 log_fn(LOG_WARN
,"Making AP bridge to dirserver failed.");
89 connection_mark_for_close(conn
, 0);
93 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING
;
94 connection_set_poll_socket(conn
);
95 connection_start_reading(conn
);
99 static void directory_send_command(connection_t
*conn
, int purpose
,
100 const char *payload
, int payload_len
) {
101 char fetchstring
[] = "GET / HTTP/1.0\r\n\r\n";
104 tor_assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
107 case DIR_PURPOSE_FETCH_DIR
:
108 tor_assert(payload
== NULL
);
109 connection_write_to_buf(fetchstring
, strlen(fetchstring
), conn
);
111 case DIR_PURPOSE_UPLOAD_DIR
:
113 snprintf(tmp
, sizeof(tmp
), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n",
115 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
116 connection_write_to_buf(payload
, payload_len
, conn
);
118 case DIR_PURPOSE_FETCH_RENDDESC
:
121 /* this must be true or we wouldn't be doing the lookup */
122 tor_assert(payload_len
<= REND_SERVICE_ID_LEN
);
123 memcpy(conn
->rend_query
, payload
, payload_len
);
124 conn
->rend_query
[payload_len
] = 0;
126 snprintf(tmp
, sizeof(tmp
), "GET %s%s HTTP/1.0\r\n\r\n", rend_fetch_url
, payload
);
127 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
129 case DIR_PURPOSE_UPLOAD_RENDDESC
:
131 snprintf(tmp
, sizeof(tmp
),
132 "POST %s HTTP/1.0\r\nContent-Length: %d\r\n\r\n", rend_publish_string
, payload_len
);
133 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
134 /* could include nuls, need to write it separately */
135 connection_write_to_buf(payload
, payload_len
, conn
);
140 /* Parse "%s %s HTTP/1..."
141 * If it's well-formed, point *url to the second %s,
142 * null-terminate it (this modifies headers!) and return 0.
143 * Otherwise, return -1.
145 int parse_http_url(char *headers
, char **url
) {
148 s
= (char *)eat_whitespace_no_nl(headers
);
150 s
= (char *)find_whitespace(s
); /* get past GET/POST */
152 s
= (char *)eat_whitespace_no_nl(s
);
154 tmp
= s
; /* this is it, assuming it's valid */
155 s
= (char *)find_whitespace(s
);
162 /* Parse "HTTP/1.%d %d%s\r\n".
163 * If it's well-formed, assign *code, point *message to the first
164 * non-space character after code if there is one and message is non-NULL
165 * (else leave it alone), and return 0.
166 * Otherwise, return -1.
168 int parse_http_response(char *headers
, int *code
, char **message
) {
170 tor_assert(headers
&& code
);
172 while(isspace((int)*headers
)) headers
++; /* tolerate leading whitespace */
174 if(sscanf(headers
, "HTTP/1.%d %d", &n1
, &n2
) < 2 ||
175 (n1
!= 0 && n1
!= 1) ||
176 (n2
< 100 || n2
>= 600)) {
177 log_fn(LOG_WARN
,"Failed to parse header '%s'",headers
);
182 /* XXX should set *message correctly */
187 int connection_dir_process_inbuf(connection_t
*conn
) {
193 tor_assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
195 if(conn
->inbuf_reached_eof
) {
196 if(conn
->state
!= DIR_CONN_STATE_CLIENT_READING
) {
197 log_fn(LOG_INFO
,"conn reached eof, not reading. Closing.");
198 connection_close_immediate(conn
); /* it was an error; give up on flushing */
199 connection_mark_for_close(conn
,0);
203 switch(fetch_from_buf_http(conn
->inbuf
,
204 &headers
, MAX_HEADERS_SIZE
,
205 &body
, &body_len
, MAX_DIR_SIZE
)) {
206 case -1: /* overflow */
207 log_fn(LOG_WARN
,"'fetch' response too large. Failing.");
208 connection_mark_for_close(conn
,0);
211 log_fn(LOG_INFO
,"'fetch' response not all here, but we're at eof. Closing.");
212 connection_mark_for_close(conn
,0);
214 /* case 1, fall through */
217 if(parse_http_response(headers
, &status_code
, NULL
) < 0) {
218 log_fn(LOG_WARN
,"Unparseable headers. Closing.");
219 free(body
); free(headers
);
220 connection_mark_for_close(conn
,0);
224 if(conn
->purpose
== DIR_PURPOSE_FETCH_DIR
) {
225 /* fetch/process the directory to learn about new routers. */
226 log_fn(LOG_INFO
,"Received directory (size %d):\n%s", body_len
, body
);
227 if(status_code
== 503 || body_len
== 0) {
228 log_fn(LOG_INFO
,"Empty directory. Ignoring.");
229 free(body
); free(headers
);
230 connection_mark_for_close(conn
,0);
233 if(status_code
!= 200) {
234 log_fn(LOG_WARN
,"Received http status code %d from dirserver. Failing.",
236 free(body
); free(headers
);
237 connection_mark_for_close(conn
,0);
240 if(router_set_routerlist_from_directory(body
, conn
->identity_pkey
) < 0){
241 log_fn(LOG_INFO
,"...but parsing failed. Ignoring.");
243 log_fn(LOG_INFO
,"updated routers.");
245 directory_has_arrived(); /* do things we've been waiting to do */
248 if(conn
->purpose
== DIR_PURPOSE_UPLOAD_DIR
) {
249 switch(status_code
) {
251 log_fn(LOG_INFO
,"eof (status 200) after uploading server descriptor: finished.");
254 log_fn(LOG_WARN
,"http status 400 (bad request) response from dirserver. Malformed server descriptor?");
257 log_fn(LOG_WARN
,"http status 403 (unapproved server) response from dirserver. Is your clock skewed? Have you mailed arma your identity fingerprint? Are you using the right key? See README.");
261 log_fn(LOG_WARN
,"http status %d response unrecognized.", status_code
);
266 if(conn
->purpose
== DIR_PURPOSE_FETCH_RENDDESC
) {
267 log_fn(LOG_INFO
,"Received rendezvous descriptor (size %d, status code %d)",
268 body_len
, status_code
);
269 switch(status_code
) {
271 if(rend_cache_store(body
, body_len
) < 0) {
272 log_fn(LOG_WARN
,"Failed to store rendezvous descriptor.");
273 /* alice's ap_stream will notice when connection_mark_for_close
276 /* success. notify pending connections about this. */
277 rend_client_desc_fetched(conn
->rend_query
, 1);
278 conn
->purpose
= DIR_PURPOSE_HAS_FETCHED_RENDDESC
;
282 /* not there. pending connections will be notified when
283 * connection_mark_for_close cleans it up. */
286 log_fn(LOG_WARN
,"http status 400 (bad request). Dirserver didn't like our rendezvous query?");
291 if(conn
->purpose
== DIR_PURPOSE_UPLOAD_RENDDESC
) {
292 switch(status_code
) {
294 log_fn(LOG_INFO
,"eof (status 200) after uploading rendezvous descriptor: finished.");
297 log_fn(LOG_WARN
,"http status 400 (bad request) response from dirserver. Malformed rendezvous descriptor?");
300 log_fn(LOG_WARN
,"http status %d response unrecognized.", status_code
);
304 free(body
); free(headers
);
305 connection_mark_for_close(conn
,0);
309 if(conn
->state
== DIR_CONN_STATE_SERVER_COMMAND_WAIT
) {
310 if (directory_handle_command(conn
) < 0) {
311 connection_mark_for_close(conn
,0);
317 /* XXX for READ states, might want to make sure inbuf isn't too big */
319 log_fn(LOG_DEBUG
,"Got data, not eof. Leaving on inbuf.");
323 static char answer200
[] = "HTTP/1.0 200 OK\r\n\r\n";
324 static char answer400
[] = "HTTP/1.0 400 Bad request\r\n\r\n";
325 static char answer403
[] = "HTTP/1.0 403 Unapproved server\r\n\r\n";
326 static char answer404
[] = "HTTP/1.0 404 Not found\r\n\r\n";
327 static char answer503
[] = "HTTP/1.0 503 Directory unavailable\r\n\r\n";
329 /* always returns 0 */
330 static int directory_handle_command_get(connection_t
*conn
,
331 char *headers
, char *body
,
338 log_fn(LOG_DEBUG
,"Received GET command.");
340 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
342 if (parse_http_url(headers
, &url
) < 0) {
343 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
347 if(!strcmp(url
,"/")) { /* directory fetch */
348 dlen
= dirserv_get_directory(&cp
);
351 log_fn(LOG_WARN
,"My directory is empty. Closing.");
352 connection_write_to_buf(answer503
, strlen(answer503
), conn
);
356 log_fn(LOG_DEBUG
,"Dumping directory to client.");
357 snprintf(tmp
, sizeof(tmp
), "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/plain\r\n\r\n",
359 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
360 connection_write_to_buf(cp
, strlen(cp
), conn
);
364 if(!strncmp(url
,rend_fetch_url
,strlen(rend_fetch_url
))) {
365 /* rendezvous descriptor fetch */
369 switch(rend_cache_lookup_desc(url
+strlen(rend_fetch_url
), &descp
, &desc_len
)) {
371 snprintf(tmp
, sizeof(tmp
), "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: application/octet-stream\r\n\r\n",
372 desc_len
); /* can't include descp here, because it's got nuls */
373 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
374 connection_write_to_buf(descp
, desc_len
, conn
);
376 case 0: /* well-formed but not present */
377 connection_write_to_buf(answer404
, strlen(answer404
), conn
);
379 case -1: /* not well-formed */
380 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
386 /* we didn't recognize the url */
387 connection_write_to_buf(answer404
, strlen(answer404
), conn
);
391 /* always returns 0 */
392 static int directory_handle_command_post(connection_t
*conn
,
393 char *headers
, char *body
,
398 log_fn(LOG_DEBUG
,"Received POST command.");
400 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
402 if (parse_http_url(headers
, &url
) < 0) {
403 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
406 log_fn(LOG_INFO
,"url '%s' posted to us.", url
);
408 if(!strcmp(url
,"/")) { /* server descriptor post */
410 switch(dirserv_add_descriptor(&cp
)) {
412 /* malformed descriptor, or something wrong */
413 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
416 /* descriptor was well-formed but server has not been approved */
417 connection_write_to_buf(answer403
, strlen(answer403
), conn
);
420 dirserv_get_directory(&cp
); /* rebuild and write to disk */
421 connection_write_to_buf(answer200
, strlen(answer200
), conn
);
427 if(!strncmp(url
,rend_publish_string
,strlen(rend_publish_string
))) {
428 /* rendezvous descriptor post */
429 if(rend_cache_store(body
, body_len
) < 0)
430 connection_write_to_buf(answer400
, strlen(answer400
), conn
);
432 connection_write_to_buf(answer200
, strlen(answer200
), conn
);
436 /* we didn't recognize the url */
437 connection_write_to_buf(answer404
, strlen(answer404
), conn
);
441 static int directory_handle_command(connection_t
*conn
) {
442 char *headers
=NULL
, *body
=NULL
;
446 tor_assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
448 switch(fetch_from_buf_http(conn
->inbuf
,
449 &headers
, MAX_HEADERS_SIZE
,
450 &body
, &body_len
, MAX_BODY_SIZE
)) {
451 case -1: /* overflow */
452 log_fn(LOG_WARN
,"input too large. Failing.");
455 log_fn(LOG_DEBUG
,"command not all here yet.");
457 /* case 1, fall through */
460 log_fn(LOG_DEBUG
,"headers '%s', body '%s'.", headers
, body
);
462 if(!strncasecmp(headers
,"GET",3))
463 r
= directory_handle_command_get(conn
, headers
, body
, body_len
);
464 else if (!strncasecmp(headers
,"POST",4))
465 r
= directory_handle_command_post(conn
, headers
, body
, body_len
);
467 log_fn(LOG_WARN
,"Got headers '%s' with unknown command. Closing.", headers
);
471 tor_free(headers
); tor_free(body
);
475 int connection_dir_finished_flushing(connection_t
*conn
) {
476 int e
, len
=sizeof(e
);
478 tor_assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
480 switch(conn
->state
) {
481 case DIR_CONN_STATE_CONNECTING
:
482 if (getsockopt(conn
->s
, SOL_SOCKET
, SO_ERROR
, (void*)&e
, &len
) < 0) { /* not yet */
483 if(!ERRNO_IS_CONN_EINPROGRESS(tor_socket_errno(conn
->s
))) {
484 log_fn(LOG_DEBUG
,"in-progress connect failed. Removing.");
485 router_mark_as_down(conn
->nickname
); /* don't try him again */
486 connection_mark_for_close(conn
,0);
489 return 0; /* no change, see if next time is better */
492 /* the connect has finished. */
494 log_fn(LOG_INFO
,"Dir connection to router %s:%u established.",
495 conn
->address
,conn
->port
);
497 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING
; /* start flushing conn */
499 case DIR_CONN_STATE_CLIENT_SENDING
:
500 log_fn(LOG_DEBUG
,"client finished sending command.");
501 conn
->state
= DIR_CONN_STATE_CLIENT_READING
;
502 connection_stop_writing(conn
);
504 case DIR_CONN_STATE_SERVER_WRITING
:
505 log_fn(LOG_INFO
,"Finished writing server response. Closing.");
506 connection_mark_for_close(conn
,0);
509 log_fn(LOG_WARN
,"BUG: called in unexpected state %d.", conn
->state
);