1 /* Copyright 2001,2002,2003 Roger Dingledine. */
2 /* See LICENSE for licensing information */
7 static int directory_send_command(connection_t
*conn
, int command
);
8 static int directory_handle_command(connection_t
*conn
);
10 /********* START VARIABLES **********/
12 extern or_options_t options
; /* command-line and config-file options */
14 static char fetchstring
[] = "GET / HTTP/1.0\r\n\r\n";
15 static char answerstring
[] = "HTTP/1.0 200 OK\r\n\r\n";
17 /********* END VARIABLES ************/
19 void directory_initiate_command(routerinfo_t
*router
, int command
) {
22 if (command
== DIR_CONN_STATE_CONNECTING_FETCH
)
23 log_fn(LOG_DEBUG
,"initiating directory fetch");
25 log_fn(LOG_DEBUG
,"initiating directory upload");
27 if (!router
) { /* i guess they didn't have one in mind for me to use */
28 log_fn(LOG_WARN
,"No running dirservers known. Not trying.");
32 conn
= connection_new(CONN_TYPE_DIR
);
34 /* set up conn so it's got all the data we need to remember */
35 conn
->addr
= router
->addr
;
36 conn
->port
= router
->dir_port
;
37 conn
->address
= tor_strdup(router
->address
);
38 conn
->nickname
= tor_strdup(router
->nickname
);
39 assert(router
->identity_pkey
);
40 conn
->identity_pkey
= crypto_pk_dup_key(router
->identity_pkey
);
42 if(connection_add(conn
) < 0) { /* no space, forget it */
43 connection_free(conn
);
47 switch(connection_connect(conn
, router
->address
, router
->addr
, router
->dir_port
)) {
49 router_mark_as_down(conn
->nickname
); /* don't try him again */
50 connection_remove(conn
);
51 connection_free(conn
);
54 connection_set_poll_socket(conn
);
55 connection_watch_events(conn
, POLLIN
| POLLOUT
| POLLERR
);
56 /* writable indicates finish, readable indicates broken link,
57 error indicates broken link in windowsland. */
58 conn
->state
= command
;
60 /* case 1: fall through */
63 connection_set_poll_socket(conn
);
64 if(directory_send_command(conn
, command
) < 0) {
65 connection_remove(conn
);
66 connection_free(conn
);
70 static int directory_send_command(connection_t
*conn
, int command
) {
74 assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
77 case DIR_CONN_STATE_CONNECTING_FETCH
:
78 connection_write_to_buf(fetchstring
, strlen(fetchstring
), conn
);
79 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING_FETCH
;
81 case DIR_CONN_STATE_CONNECTING_UPLOAD
:
82 s
= router_get_my_descriptor();
84 log_fn(LOG_WARN
,"Failed to get my descriptor.");
87 snprintf(tmp
, sizeof(tmp
), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s",
89 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
90 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING_UPLOAD
;
96 int connection_dir_process_inbuf(connection_t
*conn
) {
100 assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
102 if(conn
->inbuf_reached_eof
) {
103 switch(conn
->state
) {
104 case DIR_CONN_STATE_CLIENT_READING_FETCH
:
105 /* kill it, but first fetch/process the directory to learn about new routers. */
106 switch(fetch_from_buf_http(conn
->inbuf
,
107 NULL
, 0, &directory
, MAX_DIR_SIZE
)) {
108 case -1: /* overflow */
109 log_fn(LOG_WARN
,"'fetch' response too large. Failing.");
112 log_fn(LOG_INFO
,"'fetch' response not all here, but we're at eof. Closing.");
114 /* case 1, fall through */
116 /* XXX check headers, at least make sure returned 2xx */
117 directorylen
= strlen(directory
);
118 log_fn(LOG_INFO
,"Received directory (size %d):\n%s", directorylen
, directory
);
119 if(directorylen
== 0) {
120 log_fn(LOG_INFO
,"Empty directory. Ignoring.");
124 if(router_set_routerlist_from_directory(directory
, conn
->identity_pkey
) < 0){
125 log_fn(LOG_INFO
,"...but parsing failed. Ignoring.");
127 log_fn(LOG_INFO
,"updated routers.");
129 if(options
.ORPort
) { /* connect to them all */
130 router_retry_connections();
134 case DIR_CONN_STATE_CLIENT_READING_UPLOAD
:
135 /* XXX make sure there's a 200 OK on the buffer */
136 log_fn(LOG_INFO
,"eof while reading upload response. Finished.");
139 log_fn(LOG_INFO
,"conn reached eof, not reading. Closing.");
144 if(conn
->state
== DIR_CONN_STATE_SERVER_COMMAND_WAIT
)
145 return directory_handle_command(conn
);
147 /* XXX for READ states, might want to make sure inbuf isn't too big */
149 log_fn(LOG_DEBUG
,"Got data, not eof. Leaving on inbuf.");
153 static int directory_handle_command_get(connection_t
*conn
,
154 char *headers
, char *body
) {
158 /* XXX should check url and http version */
159 log_fn(LOG_DEBUG
,"Received GET command.");
161 dlen
= dirserv_get_directory(&cp
);
164 log_fn(LOG_WARN
,"My directory is empty. Closing.");
165 return -1; /* XXX send some helpful http error code */
168 log_fn(LOG_DEBUG
,"Dumping directory to client.");
169 connection_write_to_buf(answerstring
, strlen(answerstring
), conn
);
170 connection_write_to_buf(cp
, dlen
, conn
);
171 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
175 static int directory_handle_command_post(connection_t
*conn
,
176 char *headers
, char *body
) {
179 /* XXX should check url and http version */
180 log_fn(LOG_DEBUG
,"Received POST command.");
182 if(dirserv_add_descriptor(&cp
) < 0) {
183 log_fn(LOG_WARN
,"dirserv_add_descriptor() failed. Dropping.");
184 return -1; /* XXX should write an http failed code */
186 dirserv_get_directory(&cp
); /* rebuild and write to disk */
187 connection_write_to_buf(answerstring
, strlen(answerstring
), conn
);
188 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
192 static int directory_handle_command(connection_t
*conn
) {
193 char *headers
=NULL
, *body
=NULL
;
196 #define MAX_HEADERS_SIZE 2048
197 #define MAX_BODY_SIZE 500000
199 assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
201 switch(fetch_from_buf_http(conn
->inbuf
,
202 &headers
, MAX_HEADERS_SIZE
, &body
, MAX_BODY_SIZE
)) {
203 case -1: /* overflow */
204 log_fn(LOG_WARN
,"input too large. Failing.");
207 log_fn(LOG_DEBUG
,"command not all here yet.");
209 /* case 1, fall through */
212 log_fn(LOG_DEBUG
,"headers '%s', body '%s'.", headers
, body
);
214 if(!strncasecmp(headers
,"GET",3))
215 r
= directory_handle_command_get(conn
, headers
, body
);
216 else if (!strncasecmp(headers
,"POST",4))
217 r
= directory_handle_command_post(conn
, headers
, body
);
219 log_fn(LOG_WARN
,"Got headers '%s' with unknown command. Closing.", headers
);
223 tor_free(headers
); tor_free(body
);
227 int connection_dir_finished_flushing(connection_t
*conn
) {
228 int e
, len
=sizeof(e
);
230 assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
232 switch(conn
->state
) {
233 case DIR_CONN_STATE_CONNECTING_FETCH
:
234 case DIR_CONN_STATE_CONNECTING_UPLOAD
:
235 if (getsockopt(conn
->s
, SOL_SOCKET
, SO_ERROR
, (void*)&e
, &len
) < 0) { /* not yet */
236 if(!ERRNO_CONN_EINPROGRESS(errno
)) {
237 log_fn(LOG_DEBUG
,"in-progress connect failed. Removing.");
238 router_mark_as_down(conn
->nickname
); /* don't try him again */
241 return 0; /* no change, see if next time is better */
244 /* the connect has finished. */
246 log_fn(LOG_INFO
,"Dir connection to router %s:%u established.",
247 conn
->address
,conn
->port
);
249 return directory_send_command(conn
, conn
->state
);
250 case DIR_CONN_STATE_CLIENT_SENDING_FETCH
:
251 log_fn(LOG_DEBUG
,"client finished sending fetch command.");
252 conn
->state
= DIR_CONN_STATE_CLIENT_READING_FETCH
;
253 connection_watch_events(conn
, POLLIN
);
255 case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD
:
256 log_fn(LOG_DEBUG
,"client finished sending upload command.");
257 conn
->state
= DIR_CONN_STATE_CLIENT_READING_UPLOAD
;
258 connection_watch_events(conn
, POLLIN
);
260 case DIR_CONN_STATE_SERVER_WRITING
:
261 log_fn(LOG_INFO
,"Finished writing server response. Closing.");
262 return -1; /* kill it */
264 log_fn(LOG_WARN
,"BUG: called in unexpected state.");