terminology shift:
[tor.git] / src / or / directory.c
blobe4ab0ebe55f94ea0c92e9b3544707398deb8a15e
1 /* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2 /* See LICENSE for licensing information */
3 /* $Id$ */
5 #include "or.h"
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) {
24 connection_t *conn;
26 if (command == DIR_CONN_STATE_CONNECTING_FETCH)
27 log_fn(LOG_DEBUG,"initiating directory fetch");
28 else
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.");
33 return;
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);
48 return;
51 switch(connection_connect(conn, router->address, router->addr, router->dir_port)) {
52 case -1:
53 router_mark_as_down(conn->nickname); /* don't try him again */
54 connection_remove(conn);
55 connection_free(conn);
56 return;
57 case 0:
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;
63 return;
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) {
75 const char *s;
76 char tmp[8192];
78 assert(conn && conn->type == CONN_TYPE_DIR);
80 switch(command) {
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;
84 break;
85 case DIR_CONN_STATE_CONNECTING_UPLOAD:
86 s = router_get_my_descriptor();
87 if(!s) {
88 log_fn(LOG_WARN,"Failed to get my descriptor.");
89 return -1;
91 snprintf(tmp, sizeof(tmp), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s",
92 strlen(s), s);
93 connection_write_to_buf(tmp, strlen(tmp), conn);
94 conn->state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD;
95 break;
97 return 0;
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.");
112 return -1;
113 case 0:
114 log_fn(LOG_INFO,"'fetch' response not all here, but we're at eof. Closing.");
115 return -1;
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.");
123 return -1;
125 if(router_set_routerlist_from_directory(the_directory, conn->identity_pkey) < 0){
126 log_fn(LOG_INFO,"...but parsing failed. Ignoring.");
127 } else {
128 log_fn(LOG_INFO,"updated routers.");
130 if(options.ORPort) { /* connect to them all */
131 router_retry_connections();
133 return -1;
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.");
137 return -1;
138 default:
139 log_fn(LOG_INFO,"conn reached eof, not reading. Closing.");
140 return -1;
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.");
150 return 0;
153 static int directory_handle_command(connection_t *conn) {
154 char headers[2048];
155 char body[50000]; /* XXX */
156 size_t dlen;
157 const char *cp;
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.");
165 return -1;
166 case 0:
167 log_fn(LOG_DEBUG,"command not all here yet.");
168 return 0;
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);
179 if(dlen == 0) {
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;
188 return 0;
191 if(!strncasecmp(headers,"POST",4)) {
192 /* XXX should check url and http version */
193 log_fn(LOG_DEBUG,"Received POST command.");
194 cp = body;
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;
202 return 0;
205 log_fn(LOG_WARN,"Got headers with unknown command. Closing.");
206 return -1;
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 */
221 return -1;
222 } else {
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);
236 return 0;
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);
241 return 0;
242 case DIR_CONN_STATE_SERVER_WRITING:
243 log_fn(LOG_INFO,"Finished writing server response. Closing.");
244 return -1; /* kill it */
245 default:
246 log_fn(LOG_WARN,"BUG: called in unexpected state.");
247 return -1;
249 return 0;
253 Local Variables:
254 mode:c
255 indent-tabs-mode:nil
256 c-basic-offset:2
257 End: