directory.c 7.45 KB
Newer Older
1
/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
2
3
4
5
6
7
8
9
10
11
12
13
14
/* See LICENSE for licensing information */
/* $Id$ */

#include "or.h"

#define MAX_DIR_SIZE 50000 /* XXX, big enough? */

/********* START VARIABLES **********/

extern or_options_t options; /* command-line and config-file options */

static char the_directory[MAX_DIR_SIZE+1];
static int directorylen=0;
15
static int reading_headers=0;
16
static int directory_dirty=1;
17

18
19
static char getstring[] = "GET / HTTP/1.0\r\n\r\n";
static char answerstring[] = "HTTP/1.0 200 OK\r\n\r\n";
20
21
22
23
24
25
26
27
28

/********* END VARIABLES ************/

void directory_initiate_fetch(routerinfo_t *router) {
  connection_t *conn;

  if(!router) /* i guess they didn't have one in mind for me to use */
    return;

29
30
  if(connection_get_by_type(CONN_TYPE_DIR)) { /* there's already a fetch running */
    log_fn(LOG_DEBUG,"Canceling fetch, dir conn already active.");
31
    return;
32
  }
33

34
  log_fn(LOG_DEBUG,"initiating directory fetch");
35

36
37
38
39
40
  conn = connection_new(CONN_TYPE_DIR);
  if(!conn)
    return;

  /* set up conn so it's got all the data we need to remember */
Roger Dingledine's avatar
Roger Dingledine committed
41
42
  conn->addr = router->addr;
  conn->port = router->dir_port;
43
44
45
  conn->address = strdup(router->address);
  conn->receiver_bucket = -1; /* edge connections don't do receiver buckets */
  conn->bandwidth = -1;
Nick Mathewson's avatar
Nick Mathewson committed
46
47
48
  if (router->signing_pkey)
    conn->pkey = crypto_pk_dup_key(router->signing_pkey);
  else {
49
    log_fn(LOG_ERR, "No signing key known for directory %s; signature won't be checked", conn->address);
Nick Mathewson's avatar
Nick Mathewson committed
50
51
    conn->pkey = NULL;
  }
52

53
  if(connection_add(conn) < 0) { /* no space, forget it */
54
55
56
57
    connection_free(conn);
    return;
  }

58
59
  switch(connection_connect(conn, router->address, router->addr, router->dir_port)) {
    case -1:
60
      router_forget_router(conn->addr, conn->port); /* don't try him again */
61
62
      connection_free(conn);
      return;
63
64
    case 0:
      connection_set_poll_socket(conn);
65
66
67
      connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
      /* writable indicates finish, readable indicates broken link,
         error indicates broken link in windowsland. */
68
69
      conn->state = DIR_CONN_STATE_CONNECTING;
      return;
70
    /* case 1: fall through */
71
72
  }

73
  connection_set_poll_socket(conn);
74
75
76
77
78
79
80
81
82
83
84
  if(directory_send_command(conn) < 0) {
    connection_remove(conn);
    connection_free(conn);
  }
}

int directory_send_command(connection_t *conn) {

  assert(conn && conn->type == CONN_TYPE_DIR);

  if(connection_write_to_buf(getstring, strlen(getstring), conn) < 0) {
85
    log_fn(LOG_DEBUG,"Couldn't write command to buffer.");
86
87
88
89
90
91
92
    return -1;
  }

  conn->state = DIR_CONN_STATE_SENDING_COMMAND;
  return 0;
}

93
94
95
void directory_set_dirty(void) {
  directory_dirty = 1;
}
96

97
98
void directory_rebuild(void) {
  if(directory_dirty) {
99
100
101
102
103
    if (dump_signed_directory_to_string(the_directory, MAX_DIR_SIZE,
                                        get_signing_privatekey())) {
      log(LOG_ERR, "Error writing directory");
      return;
    }
104
105
106
107
108
109
    log(LOG_INFO,"New directory:\n%s",the_directory);
    directorylen = strlen(the_directory);
    directory_dirty = 0;
  } else {
    log(LOG_INFO,"Directory still clean, reusing.");
  }
110
111
112
113
114
115
116
117
}

