crank up some of our constants, for better scalability
[tor.git] / src / or / rendservice.c
blobd10d47c82e8c774b803364cc0e2aa6926b313af4
1 /* Copyright 2004 Roger Dingledine */
2 /* See LICENSE for licensing information */
3 /* $Id$ */
5 /**
6 * \file rendservice.c
7 * \brief The hidden-service side of rendezvous functionality.
8 **/
10 #include "or.h"
12 static circuit_t *find_intro_circuit(routerinfo_t *router, const char *pk_digest);
14 /** Represents the mapping from a virtual port of a rendezvous service to
15 * a real port on some IP.
17 typedef struct rend_service_port_config_t {
18 uint16_t virtual_port;
19 uint16_t real_port;
20 uint32_t real_address;
21 } rend_service_port_config_t;
23 /** Try to maintain this many intro points per service if possible. */
24 #define NUM_INTRO_POINTS 3
26 /** If we can't build our intro circuits, don't retry for this long. */
27 #define INTRO_CIRC_RETRY_PERIOD 60*10
28 /** Don't try to build more than this many circuits before giving up
29 * for a while.*/
30 #define MAX_INTRO_CIRCS_PER_PERIOD 10
32 /** Represents a single hidden service running at this OP. */
33 typedef struct rend_service_t {
34 /** Fields specified in config file */
35 char *directory; /**< where in the filesystem it stores it */
36 smartlist_t *ports; /**< List of rend_service_port_config_t */
37 char *intro_prefer_nodes; /**< comma-separated list of nicknames */
38 char *intro_exclude_nodes; /**< comma-separated list of nicknames */
39 /* Other fields */
40 crypto_pk_env_t *private_key;
41 char service_id[REND_SERVICE_ID_LEN+1];
42 char pk_digest[DIGEST_LEN];
43 smartlist_t *intro_nodes; /**< list of nicknames for intro points we have,
44 * or are trying to establish. */
45 time_t intro_period_started;
46 int n_intro_circuits_launched; /**< count of intro circuits we have
47 * established in this period. */
48 rend_service_descriptor_t *desc;
49 int desc_is_dirty;
50 } rend_service_t;
52 /** A list of rend_service_t's for services run on this OP.
54 static smartlist_t *rend_service_list = NULL;
56 /** Release the storage held by <b>service</b>.
58 static void rend_service_free(rend_service_t *service)
60 if (!service) return;
61 tor_free(service->directory);
62 SMARTLIST_FOREACH(service->ports, void*, p, tor_free(p));
63 smartlist_free(service->ports);
64 if (service->private_key)
65 crypto_free_pk_env(service->private_key);
66 tor_free(service->intro_prefer_nodes);
67 tor_free(service->intro_exclude_nodes);
68 SMARTLIST_FOREACH(service->intro_nodes, void*, p, tor_free(p));
69 smartlist_free(service->intro_nodes);
70 if (service->desc)
71 rend_service_descriptor_free(service->desc);
72 tor_free(service);
75 /** Release all the storage held in rend_service_list, and allocate a new,
76 * empty rend_service_list.
78 static void rend_service_free_all(void)
80 if (!rend_service_list) {
81 rend_service_list = smartlist_create();
82 return;
84 SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
85 rend_service_free(ptr));
86 smartlist_free(rend_service_list);
87 rend_service_list = smartlist_create();
90 /** Validate <b>service</b> and add it to rend_service_list if possible.
92 static void add_service(rend_service_t *service)
94 int i;
95 rend_service_port_config_t *p;
96 struct in_addr addr;
98 if (!service->intro_prefer_nodes)
99 service->intro_prefer_nodes = tor_strdup("");
100 if (!service->intro_exclude_nodes)
101 service->intro_exclude_nodes = tor_strdup("");
103 if (!smartlist_len(service->ports)) {
104 log_fn(LOG_WARN, "Hidden service with no ports configured; ignoring.");
105 rend_service_free(service);
106 } else {
107 smartlist_set_capacity(service->ports, -1);
108 smartlist_add(rend_service_list, service);
109 log_fn(LOG_DEBUG,"Configuring service with directory %s",service->directory);
110 for (i = 0; i < smartlist_len(service->ports); ++i) {
111 p = smartlist_get(service->ports, i);
112 addr.s_addr = htonl(p->real_address);
113 log_fn(LOG_DEBUG,"Service maps port %d to %s:%d",
114 p->virtual_port, inet_ntoa(addr), p->real_port);
119 /** Parses a real-port to virtual-port mapping and returns a new
120 * rend_service_port_config_t.
122 * The format is: VirtualPort (IP|RealPort|IP:RealPort)?
124 * IP defaults to 127.0.0.1; RealPort defaults to VirtualPort.
126 static rend_service_port_config_t *parse_port_config(const char *string)
128 int virtport, realport, r;
129 struct in_addr addr;
130 char *endptr, *colon, *addrstring;
131 rend_service_port_config_t *result;
133 virtport = (int) strtol(string, &endptr, 10);
134 if (endptr == string) {
135 log_fn(LOG_WARN, "Missing port in hidden service port configuration");
136 return NULL;
138 if (virtport < 1 || virtport > 65535) {
139 log_fn(LOG_WARN, "Port out of range in hidden service port configuration");
140 return NULL;
142 string = endptr + strspn(endptr, " \t");
143 if (!*string) {
144 /* No addr:port part; use default. */
145 realport = virtport;
146 addr.s_addr = htonl(0x7F000001u); /* 127.0.0.1 */
147 } else {
148 colon = strchr(string, ':');
149 if (colon) {
150 /* Try to parse addr:port. */
151 addrstring = tor_strndup(string, colon-string);
152 r = tor_inet_aton(addrstring, &addr);
153 tor_free(addrstring);
154 if (!r) {
155 log_fn(LOG_WARN,"Unparseable address in hidden service port configuration");
156 return NULL;
158 realport = strtol(colon+1, &endptr, 10);
159 if (*endptr) {
160 log_fn(LOG_WARN,"Unparseable or missing port in hidden service port configuration.");
161 return NULL;
163 } else if (strchr(string, '.') && tor_inet_aton(string, &addr)) {
164 /* We have addr; use deafult port. */
165 realport = virtport;
166 } else {
167 /* No addr:port, no addr -- must be port. */
168 realport = strtol(string, &endptr, 10);
169 if (*endptr) {
170 log_fn(LOG_WARN, "Unparseable of missing port in hidden service port configuration.");
171 return NULL;
173 addr.s_addr = htonl(0x7F000001u); /* Default to 127.0.0.1 */
176 if (realport < 1 || realport > 65535) {
177 log_fn(LOG_WARN, "Port out of range in hidden service port configuration.");
178 return NULL;
181 result = tor_malloc(sizeof(rend_service_port_config_t));
182 result->virtual_port = virtport;
183 result->real_port = realport;
184 result->real_address = (uint32_t) ntohl(addr.s_addr);
185 return result;
188 /** Set up rend_service_list, based on the values of HiddenServiceDir and
189 * HiddenServicePort in <b>options</b>. Return 0 on success and -1 on
190 * failure.
192 int rend_config_services(or_options_t *options)
194 struct config_line_t *line;
195 rend_service_t *service = NULL;
196 rend_service_port_config_t *portcfg;
197 rend_service_free_all();
199 for (line = options->RendConfigLines; line; line = line->next) {
200 if (!strcasecmp(line->key, "HiddenServiceDir")) {
201 if (service)
202 add_service(service);
203 service = tor_malloc_zero(sizeof(rend_service_t));
204 service->directory = tor_strdup(line->value);
205 service->ports = smartlist_create();
206 service->intro_nodes = smartlist_create();
207 service->intro_period_started = time(NULL);
208 continue;
210 if (!service) {
211 log_fn(LOG_WARN, "HiddenServicePort with no preceeding HiddenServiceDir directive");
212 rend_service_free(service);
213 return -1;
215 if (!strcasecmp(line->key, "HiddenServicePort")) {
216 portcfg = parse_port_config(line->value);
217 if (!portcfg) {
218 rend_service_free(service);
219 return -1;
221 smartlist_add(service->ports, portcfg);
222 } else if (!strcasecmp(line->key, "HiddenServiceNodes")) {
223 if (service->intro_prefer_nodes) {
224 log_fn(LOG_WARN, "Got multiple HiddenServiceNodes lines for a single service");
225 return -1;
227 service->intro_prefer_nodes = tor_strdup(line->value);
228 } else {
229 tor_assert(!strcasecmp(line->key, "HiddenServiceExcludeNodes"));
230 if (service->intro_exclude_nodes) {
231 log_fn(LOG_WARN, "Got multiple HiddenServiceExcludedNodes lines for a single service");
232 return -1;
234 service->intro_exclude_nodes = tor_strdup(line->value);
237 if (service)
238 add_service(service);
240 return 0;
243 /** Replace the old value of <b>service</b>-\>desc with one that reflects
244 * the other fields in service.
246 static void rend_service_update_descriptor(rend_service_t *service)
248 rend_service_descriptor_t *d;
249 circuit_t *circ;
250 int i,n;
251 routerinfo_t *router;
253 if (service->desc) {
254 rend_service_descriptor_free(service->desc);
255 service->desc = NULL;
257 d = service->desc = tor_malloc(sizeof(rend_service_descriptor_t));
258 d->pk = crypto_pk_dup_key(service->private_key);
259 d->timestamp = time(NULL);
260 n = smartlist_len(service->intro_nodes);
261 d->n_intro_points = 0;
262 d->intro_points = tor_malloc(sizeof(char*)*n);
263 for (i=0; i < n; ++i) {
264 router = router_get_by_nickname(smartlist_get(service->intro_nodes, i));
265 circ = find_intro_circuit(router, service->pk_digest);
266 if (circ && circ->purpose == CIRCUIT_PURPOSE_S_INTRO) {
267 /* We have an entirely established intro circuit. */
268 d->intro_points[d->n_intro_points++] = tor_strdup(router->nickname);
273 /** Load and/or generate private keys for all hidden services. Return 0 on
274 * success, -1 on failure.
276 int rend_service_load_keys(void)
278 int i;
279 rend_service_t *s;
280 char fname[512];
281 char buf[128];
283 for (i=0; i < smartlist_len(rend_service_list); ++i) {
284 s = smartlist_get(rend_service_list,i);
285 if (s->private_key)
286 continue;
287 log_fn(LOG_INFO, "Loading hidden-service keys from '%s'", s->directory);
289 /* Check/create directory */
290 if (check_private_dir(s->directory, 1) < 0)
291 return -1;
293 /* Load key */
294 if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
295 strlcat(fname,"/private_key",sizeof(fname)) >= sizeof(fname)) {
296 log_fn(LOG_WARN, "Directory name too long: '%s'", s->directory);
297 return -1;
299 s->private_key = init_key_from_file(fname);
300 if (!s->private_key)
301 return -1;
303 /* Create service file */
304 if (rend_get_service_id(s->private_key, s->service_id)<0) {
305 log_fn(LOG_WARN, "Couldn't encode service ID");
306 return -1;
308 if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
309 log_fn(LOG_WARN, "Couldn't compute hash of public key");
310 return -1;
312 if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
313 strlcat(fname,"/hostname",sizeof(fname)) >= sizeof(fname)) {
314 log_fn(LOG_WARN, "Directory name too long: '%s'", s->directory);
315 return -1;
317 sprintf(buf, "%s.onion\n", s->service_id);
318 if (write_str_to_file(fname,buf)<0)
319 return -1;
321 return 0;
324 /** Return the service whose public key has a digest of <b>digest</b>. Return
325 * NULL if no such service exists.
327 static rend_service_t *
328 rend_service_get_by_pk_digest(const char* digest)
330 SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s,
331 if (!memcmp(s->pk_digest,digest,DIGEST_LEN)) return s);
332 return NULL;
335 /******
336 * Handle cells
337 ******/
339 /** Respond to an INTRODUCE2 cell by launching a circuit to the chosen
340 * rendezvous points.
343 rend_service_introduce(circuit_t *circuit, const char *request, int request_len)
345 char *ptr, *rp_nickname, *r_cookie;
346 char buf[RELAY_PAYLOAD_SIZE];
347 char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
348 rend_service_t *service;
349 int len, keylen;
350 crypto_dh_env_t *dh = NULL;
351 circuit_t *launched = NULL;
352 crypt_path_t *cpath = NULL;
353 char serviceid[REND_SERVICE_ID_LEN+1];
354 char hexcookie[9];
356 if (base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
357 circuit->rend_pk_digest,10)) {
358 return -1;
360 log_fn(LOG_INFO, "Received INTRODUCE2 cell for service %s on circ %d",
361 serviceid, circuit->n_circ_id);
363 if (circuit->purpose != CIRCUIT_PURPOSE_S_INTRO) {
364 log_fn(LOG_WARN, "Got an INTRODUCE2 over a non-introduction circuit %d",
365 circuit->n_circ_id);
366 return -1;
369 /* min key length plus digest length plus nickname length */
370 if (request_len < DIGEST_LEN+REND_COOKIE_LEN+(MAX_NICKNAME_LEN+1)+
371 DH_KEY_LEN+42){
372 log_fn(LOG_WARN, "Got a truncated INTRODUCE2 cell on circ %d",
373 circuit->n_circ_id);
374 return -1;
377 /* first DIGEST_LEN bytes of request is service pk digest */
378 service = rend_service_get_by_pk_digest(request);
379 if (!service) {
380 log_fn(LOG_WARN, "Got an INTRODUCE2 cell for an unrecognized service %s",
381 serviceid);
382 return -1;
384 if (memcmp(circuit->rend_pk_digest, request, DIGEST_LEN)) {
385 if (base32_encode(serviceid, REND_SERVICE_ID_LEN+1, request, 10)) {
386 return -1;
388 log_fn(LOG_WARN, "Got an INTRODUCE2 cell for the wrong service (%s)",
389 serviceid);
390 return -1;
393 keylen = crypto_pk_keysize(service->private_key);
394 if (request_len < keylen+DIGEST_LEN) {
395 log_fn(LOG_WARN, "PK-encrypted portion of INTRODUCE2 cell was truncated");
396 return -1;
398 /* Next N bytes is encrypted with service key */
399 len = crypto_pk_private_hybrid_decrypt(
400 service->private_key,request+DIGEST_LEN,request_len-DIGEST_LEN,buf,
401 PK_PKCS1_OAEP_PADDING,1);
402 if (len<0) {
403 log_fn(LOG_WARN, "Couldn't decrypt INTRODUCE2 cell");
404 return -1;
406 ptr=memchr(buf,0,MAX_NICKNAME_LEN+1);
407 if (!ptr || ptr == buf) {
408 log_fn(LOG_WARN, "Couldn't find a null-padded nickname in INTRODUCE2 cell");
409 return -1;
411 if ((int)strspn(buf,LEGAL_NICKNAME_CHARACTERS) != ptr-buf) {
412 log_fn(LOG_WARN, "Nickname in INTRODUCE2 cell contains illegal character.");
413 return -1;
415 /* Okay, now we know that the nickname is at the start of the buffer. */
416 rp_nickname = buf;
417 ptr = buf+(MAX_NICKNAME_LEN+1);
418 len -= (MAX_NICKNAME_LEN+1);
419 if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
420 log_fn(LOG_WARN, "Bad length for INTRODUCE2 cell.");
421 return -1;
423 r_cookie = ptr;
424 hex_encode(r_cookie,4,hexcookie);
426 /* Try DH handshake... */
427 dh = crypto_dh_new();
428 if (!dh || crypto_dh_generate_public(dh)<0) {
429 log_fn(LOG_WARN, "Couldn't build DH state or generate public key");
430 goto err;
432 if (crypto_dh_compute_secret(dh, ptr+REND_COOKIE_LEN, DH_KEY_LEN, keys,
433 DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
434 log_fn(LOG_WARN, "Couldn't complete DH handshake");
435 goto err;
438 /* Launch a circuit to alice's chosen rendezvous point.
440 launched = circuit_launch_new(CIRCUIT_PURPOSE_S_CONNECT_REND, rp_nickname);
441 log_fn(LOG_INFO,
442 "Accepted intro; launching circuit to '%s' (cookie %s) for service %s",
443 rp_nickname, hexcookie, serviceid);
444 if (!launched) {
445 log_fn(LOG_WARN,
446 "Can't launch circuit to rendezvous point '%s' for service %s",
447 rp_nickname, serviceid);
448 return -1;
450 tor_assert(launched->build_state);
451 /* Fill in the circuit's state. */
452 memcpy(launched->rend_pk_digest, circuit->rend_pk_digest,
453 DIGEST_LEN);
454 memcpy(launched->rend_cookie, r_cookie, REND_COOKIE_LEN);
455 strcpy(launched->rend_query, service->service_id);
456 launched->build_state->pending_final_cpath = cpath =
457 tor_malloc_zero(sizeof(crypt_path_t));
459 cpath->handshake_state = dh;
460 dh = NULL;
461 if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0)
462 goto err;
463 memcpy(cpath->handshake_digest, keys, DIGEST_LEN);
465 return 0;
466 err:
467 if (dh) crypto_dh_free(dh);
468 if (launched) circuit_mark_for_close(launched);
469 return -1;
472 /** How many times will a hidden service operator attempt to connect to
473 * a requested rendezvous point before giving up? */
474 #define MAX_REND_FAILURES 3
476 /** Called when we fail building a rendezvous circuit at some point other
477 * than the last hop: launches a new circuit to the same rendezvous point.
479 void
480 rend_service_relaunch_rendezvous(circuit_t *oldcirc)
482 circuit_t *newcirc;
483 cpath_build_state_t *newstate, *oldstate;
485 tor_assert(oldcirc->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
487 if (!oldcirc->build_state ||
488 oldcirc->build_state->failure_count > MAX_REND_FAILURES) {
489 log_fn(LOG_INFO,"Attempt to build circuit to %s for rendezvous has failed too many times; giving up.",
490 oldcirc->build_state->chosen_exit);
491 return;
494 log_fn(LOG_INFO,"Reattempting rendezvous circuit to %s",
495 oldcirc->build_state->chosen_exit);
497 newcirc = circuit_launch_new(CIRCUIT_PURPOSE_S_CONNECT_REND,
498 oldcirc->build_state->chosen_exit);
499 if (!newcirc) {
500 log_fn(LOG_WARN,"Couldn't relaunch rendezvous circuit to %s",
501 oldcirc->build_state->chosen_exit);
502 return;
504 oldstate = oldcirc->build_state;
505 newstate = newcirc->build_state;
506 tor_assert(newstate && oldstate);
507 newstate->failure_count = oldstate->failure_count+1;
508 newstate->pending_final_cpath = oldstate->pending_final_cpath;
509 oldstate->pending_final_cpath = NULL;
511 memcpy(newcirc->rend_query, oldcirc->rend_query, REND_SERVICE_ID_LEN+1);
512 memcpy(newcirc->rend_pk_digest, oldcirc->rend_pk_digest, DIGEST_LEN);
513 memcpy(newcirc->rend_cookie, oldcirc->rend_cookie, REND_COOKIE_LEN);
516 /** Launch a circuit to serve as an introduction point for the service
517 * <b>service</b> at the introduction point <b>nickname</b>
519 static int
520 rend_service_launch_establish_intro(rend_service_t *service, const char *nickname)
522 circuit_t *launched;
524 log_fn(LOG_INFO, "Launching circuit to introduction point %s for service %s",
525 nickname, service->service_id);
527 ++service->n_intro_circuits_launched;
528 launched = circuit_launch_new(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname);
529 if (!launched) {
530 log_fn(LOG_WARN, "Can't launch circuit to establish introduction at '%s'",
531 nickname);
532 return -1;
534 strcpy(launched->rend_query, service->service_id);
535 memcpy(launched->rend_pk_digest, service->pk_digest, DIGEST_LEN);
537 return 0;
540 /** Called when we're done building a circuit to an introduction point:
541 * sends a RELAY_ESTABLISH_INTRO cell.
543 void
544 rend_service_intro_has_opened(circuit_t *circuit)
546 rend_service_t *service;
547 int len, r;
548 char buf[RELAY_PAYLOAD_SIZE];
549 char auth[DIGEST_LEN + 9];
550 char serviceid[REND_SERVICE_ID_LEN+1];
552 tor_assert(circuit->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
553 tor_assert(CIRCUIT_IS_ORIGIN(circuit) && circuit->cpath);
555 if (base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
556 circuit->rend_pk_digest,10)) {
557 tor_assert(0);
560 service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
561 if (!service) {
562 log_fn(LOG_WARN, "Unrecognized service ID %s on introduction circuit %d",
563 serviceid, circuit->n_circ_id);
564 goto err;
567 log_fn(LOG_INFO,
568 "Established circuit %d as introduction point for service %s",
569 circuit->n_circ_id, serviceid);
571 /* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
572 len = crypto_pk_asn1_encode(service->private_key, buf+2,
573 RELAY_PAYLOAD_SIZE-2);
574 set_uint16(buf, htons((uint16_t)len));
575 len += 2;
576 memcpy(auth, circuit->cpath->prev->handshake_digest, DIGEST_LEN);
577 memcpy(auth+DIGEST_LEN, "INTRODUCE", 9);
578 if (crypto_digest(auth, DIGEST_LEN+9, buf+len))
579 goto err;
580 len += 20;
581 r = crypto_pk_private_sign_digest(service->private_key, buf, len, buf+len);
582 if (r<0) {
583 log_fn(LOG_WARN, "Couldn't sign introduction request");
584 goto err;
586 len += r;
588 if (connection_edge_send_command(NULL, circuit,RELAY_COMMAND_ESTABLISH_INTRO,
589 buf, len, circuit->cpath->prev)<0) {
590 log_fn(LOG_WARN,
591 "Couldn't send introduction request for service %s on circuit %d",
592 serviceid, circuit->n_circ_id);
593 goto err;
596 return;
597 err:
598 circuit_mark_for_close(circuit);
601 /** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a
602 * live introduction point, and note that the service descriptor is
603 * now out-of-date.*/
605 rend_service_intro_established(circuit_t *circuit, const char *request, int request_len)
607 rend_service_t *service;
609 if (circuit->purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
610 log_fn(LOG_WARN, "received INTRO_ESTABLISHED cell on non-intro circuit");
611 goto err;
613 service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
614 if (!service) {
615 log_fn(LOG_WARN, "Unknown service on introduction circuit %d",
616 circuit->n_circ_id);
617 goto err;
619 service->desc_is_dirty = 1;
620 circuit->purpose = CIRCUIT_PURPOSE_S_INTRO;
622 return 0;
623 err:
624 circuit_mark_for_close(circuit);
625 return -1;
628 /** Called once a circuit to a rendezvous point is established: sends a
629 * RELAY_COMMAND_RENDEZVOUS1 cell.
631 void
632 rend_service_rendezvous_has_opened(circuit_t *circuit)
634 rend_service_t *service;
635 char buf[RELAY_PAYLOAD_SIZE];
636 crypt_path_t *hop;
637 char serviceid[REND_SERVICE_ID_LEN+1];
638 char hexcookie[9];
640 tor_assert(circuit->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
641 tor_assert(circuit->cpath);
642 tor_assert(circuit->build_state);
643 hop = circuit->build_state->pending_final_cpath;
644 tor_assert(hop);
646 hex_encode(circuit->rend_cookie, 4, hexcookie);
647 if (base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
648 circuit->rend_pk_digest,10)) {
649 tor_assert(0);
652 log_fn(LOG_INFO,
653 "Done building circuit %d to rendezvous with cookie %s for service %s",
654 circuit->n_circ_id, hexcookie, serviceid);
656 service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
657 if (!service) {
658 log_fn(LOG_WARN, "Internal error: unrecognized service ID on introduction circuit");
659 goto err;
662 /* All we need to do is send a RELAY_RENDEZVOUS1 cell... */
663 memcpy(buf, circuit->rend_cookie, REND_COOKIE_LEN);
664 if (crypto_dh_get_public(hop->handshake_state,
665 buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) {
666 log_fn(LOG_WARN,"Couldn't get DH public key");
667 goto err;
669 memcpy(buf+REND_COOKIE_LEN+DH_KEY_LEN, hop->handshake_digest,
670 DIGEST_LEN);
672 /* Send the cell */
673 if (connection_edge_send_command(NULL, circuit, RELAY_COMMAND_RENDEZVOUS1,
674 buf, REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN,
675 circuit->cpath->prev)<0) {
676 log_fn(LOG_WARN, "Couldn't send RENDEZVOUS1 cell");
677 goto err;
680 crypto_dh_free(hop->handshake_state);
681 hop->handshake_state = NULL;
683 /* Append the cpath entry. */
684 hop->state = CPATH_STATE_OPEN;
685 /* set the windows to default. these are the windows
686 * that bob thinks alice has.
688 hop->package_window = CIRCWINDOW_START;
689 hop->deliver_window = CIRCWINDOW_START;
691 onion_append_to_cpath(&circuit->cpath, hop);
692 circuit->build_state->pending_final_cpath = NULL; /* prevent double-free */
694 /* Change the circuit purpose. */
695 circuit->purpose = CIRCUIT_PURPOSE_S_REND_JOINED;
697 return;
698 err:
699 circuit_mark_for_close(circuit);
703 * Manage introduction points
706 /** Return the (possibly non-open) introduction circuit ending at
707 * <b>router</b> for the service whose public key is <b>pk_digest</b>. Return
708 * NULL if no such service is found.
710 static circuit_t *
711 find_intro_circuit(routerinfo_t *router, const char *pk_digest)
713 circuit_t *circ = NULL;
715 while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
716 CIRCUIT_PURPOSE_S_INTRO))) {
717 tor_assert(circ->cpath);
718 if (circ->build_state->chosen_exit &&
719 !strcasecmp(circ->build_state->chosen_exit, router->nickname)) {
720 return circ;
724 circ = NULL;
725 while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
726 CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
727 tor_assert(circ->cpath);
728 if (circ->build_state->chosen_exit &&
729 !strcasecmp(circ->build_state->chosen_exit, router->nickname)) {
730 return circ;
733 return NULL;
736 /** If the directory servers don't have an up-to-date descriptor for
737 * <b>service</b>, encode and sign the service descriptor for <b>service</b>,
738 * and upload it to all the dirservers.
740 static void
741 upload_service_descriptor(rend_service_t *service)
743 char *desc;
744 int desc_len;
745 if (!service->desc_is_dirty)
746 return;
748 /* Update the descriptor. */
749 rend_service_update_descriptor(service);
750 if (rend_encode_service_descriptor(service->desc,
751 service->private_key,
752 &desc, &desc_len)<0) {
753 log_fn(LOG_WARN, "Couldn't encode service descriptor; not uploading");
754 return;
757 /* Post it to the dirservers */
758 directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_RENDDESC, desc, desc_len);
759 tor_free(desc);
761 service->desc_is_dirty = 0;
764 /* XXXX Make this longer once directories remember service descriptors across
765 * restarts.*/
766 #define MAX_SERVICE_PUBLICATION_INTERVAL (15*60)
768 /** For every service, check how many intro points it currently has, and:
769 * - Pick new intro points as necessary.
770 * - Launch circuits to any new intro points.
772 void rend_services_introduce(void) {
773 int i,j,r;
774 routerinfo_t *router;
775 rend_service_t *service;
776 char *intro;
777 int changed, prev_intro_nodes;
778 smartlist_t *intro_routers, *exclude_routers;
779 time_t now;
781 intro_routers = smartlist_create();
782 exclude_routers = smartlist_create();
783 now = time(NULL);
785 for (i=0; i< smartlist_len(rend_service_list); ++i) {
786 smartlist_clear(intro_routers);
787 service = smartlist_get(rend_service_list, i);
789 tor_assert(service);
790 changed = 0;
791 if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) {
792 /* One period has elapsed; we can try building circuits again. */
793 service->intro_period_started = now;
794 service->n_intro_circuits_launched = 0;
795 } else if (service->n_intro_circuits_launched>=MAX_INTRO_CIRCS_PER_PERIOD){
796 /* We have failed too many times in this period; wait for the next
797 * one before we try again. */
798 continue;
801 /* Find out which introduction points we have in progress for this service. */
802 for (j=0;j< smartlist_len(service->intro_nodes); ++j) {
803 intro = smartlist_get(service->intro_nodes, j);
804 router = router_get_by_nickname(intro);
805 if (!router || !find_intro_circuit(router,service->pk_digest)) {
806 log_fn(LOG_INFO,"Giving up on %s as intro point for %s.",
807 intro, service->service_id);
808 smartlist_del(service->intro_nodes,j--);
809 changed = service->desc_is_dirty = 1;
811 smartlist_add(intro_routers, router);
814 /* We have enough intro points, and the intro points we thought we had were
815 * all connected.
817 if (!changed && smartlist_len(service->intro_nodes) >= NUM_INTRO_POINTS) {
818 /* We have all our intro points! Start a fresh period and reset the
819 * circuit count. */
820 service->intro_period_started = now;
821 service->n_intro_circuits_launched = 0;
822 continue;
825 /* Remember how many introduction circuits we started with. */
826 prev_intro_nodes = smartlist_len(service->intro_nodes);
828 smartlist_add_all(exclude_routers, intro_routers);
829 /* The directory is now here. Pick three ORs as intro points. */
830 for (j=prev_intro_nodes; j < NUM_INTRO_POINTS; ++j) {
831 router = router_choose_random_node(service->intro_prefer_nodes,
832 service->intro_exclude_nodes,
833 exclude_routers);
834 if (!router) {
835 log_fn(LOG_WARN, "Could only establish %d introduction points for %s",
836 smartlist_len(service->intro_nodes), service->service_id);
837 break;
839 changed = 1;
840 smartlist_add(intro_routers, router);
841 smartlist_add(exclude_routers, router);
842 smartlist_add(service->intro_nodes, tor_strdup(router->nickname));
843 log_fn(LOG_INFO,"Picked router %s as an intro point for %s.", router->nickname,
844 service->service_id);
847 /* Reset exclude_routers, for the next time around the loop. */
848 smartlist_clear(exclude_routers);
850 /* If there's no need to launch new circuits, stop here. */
851 if (!changed)
852 continue;
854 /* Establish new introduction points. */
855 for (j=prev_intro_nodes; j < smartlist_len(service->intro_nodes); ++j) {
856 intro = smartlist_get(service->intro_nodes, j);
857 r = rend_service_launch_establish_intro(service, intro);
858 if (r<0) {
859 log_fn(LOG_WARN, "Error launching circuit to node %s for service %s",
860 intro, service->service_id);
864 smartlist_free(intro_routers);
865 smartlist_free(exclude_routers);
868 /** Regenerate and upload rendezvous service descriptors for all
869 * services. If <b>force</b> is false, skip services where we've already
870 * uploaded an up-to-date copy; if <b>force</b> is true, regenerate and
871 * upload everything.
873 void
874 rend_services_upload(int force)
876 int i;
877 rend_service_t *service;
879 for (i=0; i< smartlist_len(rend_service_list); ++i) {
880 service = smartlist_get(rend_service_list, i);
881 if (force)
882 service->desc_is_dirty = 1;
883 if (service->desc_is_dirty)
884 upload_service_descriptor(service);
888 /** Log the status of introduction points for all rendezvous services
889 * at log severity <b>serverity</b>.
891 void
892 rend_service_dump_stats(int severity)
894 int i,j;
895 routerinfo_t *router;
896 rend_service_t *service;
897 char *nickname;
898 circuit_t *circ;
900 for (i=0; i < smartlist_len(rend_service_list); ++i) {
901 service = smartlist_get(rend_service_list, i);
902 log(severity, "Service configured in %s:", service->directory);
903 for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
904 nickname = smartlist_get(service->intro_nodes, j);
905 router = router_get_by_nickname(smartlist_get(service->intro_nodes,j));
906 if (!router) {
907 log(severity, " Intro point at %s: unrecognized router",nickname);
908 continue;
910 circ = find_intro_circuit(router, service->pk_digest);
911 if (!circ) {
912 log(severity, " Intro point at %s: no circuit",nickname);
913 continue;
915 log(severity, " Intro point at %s: circuit is %s",nickname,
916 circuit_state_to_string[circ->state]);
921 /** Given <b>conn</b>, a rendezvous exit stream, look up the hidden service for
922 * 'circ', and look up the port and address based on conn-\>port.
923 * Assign the actual conn-\>addr and conn-\>port. Return -1 if failure,
924 * or 0 for success.
927 rend_service_set_connection_addr_port(connection_t *conn, circuit_t *circ)
929 rend_service_t *service;
930 int i;
931 rend_service_port_config_t *p;
932 char serviceid[REND_SERVICE_ID_LEN+1];
934 tor_assert(circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
935 log_fn(LOG_DEBUG,"beginning to hunt for addr/port");
936 if (base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
937 circ->rend_pk_digest,10)) {
938 log_fn(LOG_WARN,"bug: base32 failed");
939 return -1;
941 service = rend_service_get_by_pk_digest(circ->rend_pk_digest);
942 if (!service) {
943 log_fn(LOG_WARN, "Couldn't find any service associated with pk %s on rendezvous circuit %d; closing",
944 serviceid, circ->n_circ_id);
945 return -1;
947 for (i = 0; i < smartlist_len(service->ports); ++i) {
948 p = smartlist_get(service->ports, i);
949 if (conn->port == p->virtual_port) {
950 conn->addr = p->real_address;
951 conn->port = p->real_port;
952 return 0;
955 log_fn(LOG_INFO, "No virtual port mapping exists for port %d on service %s",
956 conn->port,serviceid);
957 return -1;
961 Local Variables:
962 mode:c
963 indent-tabs-mode:nil
964 c-basic-offset:2
965 End: