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
, "Couldn't get 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
) {
59 char payload
[RELAY_PAYLOAD_SIZE
];
60 char tmp
[1+(MAX_HEX_NICKNAME_LEN
+1)+REND_COOKIE_LEN
+DH_KEY_LEN
];
61 rend_cache_entry_t
*entry
;
64 tor_assert(introcirc
->purpose
== CIRCUIT_PURPOSE_C_INTRODUCING
);
65 tor_assert(rendcirc
->purpose
== CIRCUIT_PURPOSE_C_REND_READY
);
66 tor_assert(!rend_cmp_service_ids(introcirc
->rend_query
, rendcirc
->rend_query
));
68 if (rend_cache_lookup_entry(introcirc
->rend_query
, &entry
) < 1) {
69 log_fn(LOG_WARN
,"query '%s' didn't have valid rend desc in cache. Failing.",
70 safe_str(introcirc
->rend_query
));
74 /* first 20 bytes of payload are the hash of bob's pk */
75 if (crypto_pk_get_digest(entry
->parsed
->pk
, payload
)<0) {
76 log_fn(LOG_WARN
, "Couldn't hash public key.");
80 /* Initialize the pending_final_cpath and start the DH handshake. */
81 cpath
= rendcirc
->build_state
->pending_final_cpath
;
83 cpath
= rendcirc
->build_state
->pending_final_cpath
=
84 tor_malloc_zero(sizeof(crypt_path_t
));
85 cpath
->magic
= CRYPT_PATH_MAGIC
;
86 if (!(cpath
->dh_handshake_state
= crypto_dh_new())) {
87 log_fn(LOG_WARN
, "Couldn't allocate DH");
90 if (crypto_dh_generate_public(cpath
->dh_handshake_state
)<0) {
91 log_fn(LOG_WARN
, "Couldn't generate g^x");
96 /* write the remaining items into tmp */
98 tmp
[0] = 1; /* version 1 of the cell format */
100 strncpy(tmp
+1, rendcirc
->build_state
->chosen_exit_name
, (MAX_HEX_NICKNAME_LEN
+1));
101 memcpy(tmp
+1+MAX_HEX_NICKNAME_LEN
+1, rendcirc
->rend_cookie
, REND_COOKIE_LEN
);
103 strncpy(tmp
, rendcirc
->build_state
->chosen_exit_name
, (MAX_NICKNAME_LEN
+1)); /* nul pads */
104 memcpy(tmp
+MAX_NICKNAME_LEN
+1, rendcirc
->rend_cookie
, REND_COOKIE_LEN
);
106 if (crypto_dh_get_public(cpath
->dh_handshake_state
,
108 tmp
+1+MAX_HEX_NICKNAME_LEN
+1+REND_COOKIE_LEN
,
110 tmp
+MAX_NICKNAME_LEN
+1+REND_COOKIE_LEN
,
114 log_fn(LOG_WARN
, "Couldn't extract g^x");
118 /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
119 * to avoid buffer overflows? */
120 r
= crypto_pk_public_hybrid_encrypt(entry
->parsed
->pk
, payload
+DIGEST_LEN
, tmp
,
122 1+MAX_HEX_NICKNAME_LEN
+1+REND_COOKIE_LEN
+DH_KEY_LEN
,
124 MAX_NICKNAME_LEN
+1+REND_COOKIE_LEN
+DH_KEY_LEN
,
126 PK_PKCS1_OAEP_PADDING
, 0);
128 log_fn(LOG_WARN
,"hybrid pk encrypt failed.");
132 tor_assert(DIGEST_LEN
+ r
<= RELAY_PAYLOAD_SIZE
); /* we overran something */
133 payload_len
= DIGEST_LEN
+ r
;
135 if (connection_edge_send_command(NULL
, introcirc
,
136 RELAY_COMMAND_INTRODUCE1
,
137 payload
, payload_len
,
138 introcirc
->cpath
->prev
)<0) {
139 /* introcirc is already marked for close. leave rendcirc alone. */
140 log_fn(LOG_WARN
, "Couldn't send INTRODUCE1 cell");
144 /* Now, we wait for an ACK or NAK on this circuit. */
145 introcirc
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT
;
149 circuit_mark_for_close(introcirc
);
150 circuit_mark_for_close(rendcirc
);
154 /** Called when a rendezvous circuit is open; sends a establish
155 * rendezvous circuit as appropriate. */
157 rend_client_rendcirc_has_opened(circuit_t
*circ
)
159 tor_assert(circ
->purpose
== CIRCUIT_PURPOSE_C_ESTABLISH_REND
);
160 tor_assert(CIRCUIT_IS_ORIGIN(circ
));
162 log_fn(LOG_INFO
,"rendcirc is open");
164 /* generate a rendezvous cookie, store it in circ */
165 if (rend_client_send_establish_rendezvous(circ
) < 0) {
170 /** Called when get an ACK or a NAK for a REND_INTRODUCE1 cell.
173 rend_client_introduction_acked(circuit_t
*circ
,
174 const char *request
, size_t request_len
)
179 if (circ
->purpose
!= CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT
) {
180 log_fn(LOG_WARN
, "Received REND_INTRODUCE_ACK on unexpected circuit %d",
182 circuit_mark_for_close(circ
);
186 tor_assert(circ
->build_state
->chosen_exit_name
);
188 if (request_len
== 0) {
189 /* It's an ACK; the introduction point relayed our introduction request. */
190 /* Locate the rend circ which is waiting to hear about this ack,
193 log_fn(LOG_INFO
,"Received ack. Telling rend circ...");
194 rendcirc
= circuit_get_by_rend_query_and_purpose(
195 circ
->rend_query
, CIRCUIT_PURPOSE_C_REND_READY
);
196 if (rendcirc
) { /* remember the ack */
197 rendcirc
->purpose
= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED
;
199 log_fn(LOG_INFO
,"...Found no rend circ. Dropping on the floor.");
201 /* close the circuit: we won't need it anymore. */
202 circ
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCE_ACKED
;
203 circuit_mark_for_close(circ
);
205 /* It's a NAK; the introduction point didn't relay our request. */
206 circ
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCING
;
207 /* Remove this intro point from the set of viable introduction
208 * points. If any remain, extend to a new one and try again.
209 * If none remain, refetch the service descriptor.
211 if (rend_client_remove_intro_point(circ
->build_state
->chosen_exit_name
,
212 circ
->rend_query
) > 0) {
213 /* There are introduction points left. re-extend the circuit to
214 * another intro point and try again. */
216 nickname
= rend_client_get_random_intro(circ
->rend_query
);
217 tor_assert(nickname
);
218 log_fn(LOG_INFO
,"Got nack for %s from %s, extending to %s.",
219 safe_str(circ
->rend_query
),
220 circ
->build_state
->chosen_exit_name
, nickname
);
221 if (!(r
= router_get_by_nickname(nickname
))) {
222 log_fn(LOG_WARN
, "Advertised intro point '%s' for %s is not known. Closing.",
223 nickname
, safe_str(circ
->rend_query
));
225 circuit_mark_for_close(circ
);
228 log_fn(LOG_INFO
, "Chose new intro point %s for %s (circ %d)",
229 nickname
, safe_str(circ
->rend_query
), circ
->n_circ_id
);
231 return circuit_extend_to_new_exit(circ
, r
);
237 /** If we are not currently fetching a rendezvous service descriptor
238 * for the service ID <b>query</b>, start a directory connection to fetch a
242 rend_client_refetch_renddesc(const char *query
)
244 if (connection_get_by_type_state_rendquery(CONN_TYPE_DIR
, 0, query
)) {
245 log_fn(LOG_INFO
,"Would fetch a new renddesc here (for %s), but one is already in progress.", safe_str(query
));
247 /* not one already; initiate a dir rend desc lookup */
248 directory_get_from_dirserver(DIR_PURPOSE_FETCH_RENDDESC
, query
, 1);
252 /** remove failed_intro from ent. if ent now has no intro points, or
253 * service is unrecognized, then launch a new renddesc fetch.
255 * Return -1 if error, 0 if no intro points remain or service
256 * unrecognized, 1 if recognized and some intro points remain.
259 rend_client_remove_intro_point(char *failed_intro
, const char *query
)
262 rend_cache_entry_t
*ent
;
265 r
= rend_cache_lookup_entry(query
, &ent
);
267 log_fn(LOG_WARN
, "Malformed service ID '%s'", safe_str(query
));
271 log_fn(LOG_INFO
, "Unknown service %s. Re-fetching descriptor.",
273 rend_client_refetch_renddesc(query
);
277 for (i
=0; i
< ent
->parsed
->n_intro_points
; ++i
) {
278 if (!strcasecmp(ent
->parsed
->intro_points
[i
], failed_intro
)) {
279 tor_free(ent
->parsed
->intro_points
[i
]);
280 ent
->parsed
->intro_points
[i
] =
281 ent
->parsed
->intro_points
[--ent
->parsed
->n_intro_points
];
286 if (!ent
->parsed
->n_intro_points
) {
287 log_fn(LOG_INFO
,"No more intro points remain for %s. Re-fetching descriptor.",
289 rend_client_refetch_renddesc(query
);
291 /* move all pending streams back to renddesc_wait */
292 while ((conn
= connection_get_by_type_state_rendquery(CONN_TYPE_AP
,
293 AP_CONN_STATE_CIRCUIT_WAIT
, query
))) {
294 conn
->state
= AP_CONN_STATE_RENDDESC_WAIT
;
299 log_fn(LOG_INFO
,"%d options left for %s.",
300 ent
->parsed
->n_intro_points
, safe_str(query
));
304 /** Called when we receive a RENDEZVOUS_ESTABLISHED cell; changes the state of
305 * the circuit to C_REND_READY.
308 rend_client_rendezvous_acked(circuit_t
*circ
, const char *request
, size_t request_len
)
310 /* we just got an ack for our establish-rendezvous. switch purposes. */
311 if (circ
->purpose
!= CIRCUIT_PURPOSE_C_ESTABLISH_REND
) {
312 log_fn(LOG_WARN
,"Got a rendezvous ack when we weren't expecting one. Closing circ.");
313 circuit_mark_for_close(circ
);
316 log_fn(LOG_INFO
,"Got rendezvous ack. This circuit is now ready for rendezvous.");
317 circ
->purpose
= CIRCUIT_PURPOSE_C_REND_READY
;
321 /** Bob sent us a rendezvous cell; join the circuits. */
323 rend_client_receive_rendezvous(circuit_t
*circ
, const char *request
, size_t request_len
)
326 char keys
[DIGEST_LEN
+CPATH_KEY_MATERIAL_LEN
];
328 if ((circ
->purpose
!= CIRCUIT_PURPOSE_C_REND_READY
&&
329 circ
->purpose
!= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED
)
330 || !circ
->build_state
->pending_final_cpath
) {
331 log_fn(LOG_WARN
,"Got rendezvous2 cell from Bob, but not expecting it. Closing.");
332 circuit_mark_for_close(circ
);
336 if (request_len
!= DH_KEY_LEN
+DIGEST_LEN
) {
337 log_fn(LOG_WARN
,"Incorrect length (%d) on RENDEZVOUS2 cell.",(int)request_len
);
341 /* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/
342 tor_assert(circ
->build_state
);
343 tor_assert(circ
->build_state
->pending_final_cpath
);
344 hop
= circ
->build_state
->pending_final_cpath
;
345 tor_assert(hop
->dh_handshake_state
);
346 if (crypto_dh_compute_secret(hop
->dh_handshake_state
, request
, DH_KEY_LEN
,
347 keys
, DIGEST_LEN
+CPATH_KEY_MATERIAL_LEN
)<0) {
348 log_fn(LOG_WARN
, "Couldn't complete DH handshake");
351 /* ... and set up cpath. */
352 if (circuit_init_cpath_crypto(hop
, keys
+DIGEST_LEN
, 0)<0)
355 /* Check whether the digest is right... */
356 if (memcmp(keys
, request
+DH_KEY_LEN
, DIGEST_LEN
)) {
357 log_fn(LOG_WARN
, "Incorrect digest of key material");
361 crypto_dh_free(hop
->dh_handshake_state
);
362 hop
->dh_handshake_state
= NULL
;
364 /* All is well. Extend the circuit. */
365 circ
->purpose
= CIRCUIT_PURPOSE_C_REND_JOINED
;
366 hop
->state
= CPATH_STATE_OPEN
;
367 /* set the windows to default. these are the windows
368 * that alice thinks bob has.
370 hop
->package_window
= CIRCWINDOW_START
;
371 hop
->deliver_window
= CIRCWINDOW_START
;
373 onion_append_to_cpath(&circ
->cpath
, hop
);
374 circ
->build_state
->pending_final_cpath
= NULL
; /* prevent double-free */
377 circuit_mark_for_close(circ
);
381 /** Find all the apconns in state AP_CONN_STATE_RENDDESC_WAIT that
382 * are waiting on query. If there's a working cache entry here
383 * with at least one intro point, move them to the next state;
386 void rend_client_desc_here(char *query
) {
388 rend_cache_entry_t
*entry
;
389 time_t now
= time(NULL
);
391 while ((conn
= connection_get_by_type_state_rendquery(CONN_TYPE_AP
,
392 AP_CONN_STATE_RENDDESC_WAIT
, query
))) {
393 if (rend_cache_lookup_entry(conn
->rend_query
, &entry
) == 1 &&
394 entry
->parsed
->n_intro_points
> 0) {
395 /* either this fetch worked, or it failed but there was a
396 * valid entry from before which we should reuse */
397 log_fn(LOG_INFO
,"Rend desc is usable. Launching circuits.");
398 conn
->state
= AP_CONN_STATE_CIRCUIT_WAIT
;
400 /* restart their timeout values, so they get a fair shake at
401 * connecting to the hidden service. */
402 conn
->timestamp_created
= now
;
403 conn
->timestamp_lastread
= now
;
404 conn
->timestamp_lastwritten
= now
;
406 if (connection_ap_handshake_attach_circuit(conn
) < 0) {
407 /* it will never work */
408 log_fn(LOG_WARN
,"attaching to a rend circ failed. Closing conn.");
409 connection_mark_unattached_ap(conn
, END_STREAM_REASON_CANT_ATTACH
);
411 tor_assert(conn
->state
!= AP_CONN_STATE_RENDDESC_WAIT
); /* avoid loop */
412 } else { /* 404, or fetch didn't get that far */
413 log_fn(LOG_NOTICE
,"Closing stream for '%s.onion': hidden service is unavailable (try again later).", safe_str(query
));
414 connection_mark_unattached_ap(conn
, END_STREAM_REASON_TIMEOUT
);
419 /** strdup a nickname for a random introduction
420 * point of query. return NULL if error.
422 char *rend_client_get_random_intro(char *query
) {
427 rend_cache_entry_t
*entry
;
429 if (rend_cache_lookup_entry(query
, &entry
) < 1) {
430 log_fn(LOG_WARN
,"query '%s' didn't have valid rend desc in cache. Failing.",
435 sl
= smartlist_create();
437 /* add the intro point nicknames */
438 for (i
=0;i
<entry
->parsed
->n_intro_points
;i
++)
439 smartlist_add(sl
,entry
->parsed
->intro_points
[i
]);
441 choice
= smartlist_choose(sl
);
446 nickname
= tor_strdup(choice
);