directory.c 31.5 KB
Newer Older
Roger Dingledine's avatar
Roger Dingledine committed
1
/* Copyright 2001,2002,2003 Roger Dingledine. */
2
3
4
5
6
/* See LICENSE for licensing information */
/* $Id$ */

#include "or.h"

Roger Dingledine's avatar
Roger Dingledine committed
7
8
9
10
/**
 * \file directory.c
 * \brief Implement directory HTTP protocol.
 **/
11

Roger Dingledine's avatar
Roger Dingledine committed
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* In-points to directory.c:
 *
 * - directory_post_to_dirservers(), called from
 *   router_upload_dir_desc_to_dirservers() in router.c
 *   upload_service_descriptor() in rendservice.c
 * - directory_get_from_dirserver(), called from
 *   rend_client_refetch_renddesc() in rendclient.c
 *   run_scheduled_events() in main.c
 *   do_hup() in main.c
 * - connection_dir_process_inbuf(), called from
 *   connection_process_inbuf() in connection.c
 * - connection_dir_finished_flushing(), called from
 *   connection_finished_flushing() in connection.c
 * - connection_dir_finished_connecting(), called from
 *   connection_finished_connecting() in connection.c
 */

static void
30
directory_initiate_command_router(routerinfo_t *router, uint8_t purpose,
31
                                  const char *payload, size_t payload_len);
32
33
static void
directory_initiate_command_trusted_dir(trusted_dir_server_t *dirserv,
34
                      uint8_t purpose, const char *payload, size_t payload_len);
35
36
37
38
39

static void
directory_initiate_command(const char *address, uint32_t addr, uint16_t port,
                           const char *platform,
                           const char *digest, uint8_t purpose,
40
                           const char *payload, size_t payload_len);
41

42
static void
43
44
directory_send_command(connection_t *conn, const char *platform,
                       uint16_t dir_port, int purpose,
45
                       const char *payload, size_t payload_len);
Roger Dingledine's avatar
Roger Dingledine committed
46
47
static int directory_handle_command(connection_t *conn);

48
49
50
51
/********* START VARIABLES **********/

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

52
53
#if 0 /* commented out for now, since for now what clients send is
         different from what servers want to receive */
Roger Dingledine's avatar
Roger Dingledine committed
54
/** URL for publishing rendezvous descriptors. */
55
char rend_publish_string[] = "/tor/rendezvous/publish";
Roger Dingledine's avatar
Roger Dingledine committed
56
/** Prefix for downloading rendezvous descriptors. */
57
char rend_fetch_url[] = "/tor/rendezvous/";
58
#endif
59

60
#define MAX_HEADERS_SIZE 50000
61
#define MAX_BODY_SIZE 500000
62

63
64
#define ALLOW_DIRECTORY_TIME_SKEW 30*60

65
66
/********* END VARIABLES ************/

Roger Dingledine's avatar
Roger Dingledine committed
67
68
69
70
71
72
73
/** Start a connection to every known directory server, using
 * connection purpose 'purpose' and uploading the payload 'payload'
 * (length 'payload_len').  The purpose should be one of
 * 'DIR_PURPOSE_UPLOAD_DIR' or 'DIR_PURPOSE_UPLOAD_RENDDESC'.
 */
void
directory_post_to_dirservers(uint8_t purpose, const char *payload,
74
                             size_t payload_len)
Roger Dingledine's avatar
Roger Dingledine committed
75
{
76
77
  smartlist_t *dirservers;

Roger Dingledine's avatar
Roger Dingledine committed
78
79
  routerinfo_t *router;
  routerlist_t *rl;
80
  char buf[16];
Roger Dingledine's avatar
Roger Dingledine committed
81

82
83
  router_get_trusted_dir_servers(&dirservers);
  tor_assert(dirservers);
Nick Mathewson's avatar
Nick Mathewson committed
84
85
86
  /* This tries dirservers which we believe to be down, but ultimately, that's
   * harmless, and we may as well err on the side of getting things uploaded.
   */
87
88
89
90
91
92
93
94
95
96
97
98
99
  SMARTLIST_FOREACH(dirservers, trusted_dir_server_t *, ds,
    {
      /* Pay attention to fascistfirewall when we're uploading a
       * router descriptor, but not when uploading a service
       * descriptor -- those use Tor. */
      if (options.FascistFirewall && purpose == DIR_PURPOSE_UPLOAD_DIR &&
          !options.HttpProxy) {
        sprintf(buf,"%d",ds->dir_port);
        if (!smartlist_string_isin(options.FirewallPorts, buf))
          continue;
      }
      directory_initiate_command_trusted_dir(ds, purpose, payload, payload_len);
    });
Roger Dingledine's avatar
Roger Dingledine committed
100
101
102
103
104
105
106
107
108
}

/** Start a connection to a random running directory server, using
 * connection purpose 'purpose' requesting 'payload' (length
 * 'payload_len').  The purpose should be one of
 * 'DIR_PURPOSE_FETCH_DIR' or 'DIR_PURPOSE_FETCH_RENDDESC'.
 */
void
directory_get_from_dirserver(uint8_t purpose, const char *payload,
109
                             size_t payload_len)
