1 /* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
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";
16 /* XXX the_directory is the same name as a different variable inĀ·
17 * dirserv.c, are you crazy?? */
18 static char the_directory
[MAX_DIR_SIZE
+1];
19 static int directorylen
=0;
21 /********* END VARIABLES ************/
23 void directory_initiate_command(routerinfo_t
*router
, int command
) {
26 if (command
== DIR_CONN_STATE_CONNECTING_FETCH
)
27 log_fn(LOG_DEBUG
,"initiating directory fetch");
29 log_fn(LOG_DEBUG
,"initiating directory upload");
31 if (!router
) { /* i guess they didn't have one in mind for me to use */
32 log_fn(LOG_WARN
,"No running dirservers known. Not trying.");
36 conn
= connection_new(CONN_TYPE_DIR
);
38 /* set up conn so it's got all the data we need to remember */
39 conn
->addr
= router
->addr
;
40 conn
->port
= router
->dir_port
;
41 conn
->address
= tor_strdup(router
->address
);
42 conn
->nickname
= tor_strdup(router
->nickname
);
43 assert(router
->identity_pkey
);
44 conn
->identity_pkey
= crypto_pk_dup_key(router
->identity_pkey
);
46 if(connection_add(conn
) < 0) { /* no space, forget it */
47 connection_free(conn
);
51 switch(connection_connect(conn
, router
->address
, router
->addr
, router
->dir_port
)) {
53 router_mark_as_down(conn
->nickname
); /* don't try him again */
54 connection_remove(conn
);
55 connection_free(conn
);
58 connection_set_poll_socket(conn
);
59 connection_watch_events(conn
, POLLIN
| POLLOUT
| POLLERR
);
60 /* writable indicates finish, readable indicates broken link,
61 error indicates broken link in windowsland. */
62 conn
->state
= command
;
64 /* case 1: fall through */
67 connection_set_poll_socket(conn
);
68 if(directory_send_command(conn
, command
) < 0) {
69 connection_remove(conn
);
70 connection_free(conn
);
74 static int directory_send_command(connection_t
*conn
, int command
) {
78 assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
81 case DIR_CONN_STATE_CONNECTING_FETCH
:
82 connection_write_to_buf(fetchstring
, strlen(fetchstring
), conn
);
83 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING_FETCH
;
85 case DIR_CONN_STATE_CONNECTING_UPLOAD
:
86 s
= router_get_my_descriptor();
88 log_fn(LOG_WARN
,"Failed to get my descriptor.");
91 snprintf(tmp
, sizeof(tmp
), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s",
93 connection_write_to_buf(tmp
, strlen(tmp
), conn
);
94 conn
->state
= DIR_CONN_STATE_CLIENT_SENDING_UPLOAD
;
100 int connection_dir_process_inbuf(connection_t
*conn
) {
102 assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
104 if(conn
->inbuf_reached_eof
) {
105 switch(conn
->state
) {
106 case DIR_CONN_STATE_CLIENT_READING_FETCH
:
107 /* kill it, but first process the_directory and learn about new routers. */
108 switch(fetch_from_buf_http(conn
->inbuf
,
109 NULL
, 0, the_directory
, MAX_DIR_SIZE
)) {
110 case -1: /* overflow */
111 log_fn(LOG_WARN
,"'fetch' response too large. Failing.");
114 log_fn(LOG_INFO
,"'fetch' response not all here, but we're at eof. Closing.");
116 /* case 1, fall through */
118 /* XXX check headers, at least make sure returned 2xx */
119 directorylen
= strlen(the_directory
);
120 log_fn(LOG_INFO
,"Received directory (size %d):\n%s", directorylen
, the_directory
);
121 if(directorylen
== 0) {
122 log_fn(LOG_INFO
,"Empty directory. Ignoring.");
125 if(router_set_routerlist_from_directory(the_directory
, conn
->identity_pkey
) < 0){
126 log_fn(LOG_INFO
,"...but parsing failed. Ignoring.");
128 log_fn(LOG_INFO
,"updated routers.");
130 if(options
.ORPort
) { /* connect to them all */
131 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(connection_t
*conn
) {
155 char body
[50000]; /* XXX */
159 assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
161 switch(fetch_from_buf_http(conn
->inbuf
,
162 headers
, sizeof(headers
), body
, sizeof(body
))) {
163 case -1: /* overflow */
164 log_fn(LOG_WARN
,"input too large. Failing.");
167 log_fn(LOG_DEBUG
,"command not all here yet.");
169 /* case 1, fall through */
172 log_fn(LOG_DEBUG
,"headers '%s', body '%s'.",headers
,body
);
173 if(!strncasecmp(headers
,"GET",3)) {
174 /* XXX should check url and http version */
175 log_fn(LOG_DEBUG
,"Received GET command.");
177 dlen
= dirserv_get_directory(&cp
);
180 log_fn(LOG_WARN
,"My directory is empty. Closing.");
181 return -1; /* XXX send some helpful http error code */
184 log_fn(LOG_DEBUG
,"Dumping directory to client.");
185 connection_write_to_buf(answerstring
, strlen(answerstring
), conn
);
186 connection_write_to_buf(cp
, dlen
, conn
);
187 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
191 if(!strncasecmp(headers
,"POST",4)) {
192 /* XXX should check url and http version */
193 log_fn(LOG_DEBUG
,"Received POST command.");
195 if(dirserv_add_descriptor(&cp
) < 0) {
196 log_fn(LOG_WARN
,"dirserv_add_descriptor() failed. Dropping.");
197 return -1; /* XXX should write an http failed code */
199 dirserv_get_directory(&cp
); /* rebuild and write to disk */
200 connection_write_to_buf(answerstring
, strlen(answerstring
), conn
);
201 conn
->state
= DIR_CONN_STATE_SERVER_WRITING
;
205 log_fn(LOG_WARN
,"Got headers with unknown command. Closing.");
209 int connection_dir_finished_flushing(connection_t
*conn
) {
210 int e
, len
=sizeof(e
);
212 assert(conn
&& conn
->type
== CONN_TYPE_DIR
);
214 switch(conn
->state
) {
215 case DIR_CONN_STATE_CONNECTING_FETCH
:
216 case DIR_CONN_STATE_CONNECTING_UPLOAD
:
217 if (getsockopt(conn
->s
, SOL_SOCKET
, SO_ERROR
, (void*)&e
, &len
) < 0) { /* not yet */
218 if(!ERRNO_CONN_EINPROGRESS(errno
)) {
219 log_fn(LOG_DEBUG
,"in-progress connect failed. Removing.");
220 router_mark_as_down(conn
->nickname
); /* don't try him again */
223 return 0; /* no change, see if next time is better */
226 /* the connect has finished. */
228 log_fn(LOG_INFO
,"Dir connection to router %s:%u established.",
229 conn
->address
,conn
->port
);
231 return directory_send_command(conn
, conn
->state
);
232 case DIR_CONN_STATE_CLIENT_SENDING_FETCH
:
233 log_fn(LOG_DEBUG
,"client finished sending fetch command.");
234 conn
->state
= DIR_CONN_STATE_CLIENT_READING_FETCH
;
235 connection_watch_events(conn
, POLLIN
);
237 case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD
:
238 log_fn(LOG_DEBUG
,"client finished sending upload command.");
239 conn
->state
= DIR_CONN_STATE_CLIENT_READING_UPLOAD
;
240 connection_watch_events(conn
, POLLIN
);
242 case DIR_CONN_STATE_SERVER_WRITING
:
243 log_fn(LOG_INFO
,"Finished writing server response. Closing.");
244 return -1; /* kill it */
246 log_fn(LOG_WARN
,"BUG: called in unexpected state.");