1 /* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2 /* See LICENSE for licensing information */
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
{
25 } fingerprint_entry_t
;
27 static fingerprint_entry_t fingerprint_list
[MAX_ROUTERS_IN_DIR
];
28 static int n_fingerprints
= 0;
31 add_fingerprint_to_dir(const char *nickname
, const char *fp
)
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
);
41 fingerprint_list
[n_fingerprints
].nickname
= tor_strdup(nickname
);
42 fingerprint_list
[n_fingerprints
].fingerprint
= tor_strdup(fp
);
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");
54 add_fingerprint_to_dir(nickname
, fp
);
58 /* return 0 on success, -1 on failure */
60 dirserv_parse_fingerprint_file(const char *fname
)
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;
69 if(!(file
= fopen(fname
, "r"))) {
70 log_fn(LOG_WARN
, "Cannot open fingerprint file %s", fname
);
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
);
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
);
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
);
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
;
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
);
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
)
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
];
131 if (!ent
) { /* No such server known */
132 log_fn(LOG_INFO
,"no fingerprint found for %s",router
->nickname
);
135 if (crypto_pk_get_fingerprint(router
->identity_pkey
, fp
)) {
136 log_fn(LOG_WARN
,"error computing fingerprint");
139 if (0==strcasecmp(ent
->fingerprint
, fp
)) {
140 log_fn(LOG_DEBUG
,"good fingerprint for %s",router
->nickname
);
141 return 1; /* Right fingerprint. */
143 log_fn(LOG_WARN
,"mismatched fingerprint for %s",router
->nickname
);
144 return -1; /* Wrong fingerprint. */
149 dirserv_free_fingerprint_list()
152 for (i
= 0; i
< n_fingerprints
; ++i
) {
153 free(fingerprint_list
[i
].nickname
);
154 free(fingerprint_list
[i
].fingerprint
);
162 typedef struct descriptor_entry_t
{
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
);
180 dirserv_free_descriptors()
183 for (i
= 0; i
< n_descriptors
; ++i
) {
184 free_descriptor_entry(descriptor_list
[i
]);
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
;
205 char *desc_tmp
= NULL
, *cp
;
208 start
= strstr(*desc
, "router ");
210 log(LOG_WARN
, "no descriptor found.");
213 if ((end
= strstr(start
+6, "\nrouter "))) {
214 ++end
; /* Include NL. */
215 } else if ((end
= strstr(start
+6, "\ndirectory-signature"))) {
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
);
228 log(LOG_WARN
, "Couldn't parse descriptor");
232 /* Okay. Now check whether the fingerprint is recognized. */
233 r
= dirserv_router_fingerprint_is_known(ri
);
236 log_fn(LOG_WARN
, "Unknown nickname %s. Not adding.", ri
->nickname
);
238 log_fn(LOG_WARN
, "Known nickname %s, wrong fingerprint. Not adding.", ri
->nickname
);
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
);
251 /* Do we already have an entry for this router? */
253 for (i
= 0; i
< n_descriptors
; ++i
) {
254 if (!strcasecmp(ri
->nickname
, descriptor_list
[i
]->nickname
)) {
255 desc_ent_ptr
= &descriptor_list
[i
];
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. */
269 /* We don't have a newer one; we'll update this one. */
270 free_descriptor_entry(*desc_ent_ptr
);
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';
285 the_directory_is_dirty
= 1;
298 directory_set_dirty()
300 the_directory_is_dirty
= 1;
304 dirserv_init_from_directory_string(const char *dir
)
306 const char *cp
= dir
;
308 cp
= strstr(cp
, "\nrouter ");
311 if (dirserv_add_descriptor(&cp
)) {
314 --cp
; /*Back up to newline.*/
320 list_running_servers(char **nicknames_out
)
322 char *nickname_lst
[MAX_ROUTERS_IN_DIR
];
323 connection_t
**connection_array
;
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 */
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
);
347 for (i
= 0; i
<n
; ++i
) {
350 strcat(cp
, nickname_lst
[i
]);
359 dirserv_dump_directory_to_string(char *s
, int maxlen
,
360 crypto_pk_env_t
*private_key
)
370 if (list_running_servers(&cp
))
372 published_on
= time(NULL
);
373 strftime(published
, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on
));
377 "recommended-software %s\n"
378 "running-routers %s\n\n", published
, options
.RecommendedVersions
, cp
);
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
;
388 /* These multiple strlen calls are inefficient, but dwarfed by the RSA
392 strncat(s
, "directory-signature\n", maxlen
-i
);
396 if (router_get_dir_hash(s
,digest
)) {
397 log_fn(LOG_WARN
,"couldn't compute digest");
400 if (crypto_pk_private_sign(private_key
, digest
, 20, signature
) < 0) {
401 log_fn(LOG_WARN
,"couldn't sign digest");
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);
409 "-----BEGIN SIGNATURE-----\n", maxlen
-i
);
413 if (base64_encode(cp
, maxlen
-i
, signature
, 128) < 0) {
414 log_fn(LOG_WARN
,"couldn't base64-encode signature");
420 strncat(cp
, "-----END SIGNATURE-----\n", maxlen
-i
);
423 log_fn(LOG_WARN
,"tried to exceed string length.");
430 size_t dirserv_get_directory(const char **directory
)
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.");
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
,
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.");
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.");
463 log(LOG_INFO
,"Directory still clean, reusing.");
465 *directory
= the_directory
;
466 return the_directory_len
;