Roger Dingledine's avatar
Roger Dingledine committed
110
{
111
112
  routerinfo_t *r = NULL;
  trusted_dir_server_t *ds = NULL;
113
114

  if (purpose == DIR_PURPOSE_FETCH_DIR) {
115
116
    if (advertised_server_mode()) {
      /* only ask authdirservers, and don't ask myself */
117
      ds = router_pick_trusteddirserver(1, options.FascistFirewall);
118
119
    } else {
      /* anybody with a non-zero dirport will do */
120
      r = router_pick_directory_server(1, options.FascistFirewall);
121
122
      if (!r) {
        log_fn(LOG_INFO, "No router found for directory; falling back to dirserver list");
123
        ds = router_pick_trusteddirserver(1, options.FascistFirewall);
124
      }
125
126
127
    }
  } else { // (purpose == DIR_PURPOSE_FETCH_RENDDESC)
    /* only ask authdirservers, any of them will do */
128
129
    /* Never use fascistfirewall; we're going via Tor. */
    ds = router_pick_trusteddirserver(0, 0);
130
131
  }

132
133
134
135
136
  if (r)
    directory_initiate_command_router(r, purpose, payload, payload_len);
  else if (ds)
    directory_initiate_command_trusted_dir(ds, purpose, payload, payload_len);
  else
137
    log_fn(LOG_WARN,"No running dirservers known. Not trying. (purpose %d)", purpose);
Roger Dingledine's avatar
Roger Dingledine committed
138
139
}

140

Nick Mathewson's avatar
Nick Mathewson committed
141
142
/** Launch a new connection to the directory server <b>router</b> to upload or
 * download a service or rendezvous descriptor. <b>purpose</b> determines what
143
144
145
 * kind of directory connection we're launching, and must be one of
 * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC}.
 *
Nick Mathewson's avatar
Nick Mathewson committed
146
147
148
 * When uploading, <b>payload</b> and <b>payload_len</b> determine the content
 * of the HTTP post.  When fetching a rendezvous descriptor, <b>payload</b>
 * and <b>payload_len</b> are the service ID we want to fetch.
149
 */
Roger Dingledine's avatar
Roger Dingledine committed
150
static void
151
directory_initiate_command_router(routerinfo_t *router, uint8_t purpose,
152
                                  const char *payload, size_t payload_len)
153
154
155
156
157
158
159
160
{
  directory_initiate_command(router->address, router->addr, router->dir_port,
                             router->platform, router->identity_digest,
                             purpose, payload, payload_len);
}

static void
directory_initiate_command_trusted_dir(trusted_dir_server_t *dirserv,
161
                      uint8_t purpose, const char *payload, size_t payload_len)
162
{
163
  directory_initiate_command(dirserv->address, dirserv->addr,dirserv->dir_port,
164
165
166
167
168
169
170
                        NULL, dirserv->digest, purpose, payload, payload_len);
}

static void
directory_initiate_command(const char *address, uint32_t addr,
                           uint16_t dir_port, const char *platform,
                           const char *digest, uint8_t purpose,
171
                           const char *payload, size_t payload_len)
