directory.c 9.1 KB

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