Make Tor build on win32 with VC6 without warnings.
[tor.git] / src / or / dirserv.c
blob631f3d244b7acde6b58229c52af953b1ad234ad8
1 /* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2 /* See LICENSE for licensing information */
3 /* $Id$ */
5 #include "or.h"
7 /* How old do we allow a router to get before removing it? (seconds) */
8 #define ROUTER_MAX_AGE (60*60*24)
10 /* How far in the future do we allow a router to get? (seconds) */
11 #define ROUTER_ALLOW_SKEW (30*60)
13 extern or_options_t options; /* command-line and config-file options */
15 static int the_directory_is_dirty = 1;
17 static int list_running_servers(char **nicknames_out);
18 static void directory_remove_unrecognized(void);
20 /************** Fingerprint handling code ************/
22 typedef struct fingerprint_entry_t {
23 char *nickname;
24 char *fingerprint;
25 } fingerprint_entry_t;
27 static fingerprint_entry_t fingerprint_list[MAX_ROUTERS_IN_DIR];
28 static int n_fingerprints = 0;
30 void /* Should be static; exposed for testing */
31 add_fingerprint_to_dir(const char *nickname, const char *fp)
33 int i;
34 for (i = 0; i < n_fingerprints; ++i) {
35 if (!strcasecmp(fingerprint_list[i].nickname,nickname)) {
36 free(fingerprint_list[i].fingerprint);
37 fingerprint_list[i].fingerprint = tor_strdup(fp);
38 return;
41 fingerprint_list[n_fingerprints].nickname = tor_strdup(nickname);
42 fingerprint_list[n_fingerprints].fingerprint = tor_strdup(fp);
43 ++n_fingerprints;
46 int
47 dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
49 char fp[FINGERPRINT_LEN+1];
50 if (crypto_pk_get_fingerprint(pk, fp)<0) {
51 log_fn(LOG_ERR, "Error computing fingerprint");
52 return -1;
54 add_fingerprint_to_dir(nickname, fp);
55 return 0;
58 /* return 0 on success, -1 on failure */
59 int
60 dirserv_parse_fingerprint_file(const char *fname)
62 FILE *file;
63 char line[FINGERPRINT_LEN+MAX_NICKNAME_LEN+20+1];
64 char *nickname, *fingerprint;
65 fingerprint_entry_t fingerprint_list_tmp[MAX_ROUTERS_IN_DIR];
66 int n_fingerprints_tmp = 0;
67 int i, result;
69 if(!(file = fopen(fname, "r"))) {
70 log_fn(LOG_WARN, "Cannot open fingerprint file %s", fname);
71 return -1;
73 while( (result=parse_line_from_file(line, sizeof(line),file,&nickname,&fingerprint)) > 0) {
74 if (strlen(nickname) > MAX_NICKNAME_LEN) {
75 log(LOG_WARN, "Nickname %s too long in fingerprint file. Skipping.", nickname);
76 continue;
78 if(strlen(fingerprint) != FINGERPRINT_LEN ||
79 !crypto_pk_check_fingerprint_syntax(fingerprint)) {
80 log_fn(LOG_WARN, "Invalid fingerprint (nickname %s, fingerprint %s). Skipping.",
81 nickname, fingerprint);
82 continue;
84 for (i = 0; i < n_fingerprints_tmp; ++i) {
85 if (0==strcasecmp(fingerprint_list_tmp[i].nickname, nickname)) {
86 log(LOG_WARN, "Duplicate nickname %s. Skipping.",nickname);
87 break; /* out of the for. the 'if' below means skip to the next line. */
90 if(i == n_fingerprints_tmp) { /* not a duplicate */
91 fingerprint_list_tmp[n_fingerprints_tmp].nickname = tor_strdup(nickname);
92 fingerprint_list_tmp[n_fingerprints_tmp].fingerprint = tor_strdup(fingerprint);
93 ++n_fingerprints_tmp;
96 fclose(file);
97 if(result == 0) { /* eof; replace the global fingerprints list. */
98 dirserv_free_fingerprint_list();
99 memcpy(fingerprint_list, fingerprint_list_tmp,
100 sizeof(fingerprint_entry_t)*n_fingerprints_tmp);
101 n_fingerprints = n_fingerprints_tmp;
102 /* Delete any routers whose fingerprints we no longer recognize */
103 directory_remove_unrecognized();
104 return 0;
106 /* error */
107 log_fn(LOG_WARN, "Error reading from fingerprint file");
108 for (i = 0; i < n_fingerprints_tmp; ++i) {
109 free(fingerprint_list_tmp[i].nickname);
110 free(fingerprint_list_tmp[i].fingerprint);
112 return -1;
115 /* return 1 if router's identity and nickname match,
116 * -1 if they don't match, 0 if the nickname is not known. */
118 dirserv_router_fingerprint_is_known(const routerinfo_t *router)
120 int i;
121 fingerprint_entry_t *ent =NULL;
122 char fp[FINGERPRINT_LEN+1];
124 log_fn(LOG_DEBUG, "%d fingerprints known.", n_fingerprints);
125 for (i=0;i<n_fingerprints;++i) {
126 log_fn(LOG_DEBUG,"%s vs %s", router->nickname, fingerprint_list[i].nickname);
127 if (!strcasecmp(router->nickname,fingerprint_list[i].nickname)) {
128 ent = &fingerprint_list[i];
129 break;
133 if (!ent) { /* No such server known */
134 log_fn(LOG_INFO,"no fingerprint found for %s",router->nickname);
135 return 0;
137 if (crypto_pk_get_fingerprint(router->identity_pkey, fp)) {
138 log_fn(LOG_WARN,"error computing fingerprint");
139 return -1;
141 if (0==strcasecmp(ent->fingerprint, fp)) {
142 log_fn(LOG_DEBUG,"good fingerprint for %s",router->nickname);
143 return 1; /* Right fingerprint. */
144 } else {
145 log_fn(LOG_WARN,"mismatched fingerprint for %s",router->nickname);
146 return -1; /* Wrong fingerprint. */
150 void
151 dirserv_free_fingerprint_list()
153 int i;
154 for (i = 0; i < n_fingerprints; ++i) {
155 free(fingerprint_list[i].nickname);
156 free(fingerprint_list[i].fingerprint);
158 n_fingerprints = 0;
162 * Descriptor list
164 typedef struct descriptor_entry_t {
165 char *nickname;
166 time_t published;
167 size_t desc_len;
168 char *descriptor;
169 routerinfo_t *router;
170 } descriptor_entry_t;
172 static descriptor_entry_t *descriptor_list[MAX_ROUTERS_IN_DIR];
173 static int n_descriptors = 0;
175 static void free_descriptor_entry(descriptor_entry_t *desc)
177 tor_free(desc->descriptor);
178 tor_free(desc->nickname);
179 routerinfo_free(desc->router);
180 free(desc);
183 void
184 dirserv_free_descriptors()
186 int i;
187 for (i = 0; i < n_descriptors; ++i) {
188 free_descriptor_entry(descriptor_list[i]);
190 n_descriptors = 0;
193 /* Return 1 if descriptor is well-formed and accepted;
194 * 0 if well-formed and server or descriptor is unapproved;
195 * -1 if not well-formed or other error.
197 * Update *desc to point after the descriptor if the
198 * descriptor is well-formed.
201 dirserv_add_descriptor(const char **desc)
203 descriptor_entry_t **desc_ent_ptr;
204 routerinfo_t *ri = NULL;
205 int i, r;
206 char *start, *end;
207 char *desc_tmp = NULL;
208 const char *cp;
209 size_t desc_len;
210 time_t now;
212 start = strstr(*desc, "router ");
213 if (!start) {
214 log_fn(LOG_WARN, "no 'router' line found. This is not a descriptor.");
215 return -1;
217 if ((end = strstr(start+6, "\nrouter "))) {
218 ++end; /* Include NL. */
219 } else if ((end = strstr(start+6, "\ndirectory-signature"))) {
220 ++end;
221 } else {
222 end = start+strlen(start);
224 desc_len = end-start;
225 cp = desc_tmp = tor_strndup(start, desc_len);
227 /* Check: is the descriptor syntactically valid? */
228 ri = router_get_entry_from_string(cp, NULL);
229 tor_free(desc_tmp);
230 if (!ri) {
231 log(LOG_WARN, "Couldn't parse descriptor");
232 return -1;
234 /* Okay. Now check whether the fingerprint is recognized. */
235 r = dirserv_router_fingerprint_is_known(ri);
236 if(r<1) {
237 if(r==0) {
238 char fp[FINGERPRINT_LEN+1];
239 log_fn(LOG_WARN, "Unknown nickname %s (%s:%d). Not adding.",
240 ri->nickname, ri->address, ri->or_port);
241 if (crypto_pk_get_fingerprint(ri->identity_pkey, fp) < 0) {
242 log_fn(LOG_WARN, "Error computing fingerprint for %s", ri->nickname);
243 } else {
244 log_fn(LOG_WARN, "Fingerprint line: %s %s", ri->nickname, fp);
246 } else {
247 log_fn(LOG_WARN, "Known nickname %s, wrong fingerprint. Not adding.", ri->nickname);
249 routerinfo_free(ri);
250 *desc = end;
251 return 0;
253 /* Is there too much clock skew? */
254 now = time(NULL);
255 if (ri->published_on > now+ROUTER_ALLOW_SKEW) {
256 log_fn(LOG_WARN, "Publication time for nickname %s is too far in the future; possible clock skew. Not adding.", ri->nickname);
257 routerinfo_free(ri);
258 *desc = end;
259 return 0;
261 if (ri->published_on < now-ROUTER_MAX_AGE) {
262 log_fn(LOG_WARN, "Publication time for router with nickname %s is too far in the past. Not adding.", ri->nickname);
263 routerinfo_free(ri);
264 *desc = end;
265 return 0;
268 /* Do we already have an entry for this router? */
269 desc_ent_ptr = NULL;
270 for (i = 0; i < n_descriptors; ++i) {
271 if (!strcasecmp(ri->nickname, descriptor_list[i]->nickname)) {
272 desc_ent_ptr = &descriptor_list[i];
273 break;
276 if (desc_ent_ptr) {
277 /* if so, decide whether to update it. */
278 if ((*desc_ent_ptr)->published > ri->published_on) {
279 /* We already have a newer descriptor */
280 log_fn(LOG_INFO,"We already have a newer desc for nickname %s. Not adding.",ri->nickname);
281 /* This isn't really an error; return success. */
282 routerinfo_free(ri);
283 *desc = end;
284 return 1;
286 /* We don't have a newer one; we'll update this one. */
287 log_fn(LOG_INFO,"Dirserv updating desc for nickname %s",ri->nickname);
288 free_descriptor_entry(*desc_ent_ptr);
289 } else {
290 /* Add this at the end. */
291 log_fn(LOG_INFO,"Dirserv adding desc for nickname %s",ri->nickname);
292 desc_ent_ptr = &descriptor_list[n_descriptors++];
293 /* XXX check if n_descriptors is too big */
296 (*desc_ent_ptr) = tor_malloc(sizeof(descriptor_entry_t));
297 (*desc_ent_ptr)->nickname = tor_strdup(ri->nickname);
298 (*desc_ent_ptr)->published = ri->published_on;
299 (*desc_ent_ptr)->desc_len = desc_len;
300 (*desc_ent_ptr)->descriptor = tor_malloc(desc_len+1);
301 strncpy((*desc_ent_ptr)->descriptor, start, desc_len);
302 (*desc_ent_ptr)->descriptor[desc_len] = '\0';
303 (*desc_ent_ptr)->router = ri;
304 *desc = end;
305 directory_set_dirty();
307 return 1;
310 static void
311 directory_remove_unrecognized(void)
313 int i;
314 for (i = 0; i < n_descriptors; ++i) {
315 if (dirserv_router_fingerprint_is_known(descriptor_list[i]->router)<=0) {
316 log(LOG_INFO, "Router %s is no longer recognized",
317 descriptor_list[i]->nickname);
318 free_descriptor_entry(descriptor_list[i]);
319 descriptor_list[i--] = descriptor_list[--n_descriptors];
324 void
325 directory_set_dirty()
327 the_directory_is_dirty = 1;
331 dirserv_init_from_directory_string(const char *dir)
333 const char *cp = dir;
334 while(1) {
335 cp = strstr(cp, "\nrouter ");
336 if (!cp) break;
337 ++cp;
338 if (dirserv_add_descriptor(&cp) < 0) {
339 return -1;
341 --cp; /*Back up to newline.*/
343 return 0;
346 static int
347 list_running_servers(char **nicknames_out)
349 char *nickname_lst[MAX_ROUTERS_IN_DIR];
350 connection_t **connection_array;
351 int n_conns;
352 connection_t *conn;
353 char *cp;
354 int n = 0, i;
355 int length;
356 *nicknames_out = NULL;
357 nickname_lst[n++] = options.Nickname;
359 get_connection_array(&connection_array, &n_conns);
360 for (i = 0; i<n_conns; ++i) {
361 conn = connection_array[i];
362 if (conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN)
363 continue; /* only list successfully handshaked OR's. */
364 if(!conn->nickname) /* it's an OP, don't list it */
365 continue;
366 nickname_lst[n++] = conn->nickname;
368 length = n + 1; /* spaces + EOS + 1. */
369 for (i = 0; i<n; ++i) {
370 length += strlen(nickname_lst[i]);
372 *nicknames_out = tor_malloc_zero(length);
373 cp = *nicknames_out;
374 for (i = 0; i<n; ++i) {
375 if (i)
376 strcat(cp, " ");
377 strcat(cp, nickname_lst[i]); /* can't overflow */
378 while (*cp)
379 ++cp;
381 return 0;
384 /* Remove any descriptors from the directory that are more than ROUTER_MAX_AGE
385 * seconds old.
387 void
388 dirserv_remove_old_servers(void)
390 int i;
391 time_t cutoff;
392 cutoff = time(NULL) - ROUTER_MAX_AGE;
393 for (i = 0; i < n_descriptors; ++i) {
394 if (descriptor_list[i]->published < cutoff) {
395 /* descriptor_list[i] is too old. Remove it. */
396 free_descriptor_entry(descriptor_list[i]);
397 descriptor_list[i] = descriptor_list[n_descriptors-1];
398 --n_descriptors;
399 directory_set_dirty();
400 --i; /* Don't advance the index; consider the new value now at i. */
405 /* Dump all routers currently in the directory into the string <s>, using
406 * at most <maxlen> characters, and signing the directory with <private_key>.
407 * Return 0 on success, -1 on failure.
410 dirserv_dump_directory_to_string(char *s, unsigned int maxlen,
411 crypto_pk_env_t *private_key)
413 char *cp, *eos;
414 char digest[20];
415 char signature[128];
416 char published[33];
417 time_t published_on;
418 int i;
419 eos = s+maxlen;
421 if (list_running_servers(&cp))
422 return -1;
423 dirserv_remove_old_servers();
424 published_on = time(NULL);
425 strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on));
426 snprintf(s, maxlen,
427 "signed-directory\n"
428 "published %s\n"
429 "recommended-software %s\n"
430 "running-routers %s\n\n", published, options.RecommendedVersions, cp);
431 free(cp);
432 i = strlen(s);
433 cp = s+i;
435 for (i = 0; i < n_descriptors; ++i) {
436 if (strlcat(s, descriptor_list[i]->descriptor, maxlen) >= maxlen)
437 goto truncated;
439 /* These multiple strlcat calls are inefficient, but dwarfed by the RSA
440 signature.
442 if (strlcat(s, "directory-signature ", maxlen) >= maxlen)
443 goto truncated;
444 if (strlcat(s, options.Nickname, maxlen) >= maxlen)
445 goto truncated;
446 if (strlcat(s, "\n", maxlen) >= maxlen)
447 goto truncated;
450 if (router_get_dir_hash(s,digest)) {
451 log_fn(LOG_WARN,"couldn't compute digest");
452 return -1;
454 if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) {
455 log_fn(LOG_WARN,"couldn't sign digest");
456 return -1;
458 log(LOG_DEBUG,"generated directory digest begins with %s",hex_str(digest,4));
460 if (strlcat(cp, "-----BEGIN SIGNATURE-----\n", maxlen) >= maxlen)
461 goto truncated;
463 i = strlen(s);
464 cp = s+i;
465 if (base64_encode(cp, maxlen-i, signature, 128) < 0) {
466 log_fn(LOG_WARN,"couldn't base64-encode signature");
467 return -1;
470 if (strlcat(s, "-----END SIGNATURE-----\n", maxlen) >= maxlen)
471 goto truncated;
473 return 0;
474 truncated:
475 log_fn(LOG_WARN,"tried to exceed string length.");
476 return -1;
479 static char *the_directory = NULL;
480 static int the_directory_len = -1;
482 size_t dirserv_get_directory(const char **directory)
484 char *new_directory;
485 char filename[512];
486 if (the_directory_is_dirty) {
487 new_directory = tor_malloc(MAX_DIR_SIZE);
488 if (dirserv_dump_directory_to_string(new_directory, MAX_DIR_SIZE,
489 get_identity_key())) {
490 log(LOG_WARN, "Error creating directory.");
491 free(new_directory);
492 return 0;
494 tor_free(the_directory);
495 the_directory = new_directory;
496 the_directory_len = strlen(the_directory);
497 log_fn(LOG_INFO,"New directory (size %d):\n%s",the_directory_len,
498 the_directory);
499 the_directory_is_dirty = 0;
500 /* Now read the directory we just made in order to update our own
501 * router lists. This does more signature checking than is strictly
502 * necessary, but safe is better than sorry. */
503 new_directory = tor_strdup(the_directory);
504 /* use a new copy of the dir, since get_dir_from_string scribbles on it */
505 if (router_set_routerlist_from_directory(new_directory, get_identity_key())) {
506 log_fn(LOG_ERR, "We just generated a directory we can't parse. Dying.");
507 exit(0);
509 free(new_directory);
510 sprintf(filename,"%s/cached-directory", options.DataDirectory);
511 if(write_str_to_file(filename,the_directory) < 0) {
512 log_fn(LOG_WARN, "Couldn't write cached directory to disk. Ignoring.");
514 } else {
515 log(LOG_INFO,"Directory still clean, reusing.");
517 *directory = the_directory;
518 return the_directory_len;
522 Local Variables:
523 mode:c
524 indent-tabs-mode:nil
525 c-basic-offset:2
526 End: