directory.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
  2. /* See LICENSE for licensing information */
  3. /* $Id$ */
  4. #include "or.h"
  5. #define MAX_DIR_SIZE 50000 /* XXX, big enough? */
  6. static int directory_send_command(connection_t *conn, int command);
  7. static void directory_rebuild(void);
  8. static int directory_handle_command(connection_t *conn);
  9. /********* START VARIABLES **********/
  10. extern or_options_t options; /* command-line and config-file options */
  11. static char the_directory[MAX_DIR_SIZE+1];
  12. static int directorylen=0;
  13. static int directory_dirty=1;
  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. /********* END VARIABLES ************/
  17. void directory_initiate_command(routerinfo_t *router, int command) {
  18. connection_t *conn;
  19. if(!router) /* i guess they didn't have one in mind for me to use */
  20. return;
  21. #if 0 /* there's no problem with parallel get/posts now. whichever 'get' ends
  22. last is the directory. */
  23. if(connection_get_by_type(CONN_TYPE_DIR)) { /* there's already a dir conn running */
  24. log_fn(LOG_DEBUG,"Canceling connect, dir conn already active.");
  25. return;
  26. }
  27. #endif
  28. if(command == DIR_CONN_STATE_CONNECTING_FETCH)
  29. log_fn(LOG_DEBUG,"initiating directory fetch");
  30. else
  31. log_fn(LOG_DEBUG,"initiating directory upload");
  32. conn = connection_new(CONN_TYPE_DIR);
  33. if(!conn)
  34. return;
  35. /* set up conn so it's got all the data we need to remember */
  36. conn->addr = router->addr;
  37. conn->port = router->dir_port;
  38. conn->address = strdup(router->address);
  39. conn->receiver_bucket = -1; /* edge connections don't do receiver buckets */
  40. conn->bandwidth = -1;
  41. if (router->identity_pkey)
  42. conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey);
  43. else {
  44. log_fn(LOG_WARNING, "No signing key known for dirserver %s; signature won't be checked", conn->address);
  45. conn->identity_pkey = NULL;
  46. /* XXX is there really any situation where router doesn't have an identity_pkey? */
  47. }
  48. if(connection_add(conn) < 0) { /* no space, forget it */
  49. connection_free(conn);
  50. return;
  51. }
  52. switch(connection_connect(conn, router->address, router->addr, router->dir_port)) {
  53. case -1:
  54. router_forget_router(conn->addr, conn->port); /* XXX don't try him again */
  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 */
  65. }
  66. connection_set_poll_socket(conn);
  67. if(directory_send_command(conn, command) < 0) {
  68. connection_remove(conn);
  69. connection_free(conn);
  70. }
  71. }
  72. static int directory_send_command(connection_t *conn, int command) {
  73. const char *s;
  74. char tmp[8192];
  75. assert(conn && conn->type == CONN_TYPE_DIR);
  76. switch(command) {
  77. case DIR_CONN_STATE_CONNECTING_FETCH:
  78. if(connection_write_to_buf(fetchstring, strlen(fetchstring), conn) < 0) {
  79. log_fn(LOG_WARNING,"Couldn't write fetch to buffer.");
  80. return -1;
  81. }
  82. conn->state = DIR_CONN_STATE_CLIENT_SENDING_FETCH;
  83. break;
  84. case DIR_CONN_STATE_CONNECTING_UPLOAD:
  85. s = router_get_my_descriptor();
  86. if(!s) {
  87. log_fn(LOG_WARNING,"Failed to get my descriptor.");
  88. return -1;
  89. }
  90. snprintf(tmp, sizeof(tmp), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s",
  91. strlen(s), s);
  92. if(connection_write_to_buf(tmp, strlen(tmp), conn) < 0) {
  93. log_fn(LOG_WARNING,"Couldn't write post/descriptor to buffer.");
  94. return -1;
  95. }
  96. conn->state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD;
  97. break;
  98. }
  99. return 0;
  100. }
  101. void directory_set_dirty(void) {
  102. directory_dirty = 1;
  103. }
  104. static void directory_rebuild(void) {
  105. if(directory_dirty) {
  106. if (dump_signed_directory_to_string(the_directory, MAX_DIR_SIZE,
  107. get_identity_key())) {
  108. log(LOG_WARNING, "Error creating directory");
  109. return;
  110. }
  111. directorylen = strlen(the_directory);
  112. log(LOG_INFO,"New directory (size %d):\n%s",directorylen,the_directory);
  113. directory_dirty = 0;
  114. } else {
  115. log(LOG_INFO,"Directory still clean, reusing.");
  116. }
  117. }
  118. int connection_dir_process_inbuf(connection_t *conn) {
  119. assert(conn && conn->type == CONN_TYPE_DIR);
  120. if(conn->inbuf_reached_eof) {
  121. switch(conn->state) {
  122. case DIR_CONN_STATE_CLIENT_READING_FETCH:
  123. /* kill it, but first process the_directory and learn about new routers. */
  124. switch(fetch_from_buf_http(conn->inbuf,
  125. NULL, 0, the_directory, MAX_DIR_SIZE)) {
  126. case -1: /* overflow */
  127. log_fn(LOG_WARNING,"'fetch' response too large. Failing.");
  128. return -1;
  129. case 0:
  130. log_fn(LOG_INFO,"'fetch' response not all here, but we're at eof. Closing.");
  131. return -1;
  132. /* case 1, fall through */
  133. }
  134. /* XXX check headers, at least make sure returned 2xx */
  135. directorylen = strlen(the_directory);
  136. log_fn(LOG_INFO,"Received directory (size %d):\n%s", directorylen, the_directory);
  137. if(directorylen == 0) {
  138. log_fn(LOG_INFO,"Empty directory. Ignoring.");
  139. return -1;
  140. }
  141. if(router_get_dir_from_string(the_directory, conn->identity_pkey) < 0){
  142. log_fn(LOG_INFO,"...but parsing failed. Ignoring.");
  143. } else {
  144. log_fn(LOG_INFO,"and got an %s directory; updated routers.",
  145. conn->identity_pkey ? "authenticated" : "unauthenticated");
  146. }
  147. if(options.OnionRouter) { /* connect to them all */
  148. router_retry_connections();
  149. }
  150. return -1;
  151. case DIR_CONN_STATE_CLIENT_READING_UPLOAD:
  152. /* XXX make sure there's a 200 OK on the buffer */
  153. log_fn(LOG_INFO,"eof while reading upload response. Finished.");
  154. return -1;
  155. default:
  156. log_fn(LOG_INFO,"conn reached eof, not reading. Closing.");
  157. return -1;
  158. }
  159. }
  160. if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT)
  161. return directory_handle_command(conn);
  162. /* XXX for READ states, might want to make sure inbuf isn't too big */
  163. log_fn(LOG_DEBUG,"Got data, not eof. Leaving on inbuf.");
  164. return 0;
  165. }
  166. static int directory_handle_command(connection_t *conn) {
  167. char headers[1024];
  168. char body[50000]; /* XXX */
  169. assert(conn && conn->type == CONN_TYPE_DIR);
  170. switch(fetch_from_buf_http(conn->inbuf,
  171. headers, sizeof(headers), body, sizeof(body))) {
  172. case -1: /* overflow */
  173. log_fn(LOG_WARNING,"input too large. Failing.");
  174. return -1;
  175. case 0:
  176. log_fn(LOG_DEBUG,"command not all here yet.");
  177. return 0;
  178. /* case 1, fall through */
  179. }
  180. log_fn(LOG_DEBUG,"headers '%s', body '%s'.",headers,body);
  181. if(!strncasecmp(headers,"GET",3)) {
  182. /* XXX should check url and http version */
  183. directory_rebuild(); /* rebuild it now, iff it's dirty */
  184. if(directorylen == 0) {
  185. log_fn(LOG_WARNING,"My directory is empty. Closing.");
  186. return -1;
  187. }
  188. log_fn(LOG_DEBUG,"Dumping directory to client.");
  189. if((connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) ||
  190. (connection_write_to_buf(the_directory, directorylen, conn) < 0)) {
  191. log_fn(LOG_WARNING,"Failed to write answerstring+directory to outbuf.");
  192. return -1;
  193. }
  194. conn->state = DIR_CONN_STATE_SERVER_WRITING;
  195. return 0;
  196. }
  197. if(!strncasecmp(headers,"POST",4)) {
  198. /* XXX should check url and http version */
  199. log_fn(LOG_DEBUG,"Received POST command, body '%s'", body);
  200. if(connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) {
  201. log_fn(LOG_WARNING,"Failed to write answerstring to outbuf.");
  202. return -1;
  203. }
  204. conn->state = DIR_CONN_STATE_SERVER_WRITING;
  205. return 0;
  206. }
  207. log_fn(LOG_WARNING,"Got headers with unknown command. Closing.");
  208. return -1;
  209. }
  210. int connection_dir_finished_flushing(connection_t *conn) {
  211. int e, len=sizeof(e);
  212. assert(conn && conn->type == CONN_TYPE_DIR);
  213. switch(conn->state) {
  214. case DIR_CONN_STATE_CONNECTING_FETCH:
  215. case DIR_CONN_STATE_CONNECTING_UPLOAD:
  216. if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */
  217. if(!ERRNO_CONN_EINPROGRESS(errno)) {
  218. log_fn(LOG_DEBUG,"in-progress connect failed. Removing.");
  219. router_forget_router(conn->addr, conn->port); /* don't try him again */
  220. return -1;
  221. } else {
  222. return 0; /* no change, see if next time is better */
  223. }
  224. }
  225. /* the connect has finished. */
  226. log_fn(LOG_INFO,"Dir connection to router %s:%u established.",
  227. conn->address,conn->port);
  228. return directory_send_command(conn, conn->state);
  229. case DIR_CONN_STATE_CLIENT_SENDING_FETCH:
  230. log_fn(LOG_DEBUG,"client finished sending fetch command.");
  231. conn->state = DIR_CONN_STATE_CLIENT_READING_FETCH;
  232. connection_watch_events(conn, POLLIN);
  233. return 0;
  234. case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD:
  235. log_fn(LOG_DEBUG,"client finished sending upload command.");
  236. conn->state = DIR_CONN_STATE_CLIENT_READING_UPLOAD;
  237. connection_watch_events(conn, POLLIN);
  238. return 0;
  239. case DIR_CONN_STATE_SERVER_WRITING:
  240. log_fn(LOG_INFO,"Finished writing server response. Closing.");
  241. return -1; /* kill it */
  242. default:
  243. log_fn(LOG_WARNING,"BUG: called in unexpected state.");
  244. return -1;
  245. }
  246. return 0;
  247. }
  248. /*
  249. Local Variables:
  250. mode:c
  251. indent-tabs-mode:nil
  252. c-basic-offset:2
  253. End:
  254. */