directory.c 8.7 KB

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