Roger Dingledine's avatar
Roger Dingledine committed
172
{
173
174
  connection_t *conn;

175
  tor_assert(address && addr && dir_port && digest);
176

177
  switch (purpose) {
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
    case DIR_PURPOSE_FETCH_DIR:
      log_fn(LOG_DEBUG,"initiating directory fetch");
      break;
    case DIR_PURPOSE_FETCH_RENDDESC:
      log_fn(LOG_DEBUG,"initiating hidden-service descriptor fetch");
      break;
    case DIR_PURPOSE_UPLOAD_DIR:
      log_fn(LOG_DEBUG,"initiating server descriptor upload");
      break;
    case DIR_PURPOSE_UPLOAD_RENDDESC:
      log_fn(LOG_DEBUG,"initiating hidden-service descriptor upload");
      break;
    default:
      log_fn(LOG_ERR, "Unrecognized directory connection purpose.");
      tor_assert(0);
193
  }
194

195
196
197
  conn = connection_new(CONN_TYPE_DIR);

  /* set up conn so it's got all the data we need to remember */
198
199
200
201
  if(options.HttpProxy) {
    conn->addr = options.HttpProxyAddr;
    conn->port = options.HttpProxyPort;
  } else {
202
203
    conn->addr = addr;
    conn->port = dir_port;
204
  }
205
206
207
208
209
210
  conn->address = tor_strdup(address);
  /* conn->nickname = tor_strdup(router->nickname); */
  /* tor_assert(router->identity_pkey); */
  /* conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); */
  /* crypto_pk_get_digest(conn->identity_pkey, conn->identity_digest); */
  memcpy(conn->identity_digest, digest, DIGEST_LEN);
211

Roger Dingledine's avatar
Roger Dingledine committed
212
  conn->purpose = purpose;
213

214
215
216
  /* give it an initial state */
  conn->state = DIR_CONN_STATE_CONNECTING;

Roger Dingledine's avatar
Roger Dingledine committed
217
218
219
220
221
  if(purpose == DIR_PURPOSE_FETCH_DIR ||
     purpose == DIR_PURPOSE_UPLOAD_DIR) {
    /* then we want to connect directly */
    switch(connection_connect(conn, conn->address, conn->addr, conn->port)) {
      case -1:
Nick Mathewson's avatar
Nick Mathewson committed
222
        router_mark_as_down(conn->identity_digest); /* don't try him again */
223
224
        if(purpose == DIR_PURPOSE_FETCH_DIR &&
           !all_trusted_directory_servers_down()) {
225
226
227
          log_fn(LOG_INFO,"Giving up on dirserver %s; trying another.", conn->nickname);
          directory_get_from_dirserver(purpose, payload, payload_len);
        }
228
        connection_free(conn);
Roger Dingledine's avatar
Roger Dingledine committed
229
230
231
232
233
        return;
      case 1:
        conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
        /* fall through */
      case 0:
234
        /* queue the command on the outbuf */
235
236
        directory_send_command(conn, platform, dir_port,
                               purpose, payload, payload_len);
237

Roger Dingledine's avatar
Roger Dingledine committed
238
239
240
241
242
243
        connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
        /* writable indicates finish, readable indicates broken link,
           error indicates broken link in windowsland. */
    }
  } else { /* we want to connect via tor */
    /* make an AP connection
244
     * populate it and add it at the right state
Roger Dingledine's avatar
Roger Dingledine committed
245
246
     * socketpair and hook up both sides
     */
247
248
249
    conn->s = connection_ap_make_bridge(conn->address, conn->port);
    if(conn->s < 0) {
      log_fn(LOG_WARN,"Making AP bridge to dirserver failed.");
250
      connection_mark_for_close(conn);
251
252
      return;
    }
Roger Dingledine's avatar
Roger Dingledine committed
253

254
    conn->state = DIR_CONN_STATE_CLIENT_SENDING;
255
    connection_add(conn);
256
    /* queue the command on the outbuf */
257
258
    directory_send_command(conn, platform, dir_port,
                           purpose, payload, payload_len);
259
    connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
260
261
262
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
263
264
/** Queue an appropriate HTTP command on conn-\>outbuf.  The args
 * <b>purpose</b>, <b>payload</b>, and <b>payload_len</b> are as in
265
266
 * directory_initiate_command.
 */
267
static void
268
269
directory_send_command(connection_t *conn, const char *platform,
                       uint16_t dir_port, int purpose,
270
                       const char *payload, size_t payload_len) {
271
  char tmp[8192];
272
273
274
  char proxystring[128];
  char hoststring[128];
  char url[128];
275
  int use_newer = 0;
276
  char *httpcommand = NULL;
277

Roger Dingledine's avatar
Roger Dingledine committed
278
  tor_assert(conn && conn->type == CONN_TYPE_DIR);
279
  tor_assert(dir_port && conn);
280

281
282
  /* If we don't know the platform, assume it's up-to-date. */
  use_newer = platform ? tor_version_as_new_as(platform, "0.0.9pre1"):1;
283

284
285
  if(dir_port == 80) {
    strlcpy(hoststring, conn->address, sizeof(hoststring));
286
  } else {
287
    sprintf(hoststring, "%s:%d", conn->address, dir_port);
288
289
290
291
292
293
294
  }
  if(options.HttpProxy) {
    sprintf(proxystring, "http://%s", hoststring);
  } else {
    proxystring[0] = 0;
  }

Roger Dingledine's avatar
Roger Dingledine committed
295
296
  switch(purpose) {
    case DIR_PURPOSE_FETCH_DIR:
Roger Dingledine's avatar
Roger Dingledine committed
297
      tor_assert(payload == NULL);
298
      log_fn(LOG_DEBUG, "Asking for %scompressed directory from server running %s",
299
             use_newer?"":"un", platform?platform:"<unknown version>");
300
301
      httpcommand = "GET";
      strlcpy(url, use_newer ? "/tor/dir.z" : "/", sizeof(url));
302
303
304
      break;
    case DIR_PURPOSE_FETCH_RUNNING_LIST:
      tor_assert(payload == NULL);
305
306
      httpcommand = "GET";
      strlcpy(url, use_newer ? "/tor/running-routers" : "/running-routers", sizeof(url));
307
      break;
Roger Dingledine's avatar
Roger Dingledine committed
308
    case DIR_PURPOSE_UPLOAD_DIR:
Roger Dingledine's avatar
Roger Dingledine committed
309
      tor_assert(payload);
310
311
      httpcommand = "POST";
      strlcpy(url, use_newer ? "/tor/" : "/", sizeof(url));
Roger Dingledine's avatar
Roger Dingledine committed
312
      break;
313
    case DIR_PURPOSE_FETCH_RENDDESC:
Roger Dingledine's avatar
Roger Dingledine committed
314
      tor_assert(payload);
315
316

      /* this must be true or we wouldn't be doing the lookup */
Roger Dingledine's avatar
Roger Dingledine committed
317
      tor_assert(payload_len <= REND_SERVICE_ID_LEN);
318
      /* This breaks the function abstraction. */
319
320
321
      memcpy(conn->rend_query, payload, payload_len);
      conn->rend_query[payload_len] = 0;

322
323
      httpcommand = "GET";
      sprintf(url, "%s/rendezvous/%s", use_newer ? "/tor" : "", payload);
324
325
326
327
328
329
330

      /* XXX We're using payload here to mean something other than
       * payload of the http post. This is probably bad, and should
       * be fixed one day. Kludge for now to make sure we don't post more. */
      payload_len = 0;
      payload = NULL;

Roger Dingledine's avatar
Roger Dingledine committed
331
      break;
332
    case DIR_PURPOSE_UPLOAD_RENDDESC:
Roger Dingledine's avatar
Roger Dingledine committed
333
      tor_assert(payload);
334
335
      httpcommand = "POST";
      sprintf(url, "%s/rendezvous/publish", use_newer ? "/tor" : "");
336
      break;
337
  }
338

339
  snprintf(tmp, sizeof(tmp), "%s %s%s HTTP/1.0\r\nContent-Length: %lu\r\nHost: %s\r\n\r\n",
340
341
342
           httpcommand,
           proxystring,
           url,
343
           (unsigned long)payload_len,
344
345
346
           hoststring);
  connection_write_to_buf(tmp, strlen(tmp), conn);

347
  if(payload) {
348
349
350
    /* then send the payload afterwards too */
    connection_write_to_buf(payload, payload_len, conn);
  }
Roger Dingledine's avatar
Roger Dingledine committed
351
352
}

353
354
/** Parse an HTTP request string <b>headers</b> of the form
 * "\%s [http[s]://]\%s HTTP/1..."
355
356
357
 * If it's well-formed, strdup the second \%s into *<b>url</b>, and
 * null-terminate it. If the url doesn't start with "/tor/", rewrite it
 * so it does. Return 0.
Roger Dingledine's avatar
Roger Dingledine committed
358
359
 * Otherwise, return -1.
 */
Roger Dingledine's avatar
Roger Dingledine committed
360
361
362
static int
parse_http_url(char *headers, char **url)
{
363
  char *s, *start, *tmp;
Roger Dingledine's avatar
Roger Dingledine committed
364
365
366
367
368
369
370

  s = (char *)eat_whitespace_no_nl(headers);
  if (!*s) return -1;
  s = (char *)find_whitespace(s); /* get past GET/POST */
  if (!*s) return -1;
  s = (char *)eat_whitespace_no_nl(s);
  if (!*s) return -1;
371
  start = s; /* this is it, assuming it's valid */
372
  s = (char *)find_whitespace(start);
Roger Dingledine's avatar
Roger Dingledine committed
373
  if (!*s) return -1;
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

  /* tolerate the http[s] proxy style of putting the hostname in the url */
  if(s-start >= 4 && !strcmpstart(start,"http")) {
    tmp = start + 4;
    if(*tmp == 's')
      tmp++;
    if(s-tmp >= 3 && !strcmpstart(tmp,"://")) {
      tmp = strchr(tmp+3, '/');
      if(tmp && tmp < s) {
        log_fn(LOG_DEBUG,"Skipping over 'http[s]://hostname' string");
        start = tmp;
      }
    }
  }

389
390
391
  if(s-start < 5 || strcmpstart(start,"/tor/")) { /* need to rewrite it */
    *url = tor_malloc(s - start + 5);
    strcpy(*url,"/tor");
392
393
    strlcpy((*url)+4, start, s-start+1);
    (*url)[s-start+4] = 0; /* null terminate it */
394
395
396
  } else {
    *url = tor_strndup(start, s-start);
  }
397
398
399
  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
400
401
402
/** Parse an HTTP response string <b>headers</b> of the form
 * "HTTP/1.\%d \%d\%s\r\n...".
 * If it's well-formed, assign *<b>code</b>, point *<b>message</b> to the first
403
404
 * non-space character after code if there is one and message is non-NULL
 * (else leave it alone), and return 0.
405
406
 * If <b>date</b> is provided, set *date to the Date header in the
 * http headers, or 0 if no such header is found.
407
408
 * Otherwise, return -1.
 */
Roger Dingledine's avatar
Roger Dingledine committed
409
static int
410
411
parse_http_response(char *headers, int *code, char **message, time_t *date,
                    int *compression)
Roger Dingledine's avatar
Roger Dingledine committed
412
{
413
  int n1, n2;
414
  char datestr[RFC1123_TIME_LEN+1];
415
  smartlist_t *parsed_headers;
Roger Dingledine's avatar
Roger Dingledine committed
416
  tor_assert(headers && code);
417

418
  while(isspace((int)*headers)) headers++; /* tolerate leading whitespace */
419
420
421
422
423
424
425
426
427
428
429

  if(sscanf(headers, "HTTP/1.%d %d", &n1, &n2) < 2 ||
     (n1 != 0 && n1 != 1) ||
     (n2 < 100 || n2 >= 600)) {
    log_fn(LOG_WARN,"Failed to parse header '%s'",headers);
    return -1;
  }
  *code = n2;
  if(message) {
    /* XXX should set *message correctly */
  }
430
431
432
  parsed_headers = smartlist_create();
  smartlist_split_string(parsed_headers, headers, "\n",
                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
433
434
  if (date) {
    *date = 0;
435
436
437
    SMARTLIST_FOREACH(parsed_headers, const char *, s,
      if (!strcmpstart(s, "Date: ")) {
        strlcpy(datestr, s+6, sizeof(datestr));
438
439
440
441
442
        /* This will do nothing on failure, so we don't need to check
           the result.   We shouldn't warn, since there are many other valid
           date formats besides the one we use. */
        parse_rfc1123_time(datestr, date);
        break;
443
444
445
446
447
448
      });
  }
  if (compression) {
    const char *enc = NULL;
    SMARTLIST_FOREACH(parsed_headers, const char *, s,
      if (!strcmpstart(s, "Content-Encoding: ")) {
449
        enc = s+18; break;
450
      });
451
    if (!enc || !strcmp(enc, "identity")) {
452
453
454
455
456
457
458
459
      *compression = 0;
    } else if (!strcmp(enc, "deflate") || !strcmp(enc, "x-deflate")) {
      *compression = ZLIB_METHOD;
    } else if (!strcmp(enc, "gzip") || !strcmp(enc, "x-gzip")) {
      *compression = GZIP_METHOD;
    } else {
      log_fn(LOG_WARN, "Unrecognized content encoding: '%s'", enc);
      *compression = 0;
460
461
    }
  }
462
463
  SMARTLIST_FOREACH(parsed_headers, char *, s, tor_free(s));
  smartlist_free(parsed_headers);
464

465
466
467
  return 0;
}

468
469
470
471
472
/** We are a client, and we've finished reading the server's
 * response. Parse and it and act appropriately.
 *
 * Return -1 if an error has occurred, or 0 normally. The caller
 * will take care of marking the connection for close.
473
 */
474
static int
475
connection_dir_client_reached_eof(connection_t *conn)
476
{
477
  char *body;
478
  char *headers;
479
  size_t body_len=0;
480
  int status_code;
481
482
  time_t now, date_header=0;
  int delta;
483
  int compression;
484

485
486
487
488
489
490
491
492
493
494
495
  switch(fetch_from_buf_http(conn->inbuf,
                             &headers, MAX_HEADERS_SIZE,
                             &body, &body_len, MAX_DIR_SIZE)) {
    case -1: /* overflow */
      log_fn(LOG_WARN,"'fetch' response too large. Failing.");
      return -1;
    case 0:
      log_fn(LOG_INFO,"'fetch' response not all here, but we're at eof. Closing.");
      return -1;
    /* case 1, fall through */
  }
496

497
498
  if(parse_http_response(headers, &status_code, NULL, &date_header,
                         &compression) < 0) {
499
    log_fn(LOG_WARN,"Unparseable headers. Closing.");
Roger Dingledine's avatar
Roger Dingledine committed
500
    tor_free(body); tor_free(headers);
501
502
    return -1;
  }
503
504
505
506
507
508
509
510
511
512
513
  if (date_header > 0) {
    now = time(NULL);
    delta = now-date_header;
    if (abs(delta)>ALLOW_DIRECTORY_TIME_SKEW) {
      log_fn(LOG_WARN, "Received directory with skewed time: we are %d minutes %s, or the directory is %d minutes %s.",
             abs(delta)/60, delta>0 ? "ahead" : "behind",
             abs(delta)/60, delta>0 ? "behind" : "ahead");
    } else {
      log_fn(LOG_INFO, "Time on received directory is within tolerance; we are %d seconds skewed.  (That's okay.)", delta);
    }
  }
514

515
516
517
518
519
520
521
522
523
524
  if (compression != 0) {
    char *new_body;
    size_t new_len;
    if (tor_gzip_uncompress(&new_body, &new_len, body, body_len, compression)) {
      log_fn(LOG_WARN, "Unable to decompress HTTP body.");
      tor_free(body); tor_free(headers);
      return -1;
    }
    tor_free(body);
    body = new_body;
525
    body_len = new_len;
526
527
  }

528
529
  if(conn->purpose == DIR_PURPOSE_FETCH_DIR) {
    /* fetch/process the directory to learn about new routers. */
530
    log_fn(LOG_INFO,"Received directory (size %d):\n%s", (int)body_len, body);
531
532
    if(status_code == 503 || body_len == 0) {
      log_fn(LOG_INFO,"Empty directory. Ignoring.");
Roger Dingledine's avatar
Roger Dingledine committed
533
      tor_free(body); tor_free(headers);
534
535
536
537
538
      return 0;
    }
    if(status_code != 200) {
      log_fn(LOG_WARN,"Received http status code %d from dirserver. Failing.",
             status_code);
Roger Dingledine's avatar
Roger Dingledine committed
539
      tor_free(body); tor_free(headers);
540
541
      return -1;
    }
542
    if(router_load_routerlist_from_directory(body, NULL, 1) < 0){
Roger Dingledine's avatar
Roger Dingledine committed
543
      log_fn(LOG_WARN,"I failed to parse the directory I fetched from %s:%d. Ignoring.", conn->address, conn->port);
544
545
    } else {
      log_fn(LOG_INFO,"updated routers.");
546
    }
547
548
    directory_has_arrived(); /* do things we've been waiting to do */
  }
549

550
551
552
553
  if(conn->purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
    running_routers_t *rrs;
    routerlist_t *rl;
    /* just update our list of running routers, if this list is new info */
554
    log_fn(LOG_INFO,"Received running-routers list (size %d):\n%s", (int)body_len, body);
555
556
557
    if(status_code != 200) {
      log_fn(LOG_WARN,"Received http status code %d from dirserver. Failing.",
             status_code);
Roger Dingledine's avatar
Roger Dingledine committed
558
      tor_free(body); tor_free(headers);
559
560
      return -1;
    }
561
562
    if (!(rrs = router_parse_runningrouters(body))) {
      log_fn(LOG_WARN, "Can't parse runningrouters list");
Roger Dingledine's avatar
Roger Dingledine committed
563
      tor_free(body); tor_free(headers);
564
      return -1;
565
    }
566
    router_get_routerlist(&rl);
567
568
    if (rl)
      routerlist_update_from_runningrouters(rl,rrs);
569
570
    running_routers_free(rrs);
  }
571

572
573
574
575
576
577
578
579
580
581
582
583
584
585
  if(conn->purpose == DIR_PURPOSE_UPLOAD_DIR) {
    switch(status_code) {
      case 200:
        log_fn(LOG_INFO,"eof (status 200) after uploading server descriptor: finished.");
        break;
      case 400:
        log_fn(LOG_WARN,"http status 400 (bad request) response from dirserver. Malformed server descriptor?");
        break;
      case 403:
        log_fn(LOG_WARN,"http status 403 (unapproved server) response from dirserver. Is your clock skewed? Have you mailed us your identity fingerprint? Are you using the right key? See README.");
        break;
      default:
        log_fn(LOG_WARN,"http status %d response unrecognized.", status_code);
        break;
586
    }
587
  }
588

589
590
  if(conn->purpose == DIR_PURPOSE_FETCH_RENDDESC) {
    log_fn(LOG_INFO,"Received rendezvous descriptor (size %d, status code %d)",
591
           (int)body_len, status_code);
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
    switch(status_code) {
      case 200:
        if(rend_cache_store(body, body_len) < 0) {
          log_fn(LOG_WARN,"Failed to store rendezvous descriptor.");
          /* alice's ap_stream will notice when connection_mark_for_close
           * cleans it up */
        } else {
          /* success. notify pending connections about this. */
          rend_client_desc_fetched(conn->rend_query, 1);
          conn->purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
        }
        break;
      case 404:
        /* not there. pending connections will be notified when
         * connection_mark_for_close cleans it up. */
        break;
      case 400:
        log_fn(LOG_WARN,"http status 400 (bad request). Dirserver didn't like our rendezvous query?");
        break;
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
611
    }
612
  }
Roger Dingledine's avatar
Roger Dingledine committed
613

614
615
616
617
618
619
620
621
622
623
624
  if(conn->purpose == DIR_PURPOSE_UPLOAD_RENDDESC) {
    switch(status_code) {
      case 200:
        log_fn(LOG_INFO,"eof (status 200) after uploading rendezvous descriptor: finished.");
        break;
      case 400:
        log_fn(LOG_WARN,"http status 400 (bad request) response from dirserver. Malformed rendezvous descriptor?");
        break;
      default:
        log_fn(LOG_WARN,"http status %d response unrecognized.", status_code);
        break;
Roger Dingledine's avatar
Roger Dingledine committed
625
    }
626
  }
Roger Dingledine's avatar
Roger Dingledine committed
627
  tor_free(body); tor_free(headers);
628
629
630
631
632
633
634
635
  return 0;
}

/** Read handler for directory connections.  (That's connections <em>to</em>
 * directory servers and connections <em>at</em> directory servers.)
 */
int connection_dir_process_inbuf(connection_t *conn) {
  int retval;
Roger Dingledine's avatar
Roger Dingledine committed
636

637
638
639
640
641
642
643
644
645
646
647
648
649
  tor_assert(conn && conn->type == CONN_TYPE_DIR);

  /* Directory clients write, then read data until they receive EOF;
   * directory servers read data until they get an HTTP command, then
   * write their response (when it's finished flushing, they mark for
   * close).
   */
  if(conn->inbuf_reached_eof) {
    if(conn->state != DIR_CONN_STATE_CLIENT_READING) {
      log_fn(LOG_INFO,"conn reached eof, not reading. Closing.");
      connection_close_immediate(conn); /* it was an error; give up on flushing */
      connection_mark_for_close(conn);
      return -1;
Roger Dingledine's avatar
Roger Dingledine committed
650
    }
651

652
    retval = connection_dir_client_reached_eof(conn);
653
    connection_mark_for_close(conn);
654
    return retval;
655
  } /* endif 'reached eof' */
656

657
  /* If we're on the dirserver side, look for a command. */
Roger Dingledine's avatar
Roger Dingledine committed
658
659
  if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) {
    if (directory_handle_command(conn) < 0) {
660
      connection_mark_for_close(conn);
Roger Dingledine's avatar
Roger Dingledine committed
661
662
      return -1;
    }
663
    return 0;
Roger Dingledine's avatar
Roger Dingledine committed
664
  }
665
666

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

668
  log_fn(LOG_DEBUG,"Got data, not eof. Leaving on inbuf.");
669
670
671
  return 0;
}

672
673
674
static char answer200[] = "HTTP/1.0 200 OK\r\n\r\n";
static char answer400[] = "HTTP/1.0 400 Bad request\r\n\r\n";
static char answer403[] = "HTTP/1.0 403 Unapproved server\r\n\r\n";
Roger Dingledine's avatar
Roger Dingledine committed
675
static char answer404[] = "HTTP/1.0 404 Not found\r\n\r\n";
676
677
static char answer503[] = "HTTP/1.0 503 Directory unavailable\r\n\r\n";

Roger Dingledine's avatar
Roger Dingledine committed
678
/** Helper function: called when a dirserver gets a complete HTTP GET
679
680
 * request.  Look for a request for a directory or for a rendezvous
 * service descriptor.  On finding one, write a response into
681
 * conn-\>outbuf.  If the request is unrecognized, send a 400.
682
 * Always return 0. */
Roger Dingledine's avatar
Roger Dingledine committed
683
684
static int
directory_handle_command_get(connection_t *conn, char *headers,
685
                             char *body, size_t body_len)
Roger Dingledine's avatar
Roger Dingledine committed
686
{
Roger Dingledine's avatar
Roger Dingledine committed
687
  size_t dlen;
688
  const char *cp;
Roger Dingledine's avatar
Roger Dingledine committed
689
  char *url;
690
  char tmp[8192];
691
  char date[RFC1123_TIME_LEN+1];
692

693
694
  log_fn(LOG_DEBUG,"Received GET command.");

Roger Dingledine's avatar
Roger Dingledine committed
695
  conn->state = DIR_CONN_STATE_SERVER_WRITING;
696

Roger Dingledine's avatar
Roger Dingledine committed
697
698
  if (parse_http_url(headers, &url) < 0) {
    connection_write_to_buf(answer400, strlen(answer400), conn);
699
    return 0;
700
  }
701
  log_fn(LOG_INFO,"rewritten url as '%s'.", url);
702

703
704
  if(!strcmp(url,"/tor/") || !strcmp(url,"/tor/dir.z")) { /* directory fetch */
    int deflated = !strcmp(url,"/tor/dir.z");
705
    dlen = dirserv_get_directory(&cp, deflated);
Roger Dingledine's avatar
Roger Dingledine committed
706

707
708
    tor_free(url);

Roger Dingledine's avatar
Roger Dingledine committed
709
710
711
712
713
714
    if(dlen == 0) {
      log_fn(LOG_WARN,"My directory is empty. Closing.");
      connection_write_to_buf(answer503, strlen(answer503), conn);
      return 0;
    }

715
716
    log_fn(LOG_DEBUG,"Dumping %sdirectory to client.", 
           deflated?"deflated ":"");
717
    format_rfc1123_time(date, time(NULL));
718
    snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: text/plain\r\nContent-Encoding: %s\r\n\r\n",
719
             date,
720
             (int)dlen,
721
             deflated?"deflate":"identity");
722
    connection_write_to_buf(tmp, strlen(tmp), conn);
723
    connection_write_to_buf(cp, dlen, conn);
Roger Dingledine's avatar
Roger Dingledine committed
724
725
726
    return 0;
  }

727
728
  if(!strcmp(url,"/tor/running-routers")) { /* running-routers fetch */
    tor_free(url);
729
730
731
732
733
    if(!authdir_mode()) {
      /* XXX008 for now, we don't cache running-routers. Reject. */
      connection_write_to_buf(answer400, strlen(answer400), conn);
      return 0;
    }
734
    dlen = dirserv_get_runningrouters(&cp);
735
    if(!dlen) { /* we failed to create cp */
Roger Dingledine's avatar
Roger Dingledine committed
736
737
738
      connection_write_to_buf(answer503, strlen(answer503), conn);
      return 0;
    }
739

740
    format_rfc1123_time(date, time(NULL));
741
742
    snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: text/plain\r\n\r\n",
             date,
743
744
745
746
747
748
             (int)dlen);
    connection_write_to_buf(tmp, strlen(tmp), conn);
    connection_write_to_buf(cp, strlen(cp), conn);
    return 0;
  }

749
  if(!strcmpstart(url,"/tor/rendezvous/")) {
750
    /* rendezvous descriptor fetch */
751
    const char *descp;
752
    size_t desc_len;
Roger Dingledine's avatar
Roger Dingledine committed
753

754
755
756
757
758
759
760
    if(!authdir_mode()) {
      /* We don't hand out rend descs. In fact, it could be a security
       * risk, since rend_cache_lookup_desc() below would provide it
       * if we're gone to the site recently, and 404 if we haven't.
       *
       * Reject. */
      connection_write_to_buf(answer400, strlen(answer400), conn);
761
      tor_free(url);
762
763
      return 0;
    }
764
    switch(rend_cache_lookup_desc(url+strlen("/tor/rendezvous/"), &descp, &desc_len)) {
765
      case 1: /* valid */
766
        format_rfc1123_time(date, time(NULL));
767
768
        snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: application/octet-stream\r\n\r\n",
                 date,
769
                 (int)desc_len); /* can't include descp here, because it's got nuls */
770
771
        connection_write_to_buf(tmp, strlen(tmp), conn);
        connection_write_to_buf(descp, desc_len, conn);
772
773
774
775
776
777
778
779
        break;
      case 0: /* well-formed but not present */
        connection_write_to_buf(answer404, strlen(answer404), conn);
        break;
      case -1: /* not well-formed */
        connection_write_to_buf(answer400, strlen(answer400), conn);
        break;
    }
780
    tor_free(url);
781
    return 0;
Roger Dingledine's avatar
Roger Dingledine committed
782
783
784
785
  }

  /* we didn't recognize the url */
  connection_write_to_buf(answer404, strlen(answer404), conn);
786
  tor_free(url);
787
788
789
  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
790
/** Helper function: called when a dirserver gets a complete HTTP POST
791
792
 * request.  Look for an uploaded server descriptor or rendezvous
 * service descriptor.  On finding one, process it and write a
Nick Mathewson's avatar
Nick Mathewson committed
793
 * response into conn-\>outbuf.  If the request is unrecognized, send a
794
 * 400.  Always return 0. */
Roger Dingledine's avatar
Roger Dingledine committed
795
796
static int
directory_handle_command_post(connection_t *conn, char *headers,
797
                                         char *body, size_t body_len)
Roger Dingledine's avatar
Roger Dingledine committed
798
{
799
  const char *cp;
Roger Dingledine's avatar
Roger Dingledine committed
800
  char *url;
801
802

  log_fn(LOG_DEBUG,"Received POST command.");
Roger Dingledine's avatar
Roger Dingledine committed
803

804
  conn->state = DIR_CONN_STATE_SERVER_WRITING;
Roger Dingledine's avatar
Roger Dingledine committed
805

806
807
808
809
810
811
812
  if(!authdir_mode()) {
    /* we just provide cached directories; we don't want to
     * receive anything. */
    connection_write_to_buf(answer400, strlen(answer400), conn);
    return 0;
  }

Roger Dingledine's avatar
Roger Dingledine committed
813
814
815
816
  if (parse_http_url(headers, &url) < 0) {
    connection_write_to_buf(answer400, strlen(answer400), conn);
    return 0;
  }
817
  log_fn(LOG_INFO,"rewritten url as '%s'.", url);
Roger Dingledine's avatar
Roger Dingledine committed
818

819
  if(!strcmp(url,"/tor/")) { /* server descriptor post */
Roger Dingledine's avatar
Roger Dingledine committed
820
821
822
823
824
825
826
827
828
829
830
    cp = body;
    switch(dirserv_add_descriptor(&cp)) {
      case -1:
        /* malformed descriptor, or something wrong */
        connection_write_to_buf(answer400, strlen(answer400), conn);
        break;
      case 0:
        /* descriptor was well-formed but server has not been approved */
        connection_write_to_buf(answer403, strlen(answer403), conn);
        break;
      case 1:
831
        dirserv_get_directory(&cp, 0); /* rebuild and write to disk */
Roger Dingledine's avatar
Roger Dingledine committed
832
833
834
        connection_write_to_buf(answer200, strlen(answer200), conn);
        break;
    }
835
    tor_free(url);
836
    return 0;
Roger Dingledine's avatar
Roger Dingledine committed
837
838
  }

839
  if(!strcmpstart(url,"/tor/rendezvous/publish")) {
840
    /* rendezvous descriptor post */
Nick Mathewson's avatar
Nick Mathewson committed
841
    if(rend_cache_store(body, body_len) < 0)
842
843
844
      connection_write_to_buf(answer400, strlen(answer400), conn);
    else
      connection_write_to_buf(answer200, strlen(answer200), conn);
845
    tor_free(url);
846
    return 0;
Roger Dingledine's avatar
Roger Dingledine committed
847
848
849
850
  }

  /* we didn't recognize the url */
  connection_write_to_buf(answer404, strlen(answer404), conn);
851
  tor_free(url);
852
853
854
  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
855
/** Called when a dirserver receives data on a directory connection;
856
857
858
859
 * looks for an HTTP request.  If the request is complete, remove it
 * from the inbuf, try to process it; otherwise, leave it on the
 * buffer.  Return a 0 on success, or -1 on error.
 */
860
861
static int directory_handle_command(connection_t *conn) {
  char *headers=NULL, *body=NULL;
862
  size_t body_len=0;
863
864
  int r;

Roger Dingledine's avatar
Roger Dingledine committed
865
  tor_assert(conn && conn->type == CONN_TYPE_DIR);
866

867
  switch(fetch_from_buf_http(conn->inbuf,
868
869
                             &headers, MAX_HEADERS_SIZE,
                             &body, &body_len, MAX_BODY_SIZE)) {
870
    case -1: /* overflow */
871
      log_fn(LOG_WARN,"Invalid input. Closing.");
872
873
874
875
876
      return -1;
    case 0:
      log_fn(LOG_DEBUG,"command not all here yet.");
      return 0;
    /* case 1, fall through */
877
878
  }

879
  log_fn(LOG_DEBUG,"headers '%s', body '%s'.", headers, body);
880

881
  if(!strncasecmp(headers,"GET",3))
882
    r = directory_handle_command_get(conn, headers, body, body_len);
883
  else if (!strncasecmp(headers,"POST",4))
884
    r = directory_handle_command_post(conn, headers, body, body_len);
885
886
887
  else {
    log_fn(LOG_WARN,"Got headers '%s' with unknown command. Closing.", headers);
    r = -1;
888
889
  }

890
891
  tor_free(headers); tor_free(body);
  return r;
892
893
}

Roger Dingledine's avatar
Roger Dingledine committed
894
/** Write handler for directory connections; called when all data has
895
896
 * been flushed.  Close the connection or wait for a response as
 * appropriate.
897
 */
898
899
int connection_dir_finished_flushing(connection_t *conn) {

Roger Dingledine's avatar
Roger Dingledine committed
900
  tor_assert(conn && conn->type == CONN_TYPE_DIR);
901
902

  switch(conn->state) {
Roger Dingledine's avatar
Roger Dingledine committed
903
904
905
906
    case DIR_CONN_STATE_CLIENT_SENDING:
      log_fn(LOG_DEBUG,"client finished sending command.");
      conn->state = DIR_CONN_STATE_CLIENT_READING;
      connection_stop_writing(conn);
907
      return 0;
908
    case DIR_CONN_STATE_SERVER_WRITING:
909
      log_fn(LOG_INFO,"Finished writing server response. Closing.");
910
      connection_mark_for_close(conn);
911
      return 0;
912
    default:
913
      log_fn(LOG_WARN,"BUG: called in unexpected state %d.", conn->state);
914
      return -1;
915
916
917
918
  }
  return 0;
}

919
920
921
922
923
924
925
926
927
928
929
930
931
932
/** Connected handler for directory connections: begin sending data to the
 * server */
int connection_dir_finished_connecting(connection_t *conn)
{
  tor_assert(conn && conn->type == CONN_TYPE_DIR);
  tor_assert(conn->state == DIR_CONN_STATE_CONNECTING);

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

  conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
  return 0;
}

933
934
935
936
937
938
939
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/