int connection_dir_process_inbuf(connection_t *conn) {

  assert(conn && conn->type == CONN_TYPE_DIR);

  if(conn->inbuf_reached_eof) {
    if(conn->state != DIR_CONN_STATE_READING) {
118
      log_fn(LOG_DEBUG,"conn reached eof, not reading. Closing.");
119
120
121
      return -1;
    }
    /* eof reached, kill it, but first process the_directory and learn about new routers. */
122
    log_fn(LOG_DEBUG,"Received directory (size %d)\n%s", directorylen, the_directory);
123
    if(directorylen == 0) {
124
      log_fn(LOG_DEBUG,"Empty directory. Ignoring.");
125
126
      return -1;
    }
127
    if(router_get_dir_from_string(the_directory, conn->pkey) < 0) {
128
      log_fn(LOG_DEBUG,"...but parsing failed. Ignoring.");
Nick Mathewson's avatar
Nick Mathewson committed
129
    } else {
130
      log_fn(LOG_DEBUG,"and got a %s directory; updated routers.", 
Nick Mathewson's avatar
Nick Mathewson committed
131
          conn->pkey ? "authenticated" : "unauthenticated");
132
    }
133

134
    if(options.OnionRouter) { /* connect to them all */
135
      router_retry_connections();
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
136
    }
137
138
139
140
141
142
143
144
145
    return -1;
  }

  switch(conn->state) {
    case DIR_CONN_STATE_COMMAND_WAIT:
      return directory_handle_command(conn);
    case DIR_CONN_STATE_READING:
      return directory_handle_reading(conn);
    default:
146
      log_fn(LOG_DEBUG,"Got data while writing; Ignoring.");
147
148
149
150
151
152
153
      break;
  }

  return 0;
}

int directory_handle_command(connection_t *conn) {
154
  char buf[15];
155
156
157

  assert(conn && conn->type == CONN_TYPE_DIR);

158
  if(conn->inbuf_datalen < (int)strlen(getstring)) { /* entire response available? */
159
    log_fn(LOG_DEBUG,"Entire command not here yet. Waiting.");
160
161
162
    return 0; /* not yet */
  }

163
  connection_fetch_from_buf(buf,strlen(getstring),conn);
164
165

  if(strncasecmp(buf,getstring,strlen("GET / HTTP/"))) {
166
    log_fn(LOG_DEBUG,"Command doesn't seem to be a get. Closing,");
167
168
    return -1;
  }
169

170
171
  directory_rebuild(); /* rebuild it now, iff it's dirty */

172
  if(directorylen == 0) {
173
    log_fn(LOG_DEBUG,"My directory is empty. Closing.");
174
175
176
    return -1;
  }

177
  log_fn(LOG_DEBUG,"Dumping directory to client."); 
178
179
  if((connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) ||
     (connection_write_to_buf(the_directory, directorylen, conn) < 0)) {
180
    log_fn(LOG_DEBUG,"my outbuf is full. Oops.");
181
182
183
184
185
186
187
188
189
    return -1;
  }

  conn->state = DIR_CONN_STATE_WRITING;
  return 0;
}

int directory_handle_reading(connection_t *conn) {
  int amt;
190
  char *headers;
191
192
193

  assert(conn && conn->type == CONN_TYPE_DIR);

194
195
196
197
  if(reading_headers) {
    amt = connection_find_on_inbuf("\r\n\r\n", 4, conn);
    if(amt < 0) /* not there yet */
      return 0;
198
    headers = tor_malloc(amt+1);
199
    connection_fetch_from_buf(headers,amt,conn);
200
201
202
203
204
    headers[amt] = 0; /* null terminate it, */
    free(headers); /* and then throw it away */
    reading_headers = 0;
  }

205
206
207
  amt = conn->inbuf_datalen;

  if(amt + directorylen >= MAX_DIR_SIZE) {
208
    log_fn(LOG_DEBUG,"Directory too large. Failing messily.");
209
210
211
    return -1;
  }

212
  log_fn(LOG_DEBUG,"Pulling %d bytes in at offset %d.",
213
214
    amt, directorylen);

215
  connection_fetch_from_buf(the_directory+directorylen,amt,conn);
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

  directorylen += amt;

  the_directory[directorylen] = 0;

  return 0;
}

int connection_dir_finished_flushing(connection_t *conn) {
  int e, len=sizeof(e);

  assert(conn && conn->type == CONN_TYPE_DIR);

  switch(conn->state) {
    case DIR_CONN_STATE_CONNECTING:
231
      if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0)  { /* not yet */
232
        if(!ERRNO_CONN_EINPROGRESS(errno)) {
233
          /* yuck. kill it. */
234
          log_fn(LOG_DEBUG,"in-progress connect failed. Removing.");
235
          router_forget_router(conn->addr, conn->port); /* don't try him again */
236
237
238
239
240
241
242
          return -1;
        } else {
          return 0; /* no change, see if next time is better */
        }
      }
      /* the connect has finished. */

243
      log_fn(LOG_DEBUG,"Dir connection to router %s:%u established.",
244
245
246
247
          conn->address,conn->port);

      return directory_send_command(conn);
    case DIR_CONN_STATE_SENDING_COMMAND:
248
      log_fn(LOG_DEBUG,"client finished sending command.");
249
      directorylen = 0;
250
      reading_headers = 1;
251
252
253
254
      conn->state = DIR_CONN_STATE_READING;
      connection_watch_events(conn, POLLIN);
      return 0;
    case DIR_CONN_STATE_WRITING:
255
      log_fn(LOG_DEBUG,"Finished writing directory. Closing.");
256
257
      return -1; /* kill it */
    default:
258
      log_fn(LOG_DEBUG,"BUG: called in unexpected state.");
259
260
261
262
263
264
      return 0;
  }

  return 0;
}

265
266
267
268
269
270
271
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/