directory.c 9.4 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_ERR, "No signing key known for dirserver %s; signature won't be checked", conn->address);
  45. conn->identity_pkey = NULL;
  46. }
  47. if(connection_add(conn) < 0) { /* no space, forget it */
  48. connection_free(conn);
  49. return;
  50. }
  51. switch(connection_connect(conn, router->address, router->addr, router->dir_port)) {
  52. case -1:
  53. router_forget_router(conn->addr, conn->port); /* XXX don't try him again */
  54. connection_free(conn);
  55. return;
  56. case 0:
  57. connection_set_poll_socket(conn);
  58. connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
  59. /* writable indicates finish, readable indicates broken link,
  60. error indicates broken link in windowsland. */
  61. conn->state = command;
  62. return;
  63. /* case 1: fall through */
  64. }
  65. connection_set_poll_socket(conn);
  66. if(directory_send_command(conn, command) < 0) {
  67. connection_remove(conn);
  68. connection_free(conn);
  69. }
  70. }
  71. static int directory_send_command(connection_t *conn, int command) {
  72. const char *s;
  73. char tmp[8192];
  74. assert(conn && conn->type == CONN_TYPE_DIR);
  75. switch(command) {
  76. case DIR_CONN_STATE_CONNECTING_FETCH:
  77. if(connection_write_to_buf(fetchstring, strlen(fetchstring), conn) < 0) {
  78. log_fn(LOG_DEBUG,"Couldn't write fetch to buffer.");
  79. return -1;
  80. }
  81. conn->state = DIR_CONN_STATE_CLIENT_SENDING_FETCH;
  82. break;
  83. case DIR_CONN_STATE_CONNECTING_UPLOAD:
  84. s = router_get_my_descriptor();
  85. if(!s) {
  86. log_fn(LOG_DEBUG,"Failed to get my descriptor.");
  87. return -1;
  88. }
  89. snprintf(tmp, sizeof(tmp), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s",
  90. strlen(s), s);
  91. if(connection_write_to_buf(tmp, strlen(tmp), conn) < 0) {
  92. log_fn(LOG_DEBUG,"Couldn't write post/descriptor to buffer.");
  93. return -1;
  94. }
  95. conn->state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD;
  96. break;
  97. }
  98. return 0;
  99. }
  100. void directory_set_dirty(void) {
  101. directory_dirty = 1;
  102. }
  103. static void directory_rebuild(void) {
  104. if(directory_dirty) {
  105. if (dump_signed_directory_to_string(the_directory, MAX_DIR_SIZE,
  106. get_identity_key())) {
  107. log(LOG_ERR, "Error writing directory");
  108. return;
  109. }
  110. directorylen = strlen(the_directory);
  111. log(LOG_INFO,"New directory (size %d):\n%s",directorylen,the_directory);
  112. directory_dirty = 0;
  113. } else {
  114. log(LOG_INFO,"Directory still clean, reusing.");
  115. }
  116. }
  117. int connection_dir_process_inbuf(connection_t *conn) {
  118. assert(conn && conn->type == CONN_TYPE_DIR);
  119. if(conn->inbuf_reached_eof) {
  120. switch(conn->state) {
  121. case DIR_CONN_STATE_CLIENT_READING_FETCH:
  122. /* kill it, but first process the_directory and learn about new routers. */
  123. switch(fetch_from_buf_http(conn->inbuf,
  124. NULL, 0, the_directory, MAX_DIR_SIZE)) {
  125. case -1: /* overflow */
  126. log_fn(LOG_DEBUG,"'fetch' response too large. Failing.");
  127. return -1;
  128. case 0:
  129. log_fn(LOG_DEBUG,"'fetch' response not all here, but we're at eof. Closing.");
  130. return -1;
  131. /* case 1, fall through */
  132. }
  133. /* XXX check headers, at least make sure returned 2xx */
  134. directorylen = strlen(the_directory);
  135. log_fn(LOG_DEBUG,"Received directory (size %d):\n%s", directorylen, the_directory);
  136. if(directorylen == 0) {
  137. log_fn(LOG_DEBUG,"Empty directory. Ignoring.");
  138. return -1;
  139. }
  140. if(router_get_dir_from_string(the_directory, conn->identity_pkey) < 0){
  141. log_fn(LOG_DEBUG,"...but parsing failed. Ignoring.");
  142. } else {
  143. log_fn(LOG_DEBUG,"and got an %s directory; updated routers.",
  144. conn->identity_pkey ? "authenticated" : "unauthenticated");
  145. }
  146. if(options.OnionRouter) { /* connect to them all */
  147. router_retry_connections();
  148. }
  149. return -1;
  150. case DIR_CONN_STATE_CLIENT_READING_UPLOAD:
  151. /* XXX make sure there's a 200 OK on the buffer */
  152. log_fn(LOG_DEBUG,"eof while reading upload response. Finished.");
  153. return -1;
  154. default:
  155. log_fn(LOG_DEBUG,"conn reached eof, not reading. Closing.");
  156. return -1;
  157. }
  158. }
  159. if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT)
  160. return directory_handle_command(conn);
  161. /* XXX for READ states, might want to make sure inbuf isn't too big */
  162. log_fn(LOG_DEBUG,"Got data, not eof. Leaving on inbuf.");
  163. return 0;
  164. }
  165. static int directory_handle_command(connection_t *conn) {
  166. char headers[1024];
  167. char body[50000]; /* XXX */
  168. assert(conn && conn->type == CONN_TYPE_DIR);
  169. switch(fetch_from_buf_http(conn->inbuf,
  170. headers, sizeof(headers), body, sizeof(body))) {
  171. case -1: /* overflow */
  172. log_fn(LOG_DEBUG,"input too large. Failing.");
  173. return -1;
  174. case 0:
  175. log_fn(LOG_DEBUG,"command not all here yet.");
  176. return 0;
  177. /* case 1, fall through */
  178. }
  179. log_fn(LOG_DEBUG,"headers '%s', body '%s'.",headers,body);
  180. if(!strncasecmp(headers,"GET",3)) {
  181. /* XXX should check url and http version */
  182. directory_rebuild(); /* rebuild it now, iff it's dirty */
  183. if(directorylen == 0) {
  184. log_fn(LOG_DEBUG,"My directory is empty. Closing.");
  185. return -1;
  186. }
  187. log_fn(LOG_DEBUG,"Dumping directory to client.");
  188. if((connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) ||
  189. (connection_write_to_buf(the_directory, directorylen, conn) < 0)) {
  190. log_fn(LOG_DEBUG,"Failed to write answerstring+directory to outbuf.");
  191. return -1;
  192. }
  193. conn->state = DIR_CONN_STATE_SERVER_WRITING;
  194. return 0;
  195. }
  196. if(!strncasecmp(headers,"POST",4)) {
  197. /* XXX should check url and http version */
  198. log_fn(LOG_DEBUG,"Received POST command, body '%s'", body);
  199. if(connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) {
  200. log_fn(LOG_DEBUG,"Failed to write answerstring to outbuf.");
  201. return -1;
  202. }
  203. conn->state = DIR_CONN_STATE_SERVER_WRITING;
  204. return 0;
  205. }
  206. log_fn(LOG_DEBUG,"Got headers with unknown command. Closing.");
  207. return -1;
  208. }
  209. int connection_dir_finished_flushing(connection_t *conn) {
  210. int e, len=sizeof(e);
  211. assert(conn && conn->type == CONN_TYPE_DIR);
  212. switch(conn->state) {
  213. case DIR_CONN_STATE_CONNECTING_FETCH:
  214. case DIR_CONN_STATE_CONNECTING_UPLOAD:
  215. if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */
  216. if(!ERRNO_CONN_EINPROGRESS(errno)) {
  217. log_fn(LOG_DEBUG,"in-progress connect failed. Removing.");
  218. router_forget_router(conn->addr, conn->port); /* don't try him again */
  219. return -1;
  220. } else {
  221. return 0; /* no change, see if next time is better */
  222. }
  223. }
  224. /* the connect has finished. */
  225. log_fn(LOG_DEBUG,"Dir connection to router %s:%u established.",
  226. conn->address,conn->port);
  227. return directory_send_command(conn, conn->state);
  228. case DIR_CONN_STATE_CLIENT_SENDING_FETCH:
  229. log_fn(LOG_DEBUG,"client finished sending fetch command.");
  230. conn->state = DIR_CONN_STATE_CLIENT_READING_FETCH;
  231. connection_watch_events(conn, POLLIN);
  232. return 0;
  233. case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD:
  234. log_fn(LOG_DEBUG,"client finished sending upload command.");
  235. conn->state = DIR_CONN_STATE_CLIENT_READING_UPLOAD;
  236. connection_watch_events(conn, POLLIN);
  237. return 0;
  238. case DIR_CONN_STATE_SERVER_WRITING:
  239. log_fn(LOG_DEBUG,"Finished writing server response. Closing.");
  240. return -1; /* kill it */
  241. default:
  242. log_fn(LOG_DEBUG,"BUG: called in unexpected state.");
  243. return 0;
  244. }
  245. return 0;
  246. }
  247. /*
  248. Local Variables:
  249. mode:c
  250. indent-tabs-mode:nil
  251. c-basic-offset:2
  252. End:
  253. */