terminology shift:
[tor.git] / src / or / dirserv.c
blobab46d16d735515dbcba7258cc0bb111d0b586d1e
1 /* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2 /* See LICENSE for licensing information */
3 /* $Id$ */
5 #include "or.h"
7 /* How far in the future do we allow a router to get? (seconds) */
8 #define ROUTER_ALLOW_SKEW (30*60)
10 extern or_options_t options; /* command-line and config-file options */
12 static int the_directory_is_dirty = 1;
13 /* XXX the_directory is the same name as a different variable in
14 * directory.c, are you crazy?? */
15 static char *the_directory = NULL;
16 static int the_directory_len = -1;
18 static int list_running_servers(char **nicknames_out);
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 static void
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 return 0;
104 /* error */
105 log_fn(LOG_WARN, "Error reading from fingerprint file");
106 for (i = 0; i < n_fingerprints_tmp; ++i) {
107 free(fingerprint_list_tmp[i].nickname);
108 free(fingerprint_list_tmp[i].fingerprint);
110 return -1;
113 /* return 1 if router's identity and nickname match,
114 * -1 if they don't match, 0 if the nickname is not known. */
116 dirserv_router_fingerprint_is_known(const routerinfo_t *router)
118 int i;
119 fingerprint_entry_t *ent =NULL;
120 char fp[FINGERPRINT_LEN+1];
122 log_fn(LOG_DEBUG, "%d fingerprints known.", n_fingerprints);
123 for (i=0;i<n_fingerprints;++i) {
124 log_fn(LOG_DEBUG,"%s vs %s", router->nickname, fingerprint_list[i].nickname);
125 if (!strcasecmp(router->nickname,fingerprint_list[i].nickname)) {
126 ent = &fingerprint_list[i];
127 break;
131 if (!ent) { /* No such server known */
132 log_fn(LOG_INFO,"no fingerprint found for %s",router->nickname);
133 return 0;
135 if (crypto_pk_get_fingerprint(router->identity_pkey, fp)) {
136 log_fn(LOG_WARN,"error computing fingerprint");
137 return -1;
139 if (0==strcasecmp(ent->fingerprint, fp)) {
140 log_fn(LOG_DEBUG,"good fingerprint for %s",router->nickname);
141 return 1; /* Right fingerprint. */
142 } else {
143 log_fn(LOG_WARN,"mismatched fingerprint for %s",router->nickname);
144 return -1; /* Wrong fingerprint. */
148 void
149 dirserv_free_fingerprint_list()
151 int i;
152 for (i = 0; i < n_fingerprints; ++i) {
153 free(fingerprint_list[i].nickname);
154 free(fingerprint_list[i].fingerprint);
156 n_fingerprints = 0;
160 * Descriptor list
162 typedef struct descriptor_entry_t {
163 char *nickname;
164 time_t published;
165 size_t desc_len;
166 char *descriptor;
167 } descriptor_entry_t;
169 static descriptor_entry_t *descriptor_list[MAX_ROUTERS_IN_DIR];
170 static int n_descriptors = 0;
172 static void free_descriptor_entry(descriptor_entry_t *desc)
174 tor_free(desc->descriptor);
175 tor_free(desc->nickname);
176 free(desc);
179 void
180 dirserv_free_descriptors()
182 int i;
183 for (i = 0; i < n_descriptors; ++i) {
184 free_descriptor_entry(descriptor_list[i]);
186 n_descriptors = 0;
189 /* Return 0 if descriptor is well-formed; -1 if descriptor is not
190 * well-formed. Update *desc to point after the descriptor if the
191 * descriptor is well-formed.
193 /* XXX down the road perhaps we should return 1 for accepted, 0 for
194 * well-formed but rejected, -1 for not-well-formed. So remote servers
195 * can know if their submission was accepted and not just whether it
196 * was well-formed. ...Or maybe we shouldn't give them that info?
199 dirserv_add_descriptor(const char **desc)
201 descriptor_entry_t **desc_ent_ptr;
202 routerinfo_t *ri = NULL;
203 int i, r;
204 char *start, *end;
205 char *desc_tmp = NULL, *cp;
206 size_t desc_len;
208 start = strstr(*desc, "router ");
209 if (!start) {
210 log(LOG_WARN, "no descriptor found.");
211 goto err;
213 if ((end = strstr(start+6, "\nrouter "))) {
214 ++end; /* Include NL. */
215 } else if ((end = strstr(start+6, "\ndirectory-signature"))) {
216 ++end;
217 } else {
218 end = start+strlen(start);
220 desc_len = end-start;
221 cp = desc_tmp = tor_malloc(desc_len+1);
222 strncpy(desc_tmp, start, desc_len);
223 desc_tmp[desc_len]='\0';
225 /* Check: is the descriptor syntactically valid? */
226 ri = router_get_entry_from_string(&cp);
227 if (!ri) {
228 log(LOG_WARN, "Couldn't parse descriptor");
229 goto err;
231 tor_free(desc_tmp);
232 /* Okay. Now check whether the fingerprint is recognized. */
233 r = dirserv_router_fingerprint_is_known(ri);
234 if(r<1) {
235 if(r==0) {
236 log_fn(LOG_WARN, "Unknown nickname %s. Not adding.", ri->nickname);
237 } else {
238 log_fn(LOG_WARN, "Known nickname %s, wrong fingerprint. Not adding.", ri->nickname);
240 routerinfo_free(ri);
241 *desc = end;
242 return 0;
244 /* Is there too much clock skew? */
245 if (ri->published_on > time(NULL)+ROUTER_ALLOW_SKEW) {
246 log_fn(LOG_WARN, "Publication time for nickname %s is too far in the future; possible clock skew. Not adding", ri->nickname);
247 routerinfo_free(ri);
248 *desc = end;
249 return 0;
251 /* Do we already have an entry for this router? */
252 desc_ent_ptr = NULL;
253 for (i = 0; i < n_descriptors; ++i) {
254 if (!strcasecmp(ri->nickname, descriptor_list[i]->nickname)) {
255 desc_ent_ptr = &descriptor_list[i];
256 break;
259 if (desc_ent_ptr) {
260 /* if so, decide whether to update it. */
261 if ((*desc_ent_ptr)->published > ri->published_on) {
262 /* We already have a newer descriptor */
263 log_fn(LOG_INFO,"We already have a newer desc for nickname %s. Not adding.",ri->nickname);
264 /* This isn't really an error; return. */
265 routerinfo_free(ri);
266 *desc = end;
267 return 0;
269 /* We don't have a newer one; we'll update this one. */
270 free_descriptor_entry(*desc_ent_ptr);
271 } else {
272 /* Add this at the end. */
273 desc_ent_ptr = &descriptor_list[n_descriptors++];
274 /* XXX check if n_descriptors is too big */
277 (*desc_ent_ptr) = tor_malloc(sizeof(descriptor_entry_t));
278 (*desc_ent_ptr)->nickname = tor_strdup(ri->nickname);
279 (*desc_ent_ptr)->published = ri->published_on;
280 (*desc_ent_ptr)->desc_len = desc_len;
281 (*desc_ent_ptr)->descriptor = tor_malloc(desc_len+1);
282 strncpy((*desc_ent_ptr)->descriptor, start, desc_len);
283 (*desc_ent_ptr)->descriptor[desc_len] = '\0';
284 *desc = end;
285 the_directory_is_dirty = 1;
287 routerinfo_free(ri);
288 return 0;
289 err:
290 tor_free(desc_tmp);
291 if (ri)
292 routerinfo_free(ri);
294 return -1;
297 void
298 directory_set_dirty()
300 the_directory_is_dirty = 1;
303 int
304 dirserv_init_from_directory_string(const char *dir)
306 const char *cp = dir;
307 while(1) {
308 cp = strstr(cp, "\nrouter ");
309 if (!cp) break;
310 ++cp;
311 if (dirserv_add_descriptor(&cp)) {
312 return -1;
314 --cp; /*Back up to newline.*/
316 return 0;
319 static int
320 list_running_servers(char **nicknames_out)
322 char *nickname_lst[MAX_ROUTERS_IN_DIR];
323 connection_t **connection_array;
324 int n_conns;
325 connection_t *conn;
326 char *cp;
327 int n = 0, i;
328 int length;
329 *nicknames_out = NULL;
330 nickname_lst[n++] = options.Nickname;
332 get_connection_array(&connection_array, &n_conns);
333 for (i = 0; i<n_conns; ++i) {
334 conn = connection_array[i];
335 if (conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN)
336 continue; /* only list successfully handshaked OR's. */
337 if(!conn->nickname) /* it's an OP, don't list it */
338 continue;
339 nickname_lst[n++] = conn->nickname;
341 length = n + 1; /* spaces + EOS + 1. */
342 for (i = 0; i<n; ++i) {
343 length += strlen(nickname_lst[i]);
345 *nicknames_out = tor_malloc_zero(length);
346 cp = *nicknames_out;
347 for (i = 0; i<n; ++i) {
348 if (i)
349 strcat(cp, " ");
350 strcat(cp, nickname_lst[i]);
351 while (*cp)
352 ++cp;
354 return 0;
359 dirserv_dump_directory_to_string(char *s, int maxlen,
360 crypto_pk_env_t *private_key)
362 char *cp, *eos;
363 char digest[20];
364 char signature[128];
365 char published[33];
366 time_t published_on;
367 int i;
368 eos = s+maxlen;
370 if (list_running_servers(&cp))
371 return -1;
372 published_on = time(NULL);
373 strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on));
374 snprintf(s, maxlen,
375 "signed-directory\n"
376 "published %s\n"
377 "recommended-software %s\n"
378 "running-routers %s\n\n", published, options.RecommendedVersions, cp);
379 free(cp);
380 i = strlen(s);
381 cp = s+i;
383 for (i = 0; i < n_descriptors; ++i) {
384 strncat(cp, descriptor_list[i]->descriptor, descriptor_list[i]->desc_len);
385 cp += descriptor_list[i]->desc_len;
386 assert(!*cp);
388 /* These multiple strlen calls are inefficient, but dwarfed by the RSA
389 signature.
391 i = strlen(s);
392 strncat(s, "directory-signature\n", maxlen-i);
393 i = strlen(s);
394 cp = s + i;
396 if (router_get_dir_hash(s,digest)) {
397 log_fn(LOG_WARN,"couldn't compute digest");
398 return -1;
400 if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) {
401 log_fn(LOG_WARN,"couldn't sign digest");
402 return -1;
404 log(LOG_DEBUG,"generated directory digest begins with %02x:%02x:%02x:%02x",
405 ((int)digest[0])&0xff,((int)digest[1])&0xff,
406 ((int)digest[2])&0xff,((int)digest[3])&0xff);
408 strncpy(cp,
409 "-----BEGIN SIGNATURE-----\n", maxlen-i);
411 i = strlen(s);
412 cp = s+i;
413 if (base64_encode(cp, maxlen-i, signature, 128) < 0) {
414 log_fn(LOG_WARN,"couldn't base64-encode signature");
415 return -1;
418 i = strlen(s);
419 cp = s+i;
420 strncat(cp, "-----END SIGNATURE-----\n", maxlen-i);
421 i = strlen(s);
422 if (i == maxlen) {
423 log_fn(LOG_WARN,"tried to exceed string length.");
424 return -1;
427 return 0;
430 size_t dirserv_get_directory(const char **directory)
432 char *new_directory;
433 char filename[512];
434 if (the_directory_is_dirty) {
435 new_directory = tor_malloc(MAX_DIR_SIZE);
436 if (dirserv_dump_directory_to_string(new_directory, MAX_DIR_SIZE,
437 get_identity_key())) {
438 log(LOG_WARN, "Error creating directory.");
439 free(new_directory);
440 return 0;
442 tor_free(the_directory);
443 the_directory = new_directory;
444 the_directory_len = strlen(the_directory);
445 log_fn(LOG_INFO,"New directory (size %d):\n%s",the_directory_len,
446 the_directory);
447 the_directory_is_dirty = 0;
448 /* Now read the directory we just made in order to update our own
449 * router lists. This does more signature checking than is strictly
450 * necessary, but safe is better than sorry. */
451 new_directory = tor_strdup(the_directory);
452 /* use a new copy of the dir, since get_dir_from_string scribbles on it */
453 if (router_set_routerlist_from_directory(new_directory, get_identity_key())) {
454 log_fn(LOG_ERR, "We just generated a directory we can't parse. Dying.");
455 exit(0);
457 free(new_directory);
458 sprintf(filename,"%s/cached-directory", options.DataDirectory);
459 if(write_str_to_file(filename,the_directory) < 0) {
460 log_fn(LOG_WARN, "Couldn't write cached directory to disk. Ignoring.");
462 } else {
463 log(LOG_INFO,"Directory still clean, reusing.");
465 *directory = the_directory;
466 return the_directory_len;