directory.c 9.28 KB
Newer Older
1
/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
2
3
4
5
6
7
8
/* See LICENSE for licensing information */
/* $Id$ */

#include "or.h"

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

9
static int directory_send_command(connection_t *conn, int command);
Roger Dingledine's avatar
Roger Dingledine committed
10
11
12
static void directory_rebuild(void);
static int directory_handle_command(connection_t *conn);

13
14
15
16
17
18
/********* 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;
19
static int directory_dirty=1;
20

21
22
static char fetchstring[] = "GET / HTTP/1.0\r\n\r\n";
static char uploadstring[] = "POST / HTTP/1.0\r\n\r\n";
23
static char answerstring[] = "HTTP/1.0 200 OK\r\n\r\n";
24
25
26

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

27
void directory_initiate_command(routerinfo_t *router, int command) {
28
29
30
31
32
  connection_t *conn;

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

33
34
  if(connection_get_by_type(CONN_TYPE_DIR)) { /* there's already a dir conn running */
    log_fn(LOG_DEBUG,"Canceling connect, dir conn already active.");
35
    return;
36
  }
37

38
39
  if(command == DIR_CONN_STATE_CONNECTING_FETCH)
    log_fn(LOG_DEBUG,"initiating directory fetch");
40
  else
41
    log_fn(LOG_DEBUG,"initiating directory upload");
42

43
44
45
46
47
  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
48
49
  conn->addr = router->addr;
  conn->port = router->dir_port;
50
51
52
  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
53
54
55
  if (router->signing_pkey)
    conn->pkey = crypto_pk_dup_key(router->signing_pkey);
  else {
56
    log_fn(LOG_ERR, "No signing key known for dirserver %s; signature won't be checked", conn->address);
Nick Mathewson's avatar
Nick Mathewson committed
57
58
    conn->pkey = NULL;
  }
59

60
  if(connection_add(conn) < 0) { /* no space, forget it */
61
62
63
64
    connection_free(conn);
    return;
  }

65
66
  switch(connection_connect(conn, router->address, router->addr, router->dir_port)) {
    case -1:
67
      router_forget_router(conn->addr, conn->port); /* XXX don't try him again */
68
69
      connection_free(conn);
      return;
70
71
    case 0:
      connection_set_poll_socket(conn);
72
73
74
      connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
      /* writable indicates finish, readable indicates broken link,
         error indicates broken link in windowsland. */
75
      conn->state = command;
76
      return;
77
    /* case 1: fall through */
78
79
  }

80
  connection_set_poll_socket(conn);
81
  if(directory_send_command(conn, command) < 0) {
82
83
84
85
86
    connection_remove(conn);
    connection_free(conn);
  }
}

87
88
static int directory_send_command(connection_t *conn, int command) {
  char *s;
89
90
91

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

92
  switch(command) {
93
94
95
    case DIR_CONN_STATE_CONNECTING_FETCH:
      if(connection_write_to_buf(fetchstring, strlen(fetchstring), conn) < 0) {
        log_fn(LOG_DEBUG,"Couldn't write fetch to buffer.");
96
97
        return -1;
      }
98
      conn->state = DIR_CONN_STATE_CLIENT_SENDING_FETCH;
99
      break;
100
    case DIR_CONN_STATE_CONNECTING_UPLOAD:
101
102
103
104
105
      s = router_get_my_descriptor();
      if(!s) {
        log_fn(LOG_DEBUG,"Failed to get my descriptor.");
        return -1;
      }
106
      if(connection_write_to_buf(uploadstring, strlen(uploadstring), conn) < 0 ||
107
108
109
110
         connection_write_to_buf(s, strlen(s), conn) < 0) {
        log_fn(LOG_DEBUG,"Couldn't write post/descriptor to buffer.");
        return -1;
      }
111
      conn->state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD;
112
      break;
113
114
115
116
  }
  return 0;
}

117
118
119
void directory_set_dirty(void) {
  directory_dirty = 1;
}
120

Roger Dingledine's avatar
Roger Dingledine committed
121
static void directory_rebuild(void) {
122
  if(directory_dirty) {
123
124
125
126
127
    if (dump_signed_directory_to_string(the_directory, MAX_DIR_SIZE,
                                        get_signing_privatekey())) {
      log(LOG_ERR, "Error writing directory");
      return;
    }
128
    directorylen = strlen(the_directory);
129
    log(LOG_INFO,"New directory (size %d):\n%s",directorylen,the_directory);
130
131
132
133
    directory_dirty = 0;
  } else {
    log(LOG_INFO,"Directory still clean, reusing.");
  }
134
135
136
137
138
139
140
}

int connection_dir_process_inbuf(connection_t *conn) {

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

  if(conn->inbuf_reached_eof) {
141
    switch(conn->state) {
142
      case DIR_CONN_STATE_CLIENT_READING_FETCH:
143
        /* kill it, but first process the_directory and learn about new routers. */
144
145
        switch(fetch_from_buf_http(conn->inbuf,&conn->inbuf_datalen,
                                   NULL, 0, the_directory, MAX_DIR_SIZE)) {
146
          case -1: /* overflow */
147
            log_fn(LOG_DEBUG,"'fetch' response too large. Failing.");
148
149
            return -1;
          case 0:
150
            log_fn(LOG_DEBUG,"'fetch' response not all here, but we're at eof. Closing.");
151
152
153
            return -1;
          /* case 1, fall through */
        }
154
        /* XXX check headers, at least make sure returned 2xx */
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
        directorylen = strlen(the_directory);
        log_fn(LOG_DEBUG,"Received directory (size %d):\n%s", directorylen, the_directory);
        if(directorylen == 0) {
          log_fn(LOG_DEBUG,"Empty directory. Ignoring.");
          return -1;
        }
        if(router_get_dir_from_string(the_directory, conn->pkey) < 0) {
          log_fn(LOG_DEBUG,"...but parsing failed. Ignoring.");
        } else {
          log_fn(LOG_DEBUG,"and got an %s directory; updated routers.", 
              conn->pkey ? "authenticated" : "unauthenticated");
        }
        if(options.OnionRouter) { /* connect to them all */
          router_retry_connections();
        }
        return -1;
171
      case DIR_CONN_STATE_CLIENT_READING_UPLOAD:
172
        /* XXX make sure there's a 200 OK on the buffer */
173
        log_fn(LOG_DEBUG,"eof while reading upload response. Finished.");
174
175
176
177
        return -1;
      default:
        log_fn(LOG_DEBUG,"conn reached eof, not reading. Closing.");
        return -1;
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
178
    }
179
180
  }

181
182
183
184
  if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT)
    return directory_handle_command(conn);

  /* XXX for READ states, might want to make sure inbuf isn't too big */
185

186
  log_fn(LOG_DEBUG,"Got data, not eof. Leaving on inbuf.");
187
188
189
  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
190
static int directory_handle_command(connection_t *conn) {
191
192
  char headers[1024];
  char body[1024];
193
194
195

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

196
197
  switch(fetch_from_buf_http(conn->inbuf,&conn->inbuf_datalen,
                             headers, sizeof(headers), body, sizeof(body))) {
198
199
200
201
202
203
204
    case -1: /* overflow */
      log_fn(LOG_DEBUG,"input too large. Failing.");
      return -1;
    case 0:
      log_fn(LOG_DEBUG,"command not all here yet.");
      return 0;
    /* case 1, fall through */
205
206
  }

207
208
  log_fn(LOG_DEBUG,"headers '%s', body '%s'.",headers,body);
  if(!strncasecmp(headers,"GET",3)) {
209
    /* XXX should check url and http version */
210

211
    directory_rebuild(); /* rebuild it now, iff it's dirty */
212

213
214
215
216
    if(directorylen == 0) {
      log_fn(LOG_DEBUG,"My directory is empty. Closing.");
      return -1;
    }
217

218
219
220
221
222
223
224
225
    log_fn(LOG_DEBUG,"Dumping directory to client."); 
    if((connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) ||
       (connection_write_to_buf(the_directory, directorylen, conn) < 0)) {
      log_fn(LOG_DEBUG,"Failed to write answerstring+directory to outbuf.");
      return -1;
    }
    conn->state = DIR_CONN_STATE_SERVER_WRITING;
    return 0;
226
227
  }

228
  if(!strncasecmp(headers,"POST",4)) {
229
    /* XXX should check url and http version */
230
231
232
233
234
235
236
    log_fn(LOG_DEBUG,"Received POST command, body '%s'", body);
    if(connection_write_to_buf(answerstring, strlen(answerstring), conn) < 0) {
      log_fn(LOG_DEBUG,"Failed to write answerstring to outbuf.");
      return -1;
    }
    conn->state = DIR_CONN_STATE_SERVER_WRITING;
    return 0;
237
238
  }

239
240
  log_fn(LOG_DEBUG,"Got headers with unknown command. Closing.");
  return -1;
241
242
243
244
245
246
247
248
}

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

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

  switch(conn->state) {
249
250
    case DIR_CONN_STATE_CONNECTING_FETCH:
    case DIR_CONN_STATE_CONNECTING_UPLOAD:
251
      if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0)  { /* not yet */
252
        if(!ERRNO_CONN_EINPROGRESS(errno)) {
253
          log_fn(LOG_DEBUG,"in-progress connect failed. Removing.");
254
          router_forget_router(conn->addr, conn->port); /* don't try him again */
255
256
257
258
259
260
261
          return -1;
        } else {
          return 0; /* no change, see if next time is better */
        }
      }
      /* the connect has finished. */

262
      log_fn(LOG_DEBUG,"Dir connection to router %s:%u established.",
263
264
          conn->address,conn->port);

265
      return directory_send_command(conn, conn->state);
266
267
268
    case DIR_CONN_STATE_CLIENT_SENDING_FETCH:
      log_fn(LOG_DEBUG,"client finished sending fetch command.");
      conn->state = DIR_CONN_STATE_CLIENT_READING_FETCH;
269
270
      connection_watch_events(conn, POLLIN);
      return 0;
271
272
273
    case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD:
      log_fn(LOG_DEBUG,"client finished sending upload command.");
      conn->state = DIR_CONN_STATE_CLIENT_READING_UPLOAD;
274
275
      connection_watch_events(conn, POLLIN);
      return 0;
276
277
    case DIR_CONN_STATE_SERVER_WRITING:
      log_fn(LOG_DEBUG,"Finished writing server response. Closing.");
278
279
      return -1; /* kill it */
    default:
280
      log_fn(LOG_DEBUG,"BUG: called in unexpected state.");
281
282
283
284
285
286
      return 0;
  }

  return 0;
}

287
288
289
290
291
292
293
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/