1 /* Copyright 2004-2005 Roger Dingledine, Nick Mathewson. */
2 /* See LICENSE for licensing information */
4 const char rendclient_c_id
[] = "$Id$";
8 * \brief Client code to access location-hidden services.
13 /** Called when we've established a circuit to an introduction point:
14 * send the introduction request. */
16 rend_client_introcirc_has_opened(circuit_t
*circ
)
18 tor_assert(circ
->purpose
== CIRCUIT_PURPOSE_C_INTRODUCING
);
19 tor_assert(CIRCUIT_IS_ORIGIN(circ
));
20 tor_assert(circ
->cpath
);
22 log_fn(LOG_INFO
,"introcirc is open");
23 connection_ap_attach_pending();
26 /** Send the establish-rendezvous cell along a rendezvous circuit. if
27 * it fails, mark the circ for close and return -1. else return 0.
30 rend_client_send_establish_rendezvous(circuit_t
*circ
)
32 tor_assert(circ
->purpose
== CIRCUIT_PURPOSE_C_ESTABLISH_REND
);
33 log_fn(LOG_INFO
, "Sending an ESTABLISH_RENDEZVOUS cell");
35 if (crypto_rand(circ
->rend_cookie
, REND_COOKIE_LEN
) < 0) {
36 log_fn(LOG_WARN
, "Internal error: Couldn't produce random cookie.");
37 circuit_mark_for_close(circ
);
40 if (connection_edge_send_command(NULL
,circ
,
41 RELAY_COMMAND_ESTABLISH_RENDEZVOUS
,
42 circ
->rend_cookie
, REND_COOKIE_LEN
,
43 circ
->cpath
->prev
)<0) {
44 /* circ is already marked for close */
45 log_fn(LOG_WARN
, "Couldn't send ESTABLISH_RENDEZVOUS cell");
52 /** Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell
53 * down introcirc if possible.
56 rend_client_send_introduction(circuit_t
*introcirc
, circuit_t
*rendcirc
)
60 char payload
[RELAY_PAYLOAD_SIZE
];
61 char tmp
[RELAY_PAYLOAD_SIZE
];
62 rend_cache_entry_t
*entry
;
66 tor_assert(introcirc
->purpose
== CIRCUIT_PURPOSE_C_INTRODUCING
);
67 tor_assert(rendcirc
->purpose
== CIRCUIT_PURPOSE_C_REND_READY
);
68 tor_assert(!rend_cmp_service_ids(introcirc
->rend_query
, rendcirc
->rend_query
));
70 if (rend_cache_lookup_entry(introcirc
->rend_query
, -1, &entry
) < 1) {
71 log_fn(LOG_WARN
,"query '%s' didn't have valid rend desc in cache. Failing.",
72 safe_str(introcirc
->rend_query
));
76 /* first 20 bytes of payload are the hash of bob's pk */
77 if (crypto_pk_get_digest(entry
->parsed
->pk
, payload
)<0) {
78 log_fn(LOG_WARN
, "Internal error: couldn't hash public key.");
82 /* Initialize the pending_final_cpath and start the DH handshake. */
83 cpath
= rendcirc
->build_state
->pending_final_cpath
;
85 cpath
= rendcirc
->build_state
->pending_final_cpath
=
86 tor_malloc_zero(sizeof(crypt_path_t
));
87 cpath
->magic
= CRYPT_PATH_MAGIC
;
88 if (!(cpath
->dh_handshake_state
= crypto_dh_new())) {
89 log_fn(LOG_WARN
, "Internal error: couldn't allocate DH.");
92 if (crypto_dh_generate_public(cpath
->dh_handshake_state
)<0) {
93 log_fn(LOG_WARN
, "Internal error: couldn't generate g^x.");
98 /* write the remaining items into tmp */
99 if (entry
->parsed
->protocols
& (1<<2)) {
100 /* version 2 format */
101 extend_info_t
*extend_info
= rendcirc
->build_state
->chosen_exit
;
103 tmp
[0] = 2; /* version 2 of the cell format */
105 set_uint32(tmp
+1, htonl(extend_info
->addr
));
106 set_uint16(tmp
+5, htons(extend_info
->port
));
107 memcpy(tmp
+7, extend_info
->identity_digest
, DIGEST_LEN
);
108 klen
= crypto_pk_asn1_encode(extend_info
->onion_key
, tmp
+7+DIGEST_LEN
+2,
109 sizeof(tmp
)-(7+DIGEST_LEN
+2));
110 set_uint16(tmp
+7+DIGEST_LEN
, htons(klen
));
111 memcpy(tmp
+7+DIGEST_LEN
+2+klen
, rendcirc
->rend_cookie
, REND_COOKIE_LEN
);
112 dh_offset
= 7+DIGEST_LEN
+2+klen
+REND_COOKIE_LEN
;
115 strncpy(tmp
, rendcirc
->build_state
->chosen_exit
->nickname
, (MAX_NICKNAME_LEN
+1)); /* nul pads */
116 memcpy(tmp
+MAX_NICKNAME_LEN
+1, rendcirc
->rend_cookie
, REND_COOKIE_LEN
);
117 dh_offset
= MAX_NICKNAME_LEN
+1+REND_COOKIE_LEN
;
120 if (crypto_dh_get_public(cpath
->dh_handshake_state
, tmp
+dh_offset
,
122 log_fn(LOG_WARN
, "Internal error: couldn't extract g^x.");
126 /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
127 * to avoid buffer overflows? */
128 r
= crypto_pk_public_hybrid_encrypt(entry
->parsed
->pk
, payload
+DIGEST_LEN
, tmp
,
129 dh_offset
+DH_KEY_LEN
,
130 PK_PKCS1_OAEP_PADDING
, 0);
132 log_fn(LOG_WARN
,"Internal error: hybrid pk encrypt failed.");
136 tor_assert(DIGEST_LEN
+ r
<= RELAY_PAYLOAD_SIZE
); /* we overran something */
137 payload_len
= DIGEST_LEN
+ r
;
139 if (connection_edge_send_command(NULL
, introcirc
,
140 RELAY_COMMAND_INTRODUCE1
,
141 payload
, payload_len
,
142 introcirc
->cpath
->prev
)<0) {
143 /* introcirc is already marked for close. leave rendcirc alone. */
144 log_fn(LOG_WARN
, "Couldn't send INTRODUCE1 cell");
148 /* Now, we wait for an ACK or NAK on this circuit. */
149 introcirc
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT
;
153 circuit_mark_for_close(introcirc
);
154 circuit_mark_for_close(rendcirc
);
158 /** Called when a rendezvous circuit is open; sends a establish
159 * rendezvous circuit as appropriate. */
161 rend_client_rendcirc_has_opened(circuit_t
*circ
)
163 tor_assert(circ
->purpose
== CIRCUIT_PURPOSE_C_ESTABLISH_REND
);
164 tor_assert(CIRCUIT_IS_ORIGIN(circ
));
166 log_fn(LOG_INFO
,"rendcirc is open");
168 /* generate a rendezvous cookie, store it in circ */
169 if (rend_client_send_establish_rendezvous(circ
) < 0) {
174 /** Called when get an ACK or a NAK for a REND_INTRODUCE1 cell.
177 rend_client_introduction_acked(circuit_t
*circ
,
178 const char *request
, size_t request_len
)
182 if (circ
->purpose
!= CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT
) {
183 log_fn(LOG_WARN
, "Received REND_INTRODUCE_ACK on unexpected circuit %d.",
185 circuit_mark_for_close(circ
);
189 tor_assert(circ
->build_state
->chosen_exit
);
190 tor_assert(circ
->build_state
->chosen_exit
->nickname
);
192 if (request_len
== 0) {
193 /* It's an ACK; the introduction point relayed our introduction request. */
194 /* Locate the rend circ which is waiting to hear about this ack,
197 log_fn(LOG_INFO
,"Received ack. Telling rend circ...");
198 rendcirc
= circuit_get_by_rend_query_and_purpose(
199 circ
->rend_query
, CIRCUIT_PURPOSE_C_REND_READY
);
200 if (rendcirc
) { /* remember the ack */
201 rendcirc
->purpose
= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED
;
203 log_fn(LOG_INFO
,"...Found no rend circ. Dropping on the floor.");
205 /* close the circuit: we won't need it anymore. */
206 circ
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCE_ACKED
;
207 circuit_mark_for_close(circ
);
209 /* It's a NAK; the introduction point didn't relay our request. */
210 circ
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCING
;
211 /* Remove this intro point from the set of viable introduction
212 * points. If any remain, extend to a new one and try again.
213 * If none remain, refetch the service descriptor.
215 if (rend_client_remove_intro_point(circ
->build_state
->chosen_exit
,
216 circ
->rend_query
) > 0) {
217 /* There are introduction points left. Re-extend the circuit to
218 * another intro point and try again. */
221 info
= rend_client_get_random_intro(circ
->rend_query
);
223 log_fn(LOG_WARN
, "No introduction points left for %s. Closing.",
224 safe_str(circ
->rend_query
));
225 circuit_mark_for_close(circ
);
228 log_fn(LOG_INFO
,"Got nack for %s from %s. Re-extending circ %d, this time to %s.",
229 safe_str(circ
->rend_query
),
230 circ
->build_state
->chosen_exit
->nickname
, circ
->n_circ_id
,
232 result
= circuit_extend_to_new_exit(circ
, info
);
233 extend_info_free(info
);
240 /** If we are not currently fetching a rendezvous service descriptor
241 * for the service ID <b>query</b>, start a directory connection to fetch a
245 rend_client_refetch_renddesc(const char *query
)
247 if (connection_get_by_type_state_rendquery(CONN_TYPE_DIR
, 0, query
)) {
248 log_fn(LOG_INFO
,"Would fetch a new renddesc here (for %s), but one is already in progress.", safe_str(query
));
250 /* not one already; initiate a dir rend desc lookup */
251 directory_get_from_dirserver(DIR_PURPOSE_FETCH_RENDDESC
, query
, 1);
255 /** remove failed_intro from ent. if ent now has no intro points, or
256 * service is unrecognized, then launch a new renddesc fetch.
258 * Return -1 if error, 0 if no intro points remain or service
259 * unrecognized, 1 if recognized and some intro points remain.
262 rend_client_remove_intro_point(extend_info_t
*failed_intro
, const char *query
)
265 rend_cache_entry_t
*ent
;
268 r
= rend_cache_lookup_entry(query
, -1, &ent
);
270 log_fn(LOG_WARN
, "Bug: malformed service ID '%s'.", safe_str(query
));
274 log_fn(LOG_INFO
, "Unknown service %s. Re-fetching descriptor.",
276 rend_client_refetch_renddesc(query
);
280 if (ent
->parsed
->intro_point_extend_info
) {
281 for (i
=0; i
< ent
->parsed
->n_intro_points
; ++i
) {
282 if (!memcmp(failed_intro
->identity_digest
,
283 ent
->parsed
->intro_point_extend_info
[i
]->identity_digest
,
285 tor_assert(!strcmp(ent
->parsed
->intro_points
[i
],
286 ent
->parsed
->intro_point_extend_info
[i
]->nickname
));
287 tor_free(ent
->parsed
->intro_points
[i
]);
288 extend_info_free(ent
->parsed
->intro_point_extend_info
[i
]);
289 --ent
->parsed
->n_intro_points
;
290 ent
->parsed
->intro_points
[i
] =
291 ent
->parsed
->intro_points
[ent
->parsed
->n_intro_points
];
292 ent
->parsed
->intro_point_extend_info
[i
] =
293 ent
->parsed
->intro_point_extend_info
[ent
->parsed
->n_intro_points
];
298 for (i
=0; i
< ent
->parsed
->n_intro_points
; ++i
) {
299 if (!strcasecmp(ent
->parsed
->intro_points
[i
], failed_intro
->nickname
)) {
300 tor_free(ent
->parsed
->intro_points
[i
]);
301 ent
->parsed
->intro_points
[i
] =
302 ent
->parsed
->intro_points
[--ent
->parsed
->n_intro_points
];
308 if (!ent
->parsed
->n_intro_points
) {
309 log_fn(LOG_INFO
,"No more intro points remain for %s. Re-fetching descriptor.",
311 rend_client_refetch_renddesc(query
);
313 /* move all pending streams back to renddesc_wait */
314 while ((conn
= connection_get_by_type_state_rendquery(CONN_TYPE_AP
,
315 AP_CONN_STATE_CIRCUIT_WAIT
, query
))) {
316 conn
->state
= AP_CONN_STATE_RENDDESC_WAIT
;
321 log_fn(LOG_INFO
,"%d options left for %s.",
322 ent
->parsed
->n_intro_points
, safe_str(query
));
326 /** Called when we receive a RENDEZVOUS_ESTABLISHED cell; changes the state of
327 * the circuit to C_REND_READY.
330 rend_client_rendezvous_acked(circuit_t
*circ
, const char *request
, size_t request_len
)
332 /* we just got an ack for our establish-rendezvous. switch purposes. */
333 if (circ
->purpose
!= CIRCUIT_PURPOSE_C_ESTABLISH_REND
) {
334 log_fn(LOG_WARN
,"Got a rendezvous ack when we weren't expecting one. Closing circ.");
335 circuit_mark_for_close(circ
);
338 log_fn(LOG_INFO
,"Got rendezvous ack. This circuit is now ready for rendezvous.");
339 circ
->purpose
= CIRCUIT_PURPOSE_C_REND_READY
;
343 /** Bob sent us a rendezvous cell; join the circuits. */
345 rend_client_receive_rendezvous(circuit_t
*circ
, const char *request
, size_t request_len
)
348 char keys
[DIGEST_LEN
+CPATH_KEY_MATERIAL_LEN
];
350 if ((circ
->purpose
!= CIRCUIT_PURPOSE_C_REND_READY
&&
351 circ
->purpose
!= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED
)
352 || !circ
->build_state
->pending_final_cpath
) {
353 log_fn(LOG_WARN
,"Got rendezvous2 cell from hidden service, but not expecting it. Closing.");
354 circuit_mark_for_close(circ
);
358 if (request_len
!= DH_KEY_LEN
+DIGEST_LEN
) {
359 log_fn(LOG_WARN
,"Incorrect length (%d) on RENDEZVOUS2 cell.",(int)request_len
);
363 /* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/
364 tor_assert(circ
->build_state
);
365 tor_assert(circ
->build_state
->pending_final_cpath
);
366 hop
= circ
->build_state
->pending_final_cpath
;
367 tor_assert(hop
->dh_handshake_state
);
368 if (crypto_dh_compute_secret(hop
->dh_handshake_state
, request
, DH_KEY_LEN
,
369 keys
, DIGEST_LEN
+CPATH_KEY_MATERIAL_LEN
)<0) {
370 log_fn(LOG_WARN
, "Couldn't complete DH handshake.");
373 /* ... and set up cpath. */
374 if (circuit_init_cpath_crypto(hop
, keys
+DIGEST_LEN
, 0)<0)
377 /* Check whether the digest is right... */
378 if (memcmp(keys
, request
+DH_KEY_LEN
, DIGEST_LEN
)) {
379 log_fn(LOG_WARN
, "Incorrect digest of key material.");
383 crypto_dh_free(hop
->dh_handshake_state
);
384 hop
->dh_handshake_state
= NULL
;
386 /* All is well. Extend the circuit. */
387 circ
->purpose
= CIRCUIT_PURPOSE_C_REND_JOINED
;
388 hop
->state
= CPATH_STATE_OPEN
;
389 /* set the windows to default. these are the windows
390 * that alice thinks bob has.
392 hop
->package_window
= CIRCWINDOW_START
;
393 hop
->deliver_window
= CIRCWINDOW_START
;
395 onion_append_to_cpath(&circ
->cpath
, hop
);
396 circ
->build_state
->pending_final_cpath
= NULL
; /* prevent double-free */
399 circuit_mark_for_close(circ
);
403 /** Find all the apconns in state AP_CONN_STATE_RENDDESC_WAIT that
404 * are waiting on query. If there's a working cache entry here
405 * with at least one intro point, move them to the next state;
409 rend_client_desc_here(const char *query
)
412 rend_cache_entry_t
*entry
;
413 time_t now
= time(NULL
);
415 while ((conn
= connection_get_by_type_state_rendquery(CONN_TYPE_AP
,
416 AP_CONN_STATE_RENDDESC_WAIT
, query
))) {
417 if (rend_cache_lookup_entry(conn
->rend_query
, -1, &entry
) == 1 &&
418 entry
->parsed
->n_intro_points
> 0) {
419 /* either this fetch worked, or it failed but there was a
420 * valid entry from before which we should reuse */
421 log_fn(LOG_INFO
,"Rend desc is usable. Launching circuits.");
422 conn
->state
= AP_CONN_STATE_CIRCUIT_WAIT
;
424 /* restart their timeout values, so they get a fair shake at
425 * connecting to the hidden service. */
426 conn
->timestamp_created
= now
;
427 conn
->timestamp_lastread
= now
;
428 conn
->timestamp_lastwritten
= now
;
430 if (connection_ap_handshake_attach_circuit(conn
) < 0) {
431 /* it will never work */
432 log_fn(LOG_WARN
,"Rendezvous attempt failed. Closing.");
433 connection_mark_unattached_ap(conn
, END_STREAM_REASON_CANT_ATTACH
);
435 tor_assert(conn
->state
!= AP_CONN_STATE_RENDDESC_WAIT
); /* avoid loop */
436 } else { /* 404, or fetch didn't get that far */
437 log_fn(LOG_NOTICE
,"Closing stream for '%s.onion': hidden service is unavailable (try again later).", safe_str(query
));
438 connection_mark_unattached_ap(conn
, END_STREAM_REASON_TIMEOUT
);
443 /** Return a newly allocated extend_info_t* for a randomly chosen introduction
444 * point for the named hidden service. Return NULL if all introduction points
445 * have been tried and failed.
448 rend_client_get_random_intro(const char *query
)
451 rend_cache_entry_t
*entry
;
453 if (rend_cache_lookup_entry(query
, -1, &entry
) < 1) {
454 log_fn(LOG_WARN
,"Query '%s' didn't have valid rend desc in cache. Failing.",
460 if (!entry
->parsed
->n_intro_points
)
463 i
= crypto_rand_int(entry
->parsed
->n_intro_points
);
465 if (entry
->parsed
->intro_point_extend_info
) {
466 return extend_info_dup(entry
->parsed
->intro_point_extend_info
[i
]);
468 /* add the intro point nicknames */
469 char *choice
= entry
->parsed
->intro_points
[i
];
470 routerinfo_t
*router
= router_get_by_nickname(choice
, 0);
472 log_fn(LOG_INFO
, "Unknown router with nickname '%s'; trying another.",choice
);
474 entry
->parsed
->intro_points
[i
] =
475 entry
->parsed
->intro_points
[--entry
->parsed
->n_intro_points
];
478 return extend_info_from_router(router
);