diff options
Diffstat (limited to 'src/lib/util/client.c')
-rw-r--r-- | src/lib/util/client.c | 1112 |
1 files changed, 1112 insertions, 0 deletions
diff --git a/src/lib/util/client.c b/src/lib/util/client.c new file mode 100644 index 000000000..fb2120ee8 --- /dev/null +++ b/src/lib/util/client.c | |||
@@ -0,0 +1,1112 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001-2016, 2019 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/client.c | ||
23 | * @brief code for access to services | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * Generic TCP code for reliable, record-oriented TCP | ||
27 | * connections between clients and service providers. | ||
28 | */ | ||
29 | |||
30 | #include "platform.h" | ||
31 | #include "gnunet_protocols.h" | ||
32 | #include "gnunet_util_lib.h" | ||
33 | #include "gnunet_resolver_service.h" | ||
34 | #include "gnunet_socks.h" | ||
35 | |||
36 | |||
37 | #define LOG(kind, ...) GNUNET_log_from (kind, "util-client", __VA_ARGS__) | ||
38 | |||
39 | /** | ||
40 | * Timeout we use on TCP connect before trying another | ||
41 | * result from the DNS resolver. Actual value used | ||
42 | * is this value divided by the number of address families. | ||
43 | * Default is 5s. | ||
44 | */ | ||
45 | #define CONNECT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
46 | GNUNET_TIME_UNIT_SECONDS, 5) | ||
47 | |||
48 | |||
49 | /** | ||
50 | * Internal state for a client connected to a GNUnet service. | ||
51 | */ | ||
52 | struct ClientState; | ||
53 | |||
54 | |||
55 | /** | ||
56 | * During connect, we try multiple possible IP addresses | ||
57 | * to find out which one might work. | ||
58 | */ | ||
59 | struct AddressProbe | ||
60 | { | ||
61 | /** | ||
62 | * This is a linked list. | ||
63 | */ | ||
64 | struct AddressProbe *next; | ||
65 | |||
66 | /** | ||
67 | * This is a doubly-linked list. | ||
68 | */ | ||
69 | struct AddressProbe *prev; | ||
70 | |||
71 | /** | ||
72 | * The address; do not free (allocated at the end of this struct). | ||
73 | */ | ||
74 | const struct sockaddr *addr; | ||
75 | |||
76 | /** | ||
77 | * Underlying OS's socket. | ||
78 | */ | ||
79 | struct GNUNET_NETWORK_Handle *sock; | ||
80 | |||
81 | /** | ||
82 | * Connection for which we are probing. | ||
83 | */ | ||
84 | struct ClientState *cstate; | ||
85 | |||
86 | /** | ||
87 | * Length of addr. | ||
88 | */ | ||
89 | socklen_t addrlen; | ||
90 | |||
91 | /** | ||
92 | * Task waiting for the connection to finish connecting. | ||
93 | */ | ||
94 | struct GNUNET_SCHEDULER_Task *task; | ||
95 | }; | ||
96 | |||
97 | |||
98 | /** | ||
99 | * Internal state for a client connected to a GNUnet service. | ||
100 | */ | ||
101 | struct ClientState | ||
102 | { | ||
103 | /** | ||
104 | * The connection handle, NULL if not live | ||
105 | */ | ||
106 | struct GNUNET_NETWORK_Handle *sock; | ||
107 | |||
108 | /** | ||
109 | * Handle to a pending DNS lookup request, NULL if DNS is finished. | ||
110 | */ | ||
111 | struct GNUNET_RESOLVER_RequestHandle *dns_active; | ||
112 | |||
113 | /** | ||
114 | * Our configuration. | ||
115 | */ | ||
116 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
117 | |||
118 | /** | ||
119 | * Linked list of sockets we are currently trying out | ||
120 | * (during connect). | ||
121 | */ | ||
122 | struct AddressProbe *ap_head; | ||
123 | |||
124 | /** | ||
125 | * Linked list of sockets we are currently trying out | ||
126 | * (during connect). | ||
127 | */ | ||
128 | struct AddressProbe *ap_tail; | ||
129 | |||
130 | /** | ||
131 | * Name of the service we interact with. | ||
132 | */ | ||
133 | char *service_name; | ||
134 | |||
135 | /** | ||
136 | * Hostname, if any. | ||
137 | */ | ||
138 | char *hostname; | ||
139 | |||
140 | /** | ||
141 | * Next message to transmit to the service. NULL for none. | ||
142 | */ | ||
143 | const struct GNUNET_MessageHeader *msg; | ||
144 | |||
145 | /** | ||
146 | * Task for trying to connect to the service. | ||
147 | */ | ||
148 | struct GNUNET_SCHEDULER_Task *retry_task; | ||
149 | |||
150 | /** | ||
151 | * Task for sending messages to the service. | ||
152 | */ | ||
153 | struct GNUNET_SCHEDULER_Task *send_task; | ||
154 | |||
155 | /** | ||
156 | * Task for sending messages to the service. | ||
157 | */ | ||
158 | struct GNUNET_SCHEDULER_Task *recv_task; | ||
159 | |||
160 | /** | ||
161 | * Tokenizer for inbound messages. | ||
162 | */ | ||
163 | struct GNUNET_MessageStreamTokenizer *mst; | ||
164 | |||
165 | /** | ||
166 | * Message queue under our control. | ||
167 | */ | ||
168 | struct GNUNET_MQ_Handle *mq; | ||
169 | |||
170 | /** | ||
171 | * Timeout for receiving a response (absolute time). | ||
172 | */ | ||
173 | struct GNUNET_TIME_Absolute receive_timeout; | ||
174 | |||
175 | /** | ||
176 | * Current value for our incremental back-off (for | ||
177 | * connect re-tries). | ||
178 | */ | ||
179 | struct GNUNET_TIME_Relative back_off; | ||
180 | |||
181 | /** | ||
182 | * TCP port (0 for disabled). | ||
183 | */ | ||
184 | unsigned long long port; | ||
185 | |||
186 | /** | ||
187 | * Offset in the message where we are for transmission. | ||
188 | */ | ||
189 | size_t msg_off; | ||
190 | |||
191 | /** | ||
192 | * How often have we tried to connect? | ||
193 | */ | ||
194 | unsigned int attempts; | ||
195 | |||
196 | /** | ||
197 | * Are we supposed to die? #GNUNET_SYSERR if destruction must be | ||
198 | * deferred, #GNUNET_NO by default, #GNUNET_YES if destruction was | ||
199 | * deferred. | ||
200 | */ | ||
201 | int in_destroy; | ||
202 | }; | ||
203 | |||
204 | |||
205 | /** | ||
206 | * Try to connect to the service. | ||
207 | * | ||
208 | * @param cls the `struct ClientState` to try to connect to the service | ||
209 | */ | ||
210 | static void | ||
211 | start_connect (void *cls); | ||
212 | |||
213 | |||
214 | /** | ||
215 | * We've failed for good to establish a connection (timeout or | ||
216 | * no more addresses to try). | ||
217 | * | ||
218 | * @param cstate the connection we tried to establish | ||
219 | */ | ||
220 | static void | ||
221 | connect_fail_continuation (struct ClientState *cstate) | ||
222 | { | ||
223 | GNUNET_break (NULL == cstate->ap_head); | ||
224 | GNUNET_break (NULL == cstate->ap_tail); | ||
225 | GNUNET_break (NULL == cstate->dns_active); | ||
226 | GNUNET_break (NULL == cstate->sock); | ||
227 | GNUNET_assert (NULL == cstate->send_task); | ||
228 | GNUNET_assert (NULL == cstate->recv_task); | ||
229 | // GNUNET_assert (NULL == cstate->proxy_handshake); | ||
230 | |||
231 | cstate->back_off = GNUNET_TIME_STD_BACKOFF (cstate->back_off); | ||
232 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
233 | "Failed to establish connection to `%s', no further addresses to try, will try again in %s.\n", | ||
234 | cstate->service_name, | ||
235 | GNUNET_STRINGS_relative_time_to_string (cstate->back_off, | ||
236 | GNUNET_YES)); | ||
237 | cstate->retry_task | ||
238 | = GNUNET_SCHEDULER_add_delayed (cstate->back_off, | ||
239 | &start_connect, | ||
240 | cstate); | ||
241 | } | ||
242 | |||
243 | |||
244 | /** | ||
245 | * We are ready to send a message to the service. | ||
246 | * | ||
247 | * @param cls the `struct ClientState` with the `msg` to transmit | ||
248 | */ | ||
249 | static void | ||
250 | transmit_ready (void *cls) | ||
251 | { | ||
252 | struct ClientState *cstate = cls; | ||
253 | ssize_t ret; | ||
254 | size_t len; | ||
255 | const char *pos; | ||
256 | int notify_in_flight; | ||
257 | |||
258 | cstate->send_task = NULL; | ||
259 | if (GNUNET_YES == cstate->in_destroy) | ||
260 | return; | ||
261 | pos = (const char *) cstate->msg; | ||
262 | len = ntohs (cstate->msg->size); | ||
263 | GNUNET_assert (cstate->msg_off < len); | ||
264 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
265 | "message of type %u and size %u trying to send with socket %p (MQ: %p\n", | ||
266 | ntohs (cstate->msg->type), | ||
267 | ntohs (cstate->msg->size), | ||
268 | cstate->sock, | ||
269 | cstate->mq); | ||
270 | |||
271 | RETRY: | ||
272 | ret = GNUNET_NETWORK_socket_send (cstate->sock, | ||
273 | &pos[cstate->msg_off], | ||
274 | len - cstate->msg_off); | ||
275 | if ( (-1 == ret) && | ||
276 | ( (EAGAIN == errno) || | ||
277 | (EINTR == errno) ) ) | ||
278 | { | ||
279 | /* ignore */ | ||
280 | ret = 0; | ||
281 | } | ||
282 | if (-1 == ret) | ||
283 | { | ||
284 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
285 | "Error during sending message of type %u: %s\n", | ||
286 | ntohs (cstate->msg->type), | ||
287 | strerror (errno)); | ||
288 | if (EINTR == errno) | ||
289 | { | ||
290 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
291 | "Retrying message of type %u\n", | ||
292 | ntohs (cstate->msg->type)); | ||
293 | goto RETRY; | ||
294 | } | ||
295 | GNUNET_MQ_inject_error (cstate->mq, | ||
296 | GNUNET_MQ_ERROR_WRITE); | ||
297 | return; | ||
298 | } | ||
299 | notify_in_flight = (0 == cstate->msg_off); | ||
300 | cstate->msg_off += ret; | ||
301 | if (cstate->msg_off < len) | ||
302 | { | ||
303 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
304 | "rescheduling message of type %u\n", | ||
305 | ntohs (cstate->msg->type)); | ||
306 | cstate->send_task | ||
307 | = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
308 | cstate->sock, | ||
309 | &transmit_ready, | ||
310 | cstate); | ||
311 | if (notify_in_flight) | ||
312 | GNUNET_MQ_impl_send_in_flight (cstate->mq); | ||
313 | return; | ||
314 | } | ||
315 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
316 | "sending message of type %u and size %u successful\n", | ||
317 | ntohs (cstate->msg->type), | ||
318 | ntohs (cstate->msg->size)); | ||
319 | cstate->msg = NULL; | ||
320 | GNUNET_MQ_impl_send_continue (cstate->mq); | ||
321 | } | ||
322 | |||
323 | |||
324 | /** | ||
325 | * We have received a full message, pass to the MQ dispatcher. | ||
326 | * Called by the tokenizer via #receive_ready(). | ||
327 | * | ||
328 | * @param cls the `struct ClientState` | ||
329 | * @param msg message we received. | ||
330 | * @return #GNUNET_OK on success, | ||
331 | * #GNUNET_NO to stop further processing due to disconnect (no error) | ||
332 | * #GNUNET_SYSERR to stop further processing due to error | ||
333 | */ | ||
334 | static enum GNUNET_GenericReturnValue | ||
335 | recv_message (void *cls, | ||
336 | const struct GNUNET_MessageHeader *msg) | ||
337 | { | ||
338 | struct ClientState *cstate = cls; | ||
339 | |||
340 | if (GNUNET_YES == cstate->in_destroy) | ||
341 | return GNUNET_NO; | ||
342 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
343 | "Received message of type %u and size %u from %s\n", | ||
344 | ntohs (msg->type), | ||
345 | ntohs (msg->size), | ||
346 | cstate->service_name); | ||
347 | GNUNET_MQ_inject_message (cstate->mq, | ||
348 | msg); | ||
349 | if (GNUNET_YES == cstate->in_destroy) | ||
350 | return GNUNET_NO; | ||
351 | return GNUNET_OK; | ||
352 | } | ||
353 | |||
354 | |||
355 | /** | ||
356 | * Cancel all remaining connect attempts | ||
357 | * | ||
358 | * @param cstate handle of the client state to process | ||
359 | */ | ||
360 | static void | ||
361 | cancel_aps (struct ClientState *cstate) | ||
362 | { | ||
363 | struct AddressProbe *pos; | ||
364 | |||
365 | while (NULL != (pos = cstate->ap_head)) | ||
366 | { | ||
367 | GNUNET_break (GNUNET_OK == | ||
368 | GNUNET_NETWORK_socket_close (pos->sock)); | ||
369 | GNUNET_SCHEDULER_cancel (pos->task); | ||
370 | GNUNET_CONTAINER_DLL_remove (cstate->ap_head, | ||
371 | cstate->ap_tail, | ||
372 | pos); | ||
373 | GNUNET_free (pos); | ||
374 | } | ||
375 | } | ||
376 | |||
377 | |||
378 | /** | ||
379 | * Implement the destruction of a message queue. Implementations must | ||
380 | * not free @a mq, but should take care of @a impl_state. | ||
381 | * | ||
382 | * @param mq the message queue to destroy | ||
383 | * @param impl_state our `struct ClientState` | ||
384 | */ | ||
385 | static void | ||
386 | connection_client_destroy_impl (struct GNUNET_MQ_Handle *mq, | ||
387 | void *impl_state) | ||
388 | { | ||
389 | struct ClientState *cstate = impl_state; | ||
390 | |||
391 | (void) mq; | ||
392 | if (NULL != cstate->dns_active) | ||
393 | { | ||
394 | GNUNET_RESOLVER_request_cancel (cstate->dns_active); | ||
395 | cstate->dns_active = NULL; | ||
396 | } | ||
397 | if (NULL != cstate->send_task) | ||
398 | { | ||
399 | GNUNET_SCHEDULER_cancel (cstate->send_task); | ||
400 | cstate->send_task = NULL; | ||
401 | } | ||
402 | if (NULL != cstate->retry_task) | ||
403 | { | ||
404 | GNUNET_SCHEDULER_cancel (cstate->retry_task); | ||
405 | cstate->retry_task = NULL; | ||
406 | } | ||
407 | if (GNUNET_SYSERR == cstate->in_destroy) | ||
408 | { | ||
409 | /* defer destruction */ | ||
410 | cstate->in_destroy = GNUNET_YES; | ||
411 | cstate->mq = NULL; | ||
412 | return; | ||
413 | } | ||
414 | if (NULL != cstate->recv_task) | ||
415 | { | ||
416 | GNUNET_SCHEDULER_cancel (cstate->recv_task); | ||
417 | cstate->recv_task = NULL; | ||
418 | } | ||
419 | if (NULL != cstate->sock) | ||
420 | { | ||
421 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
422 | "destroying socket: %p\n", | ||
423 | cstate->sock); | ||
424 | GNUNET_NETWORK_socket_close (cstate->sock); | ||
425 | } | ||
426 | cancel_aps (cstate); | ||
427 | GNUNET_free (cstate->service_name); | ||
428 | GNUNET_free (cstate->hostname); | ||
429 | GNUNET_MST_destroy (cstate->mst); | ||
430 | GNUNET_free (cstate); | ||
431 | } | ||
432 | |||
433 | |||
434 | /** | ||
435 | * This function is called once we have data ready to read. | ||
436 | * | ||
437 | * @param cls `struct ClientState` with connection to read from | ||
438 | */ | ||
439 | static void | ||
440 | receive_ready (void *cls) | ||
441 | { | ||
442 | struct ClientState *cstate = cls; | ||
443 | enum GNUNET_GenericReturnValue ret; | ||
444 | |||
445 | cstate->recv_task = NULL; | ||
446 | cstate->in_destroy = GNUNET_SYSERR; | ||
447 | ret = GNUNET_MST_read (cstate->mst, | ||
448 | cstate->sock, | ||
449 | GNUNET_NO, | ||
450 | GNUNET_NO); | ||
451 | if (GNUNET_SYSERR == ret) | ||
452 | { | ||
453 | if (NULL != cstate->mq) | ||
454 | GNUNET_MQ_inject_error (cstate->mq, | ||
455 | GNUNET_MQ_ERROR_READ); | ||
456 | if (GNUNET_YES == cstate->in_destroy) | ||
457 | connection_client_destroy_impl (cstate->mq, | ||
458 | cstate); | ||
459 | return; | ||
460 | } | ||
461 | if (GNUNET_YES == cstate->in_destroy) | ||
462 | { | ||
463 | connection_client_destroy_impl (cstate->mq, | ||
464 | cstate); | ||
465 | return; | ||
466 | } | ||
467 | cstate->in_destroy = GNUNET_NO; | ||
468 | GNUNET_assert (NULL == cstate->recv_task); | ||
469 | cstate->recv_task | ||
470 | = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
471 | cstate->sock, | ||
472 | &receive_ready, | ||
473 | cstate); | ||
474 | } | ||
475 | |||
476 | |||
477 | /** | ||
478 | * We've succeeded in establishing a connection. | ||
479 | * | ||
480 | * @param cstate the connection we tried to establish | ||
481 | */ | ||
482 | static void | ||
483 | connect_success_continuation (struct ClientState *cstate) | ||
484 | { | ||
485 | GNUNET_assert (NULL == cstate->recv_task); | ||
486 | cstate->recv_task | ||
487 | = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
488 | cstate->sock, | ||
489 | &receive_ready, | ||
490 | cstate); | ||
491 | if (NULL != cstate->msg) | ||
492 | { | ||
493 | GNUNET_assert (NULL == cstate->send_task); | ||
494 | cstate->send_task | ||
495 | = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
496 | cstate->sock, | ||
497 | &transmit_ready, | ||
498 | cstate); | ||
499 | } | ||
500 | } | ||
501 | |||
502 | |||
503 | /** | ||
504 | * Try connecting to the server using UNIX domain sockets. | ||
505 | * | ||
506 | * @param service_name name of service to connect to | ||
507 | * @param cfg configuration to use | ||
508 | * @return NULL on error, socket connected to UNIX otherwise | ||
509 | */ | ||
510 | static struct GNUNET_NETWORK_Handle * | ||
511 | try_unixpath (const char *service_name, | ||
512 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
513 | { | ||
514 | #if AF_UNIX | ||
515 | struct GNUNET_NETWORK_Handle *sock; | ||
516 | char *unixpath; | ||
517 | struct sockaddr_un s_un; | ||
518 | |||
519 | unixpath = NULL; | ||
520 | if ((GNUNET_OK == | ||
521 | GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
522 | service_name, | ||
523 | "UNIXPATH", | ||
524 | &unixpath)) && | ||
525 | (0 < strlen (unixpath))) | ||
526 | { | ||
527 | /* We have a non-NULL unixpath, need to validate it */ | ||
528 | if (strlen (unixpath) >= sizeof(s_un.sun_path)) | ||
529 | { | ||
530 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
531 | _ ("UNIXPATH `%s' too long, maximum length is %llu\n"), | ||
532 | unixpath, | ||
533 | (unsigned long long) sizeof(s_un.sun_path)); | ||
534 | unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath); | ||
535 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
536 | _ ("Using `%s' instead\n"), | ||
537 | unixpath); | ||
538 | if (NULL == unixpath) | ||
539 | return NULL; | ||
540 | } | ||
541 | memset (&s_un, | ||
542 | 0, | ||
543 | sizeof(s_un)); | ||
544 | s_un.sun_family = AF_UNIX; | ||
545 | GNUNET_strlcpy (s_un.sun_path, | ||
546 | unixpath, | ||
547 | sizeof(s_un.sun_path)); | ||
548 | #if HAVE_SOCKADDR_UN_SUN_LEN | ||
549 | s_un.sun_len = (u_char) sizeof(struct sockaddr_un); | ||
550 | #endif | ||
551 | sock = GNUNET_NETWORK_socket_create (AF_UNIX, | ||
552 | SOCK_STREAM, | ||
553 | 0); | ||
554 | if ((NULL != sock) && | ||
555 | ((GNUNET_OK == | ||
556 | GNUNET_NETWORK_socket_connect (sock, | ||
557 | (struct sockaddr *) &s_un, | ||
558 | sizeof(s_un))) || | ||
559 | (EINPROGRESS == errno))) | ||
560 | { | ||
561 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
562 | "Successfully connected to unixpath `%s'!\n", | ||
563 | unixpath); | ||
564 | GNUNET_free (unixpath); | ||
565 | return sock; | ||
566 | } | ||
567 | if (NULL != sock) | ||
568 | GNUNET_NETWORK_socket_close (sock); | ||
569 | } | ||
570 | GNUNET_free (unixpath); | ||
571 | #endif | ||
572 | return NULL; | ||
573 | } | ||
574 | |||
575 | |||
576 | /** | ||
577 | * Scheduler let us know that we're either ready to write on the | ||
578 | * socket OR connect timed out. Do the right thing. | ||
579 | * | ||
580 | * @param cls the `struct AddressProbe *` with the address that we are probing | ||
581 | */ | ||
582 | static void | ||
583 | connect_probe_continuation (void *cls) | ||
584 | { | ||
585 | struct AddressProbe *ap = cls; | ||
586 | struct ClientState *cstate = ap->cstate; | ||
587 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
588 | int error; | ||
589 | socklen_t len; | ||
590 | |||
591 | ap->task = NULL; | ||
592 | GNUNET_assert (NULL != ap->sock); | ||
593 | GNUNET_CONTAINER_DLL_remove (cstate->ap_head, | ||
594 | cstate->ap_tail, | ||
595 | ap); | ||
596 | len = sizeof(error); | ||
597 | error = 0; | ||
598 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
599 | if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) || | ||
600 | (GNUNET_OK != | ||
601 | GNUNET_NETWORK_socket_getsockopt (ap->sock, | ||
602 | SOL_SOCKET, | ||
603 | SO_ERROR, | ||
604 | &error, | ||
605 | &len)) || | ||
606 | (0 != error)) | ||
607 | { | ||
608 | GNUNET_break (GNUNET_OK == | ||
609 | GNUNET_NETWORK_socket_close (ap->sock)); | ||
610 | GNUNET_free (ap); | ||
611 | if ((NULL == cstate->ap_head) && | ||
612 | // (NULL == cstate->proxy_handshake) && | ||
613 | (NULL == cstate->dns_active)) | ||
614 | connect_fail_continuation (cstate); | ||
615 | return; | ||
616 | } | ||
617 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
618 | "Connection to `%s' succeeded!\n", | ||
619 | cstate->service_name); | ||
620 | /* trigger jobs that waited for the connection */ | ||
621 | GNUNET_assert (NULL == cstate->sock); | ||
622 | cstate->sock = ap->sock; | ||
623 | GNUNET_free (ap); | ||
624 | cancel_aps (cstate); | ||
625 | connect_success_continuation (cstate); | ||
626 | } | ||
627 | |||
628 | |||
629 | /** | ||
630 | * Try to establish a connection given the specified address. | ||
631 | * This function is called by the resolver once we have a DNS reply. | ||
632 | * | ||
633 | * @param cls our `struct ClientState *` | ||
634 | * @param addr address to try, NULL for "last call" | ||
635 | * @param addrlen length of @a addr | ||
636 | */ | ||
637 | static void | ||
638 | try_connect_using_address (void *cls, | ||
639 | const struct sockaddr *addr, | ||
640 | socklen_t addrlen) | ||
641 | { | ||
642 | struct ClientState *cstate = cls; | ||
643 | struct AddressProbe *ap; | ||
644 | |||
645 | if (NULL == addr) | ||
646 | { | ||
647 | cstate->dns_active = NULL; | ||
648 | if ((NULL == cstate->ap_head) && | ||
649 | // (NULL == cstate->proxy_handshake) && | ||
650 | (NULL == cstate->sock)) | ||
651 | connect_fail_continuation (cstate); | ||
652 | return; | ||
653 | } | ||
654 | if (NULL != cstate->sock) | ||
655 | return; /* already connected */ | ||
656 | /* try to connect */ | ||
657 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
658 | "Trying to connect using address `%s:%u'\n", | ||
659 | GNUNET_a2s (addr, | ||
660 | addrlen), | ||
661 | (unsigned int) cstate->port); | ||
662 | ap = GNUNET_malloc (sizeof(struct AddressProbe) + addrlen); | ||
663 | ap->addr = (const struct sockaddr *) &ap[1]; | ||
664 | GNUNET_memcpy (&ap[1], | ||
665 | addr, | ||
666 | addrlen); | ||
667 | ap->addrlen = addrlen; | ||
668 | ap->cstate = cstate; | ||
669 | |||
670 | switch (ap->addr->sa_family) | ||
671 | { | ||
672 | case AF_INET: | ||
673 | ((struct sockaddr_in *) ap->addr)->sin_port = htons (cstate->port); | ||
674 | break; | ||
675 | |||
676 | case AF_INET6: | ||
677 | ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (cstate->port); | ||
678 | break; | ||
679 | |||
680 | default: | ||
681 | GNUNET_break (0); | ||
682 | GNUNET_free (ap); | ||
683 | return; /* not supported by us */ | ||
684 | } | ||
685 | ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family, | ||
686 | SOCK_STREAM, | ||
687 | 0); | ||
688 | if (NULL == ap->sock) | ||
689 | { | ||
690 | GNUNET_free (ap); | ||
691 | return; /* not supported by OS */ | ||
692 | } | ||
693 | if ((GNUNET_OK != | ||
694 | GNUNET_NETWORK_socket_connect (ap->sock, | ||
695 | ap->addr, | ||
696 | ap->addrlen)) && | ||
697 | (EINPROGRESS != errno)) | ||
698 | { | ||
699 | /* maybe refused / unsupported address, try next */ | ||
700 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, | ||
701 | "connect"); | ||
702 | GNUNET_break (GNUNET_OK == | ||
703 | GNUNET_NETWORK_socket_close (ap->sock)); | ||
704 | GNUNET_free (ap); | ||
705 | return; | ||
706 | } | ||
707 | GNUNET_CONTAINER_DLL_insert (cstate->ap_head, | ||
708 | cstate->ap_tail, | ||
709 | ap); | ||
710 | ap->task = GNUNET_SCHEDULER_add_write_net (CONNECT_RETRY_TIMEOUT, | ||
711 | ap->sock, | ||
712 | &connect_probe_continuation, | ||
713 | ap); | ||
714 | } | ||
715 | |||
716 | |||
717 | /** | ||
718 | * Test whether the configuration has proper values for connection | ||
719 | * (UNIXPATH || (PORT && HOSTNAME)). | ||
720 | * | ||
721 | * @param service_name name of service to connect to | ||
722 | * @param cfg configuration to use | ||
723 | * @return #GNUNET_OK if the configuration is valid, #GNUNET_SYSERR if not | ||
724 | */ | ||
725 | static int | ||
726 | test_service_configuration (const char *service_name, | ||
727 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
728 | { | ||
729 | int ret = GNUNET_SYSERR; | ||
730 | char *hostname = NULL; | ||
731 | unsigned long long port; | ||
732 | |||
733 | #if AF_UNIX | ||
734 | char *unixpath = NULL; | ||
735 | |||
736 | if ((GNUNET_OK == | ||
737 | GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
738 | service_name, | ||
739 | "UNIXPATH", | ||
740 | &unixpath)) && | ||
741 | (0 < strlen (unixpath))) | ||
742 | ret = GNUNET_OK; | ||
743 | else if ((GNUNET_OK == | ||
744 | GNUNET_CONFIGURATION_have_value (cfg, | ||
745 | service_name, | ||
746 | "UNIXPATH"))) | ||
747 | { | ||
748 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, | ||
749 | service_name, | ||
750 | "UNIXPATH", | ||
751 | _ ("not a valid filename")); | ||
752 | GNUNET_free (unixpath); | ||
753 | return GNUNET_SYSERR; /* UNIXPATH specified but invalid! */ | ||
754 | } | ||
755 | GNUNET_free (unixpath); | ||
756 | #endif | ||
757 | |||
758 | if ((GNUNET_YES == | ||
759 | GNUNET_CONFIGURATION_have_value (cfg, | ||
760 | service_name, | ||
761 | "PORT")) && | ||
762 | (GNUNET_OK == | ||
763 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
764 | service_name, | ||
765 | "PORT", | ||
766 | &port)) && | ||
767 | (port <= 65535) && | ||
768 | (0 != port) && | ||
769 | (GNUNET_OK == | ||
770 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
771 | service_name, | ||
772 | "HOSTNAME", | ||
773 | &hostname)) && | ||
774 | (0 != strlen (hostname))) | ||
775 | ret = GNUNET_OK; | ||
776 | GNUNET_free (hostname); | ||
777 | return ret; | ||
778 | } | ||
779 | |||
780 | |||
781 | /** | ||
782 | * Try to connect to the service. | ||
783 | * | ||
784 | * @param cls the `struct ClientState` to try to connect to the service | ||
785 | */ | ||
786 | static void | ||
787 | start_connect (void *cls) | ||
788 | { | ||
789 | struct ClientState *cstate = cls; | ||
790 | |||
791 | cstate->retry_task = NULL; | ||
792 | #if 0 | ||
793 | /* Never use a local source if a proxy is configured */ | ||
794 | if (GNUNET_YES == | ||
795 | GNUNET_SOCKS_check_service (cstate->service_name, | ||
796 | cstate->cfg)) | ||
797 | { | ||
798 | socks_connect (cstate); | ||
799 | return; | ||
800 | } | ||
801 | #endif | ||
802 | |||
803 | if ((0 == (cstate->attempts++ % 2)) || | ||
804 | (0 == cstate->port) || | ||
805 | (NULL == cstate->hostname)) | ||
806 | { | ||
807 | /* on even rounds, try UNIX first, or always | ||
808 | if we do not have a DNS name and TCP port. */ | ||
809 | cstate->sock = try_unixpath (cstate->service_name, | ||
810 | cstate->cfg); | ||
811 | if (NULL != cstate->sock) | ||
812 | { | ||
813 | connect_success_continuation (cstate); | ||
814 | return; | ||
815 | } | ||
816 | } | ||
817 | if ((NULL == cstate->hostname) || | ||
818 | (0 == cstate->port)) | ||
819 | { | ||
820 | /* All options failed. Boo! */ | ||
821 | connect_fail_continuation (cstate); | ||
822 | return; | ||
823 | } | ||
824 | cstate->dns_active | ||
825 | = GNUNET_RESOLVER_ip_get (cstate->hostname, | ||
826 | AF_UNSPEC, | ||
827 | CONNECT_RETRY_TIMEOUT, | ||
828 | &try_connect_using_address, | ||
829 | cstate); | ||
830 | } | ||
831 | |||
832 | |||
833 | /** | ||
834 | * Implements the transmission functionality of a message queue. | ||
835 | * | ||
836 | * @param mq the message queue | ||
837 | * @param msg the message to send | ||
838 | * @param impl_state our `struct ClientState` | ||
839 | */ | ||
840 | static void | ||
841 | connection_client_send_impl (struct GNUNET_MQ_Handle *mq, | ||
842 | const struct GNUNET_MessageHeader *msg, | ||
843 | void *impl_state) | ||
844 | { | ||
845 | struct ClientState *cstate = impl_state; | ||
846 | |||
847 | (void) mq; | ||
848 | /* only one message at a time allowed */ | ||
849 | GNUNET_assert (NULL == cstate->msg); | ||
850 | GNUNET_assert (NULL == cstate->send_task); | ||
851 | cstate->msg = msg; | ||
852 | cstate->msg_off = 0; | ||
853 | if (NULL == cstate->sock) | ||
854 | { | ||
855 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
856 | "message of type %u waiting for socket\n", | ||
857 | ntohs (msg->type)); | ||
858 | return; /* still waiting for connection */ | ||
859 | } | ||
860 | cstate->send_task | ||
861 | = GNUNET_SCHEDULER_add_now (&transmit_ready, | ||
862 | cstate); | ||
863 | } | ||
864 | |||
865 | |||
866 | /** | ||
867 | * Cancel the currently sent message. | ||
868 | * | ||
869 | * @param mq message queue | ||
870 | * @param impl_state our `struct ClientState` | ||
871 | */ | ||
872 | static void | ||
873 | connection_client_cancel_impl (struct GNUNET_MQ_Handle *mq, | ||
874 | void *impl_state) | ||
875 | { | ||
876 | struct ClientState *cstate = impl_state; | ||
877 | |||
878 | (void) mq; | ||
879 | GNUNET_assert (NULL != cstate->msg); | ||
880 | GNUNET_assert (0 == cstate->msg_off); | ||
881 | cstate->msg = NULL; | ||
882 | if (NULL != cstate->send_task) | ||
883 | { | ||
884 | GNUNET_SCHEDULER_cancel (cstate->send_task); | ||
885 | cstate->send_task = NULL; | ||
886 | } | ||
887 | } | ||
888 | |||
889 | |||
890 | /** | ||
891 | * Test if the port or UNIXPATH of the given @a service_name | ||
892 | * is in use and thus (most likely) the respective service is up. | ||
893 | * | ||
894 | * @param cfg our configuration | ||
895 | * @param service_name name of the service to connect to | ||
896 | * @return #GNUNET_YES if the service is (likely) up, | ||
897 | * #GNUNET_NO if the service is (definitively) down, | ||
898 | * #GNUNET_SYSERR if the configuration does not give us | ||
899 | * the necessary information about the service, or if | ||
900 | * we could not check (e.g. socket() failed) | ||
901 | */ | ||
902 | int | ||
903 | GNUNET_CLIENT_test (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
904 | const char *service_name) | ||
905 | { | ||
906 | char *hostname = NULL; | ||
907 | unsigned long long port; | ||
908 | int ret; | ||
909 | |||
910 | #if AF_UNIX | ||
911 | { | ||
912 | char *unixpath = NULL; | ||
913 | |||
914 | if (GNUNET_OK == | ||
915 | GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
916 | service_name, | ||
917 | "UNIXPATH", | ||
918 | &unixpath)) | ||
919 | { | ||
920 | if (0 == strlen (unixpath)) | ||
921 | { | ||
922 | GNUNET_free (unixpath); | ||
923 | return GNUNET_SYSERR; /* empty string not OK */ | ||
924 | } | ||
925 | if (0 == access (unixpath, | ||
926 | F_OK)) | ||
927 | { | ||
928 | GNUNET_free (unixpath); | ||
929 | return GNUNET_OK; /* file exists, we assume service is running */ | ||
930 | } | ||
931 | GNUNET_free (unixpath); | ||
932 | } | ||
933 | else if (GNUNET_OK == | ||
934 | GNUNET_CONFIGURATION_have_value (cfg, | ||
935 | service_name, | ||
936 | "UNIXPATH")) | ||
937 | { | ||
938 | /* UNIXPATH specified but not a valid path! */ | ||
939 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, | ||
940 | service_name, | ||
941 | "UNIXPATH", | ||
942 | _ ("not a valid filename")); | ||
943 | return GNUNET_SYSERR; | ||
944 | } | ||
945 | } | ||
946 | #endif | ||
947 | |||
948 | if ( (GNUNET_OK != | ||
949 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
950 | service_name, | ||
951 | "PORT", | ||
952 | &port)) || | ||
953 | (port > 65535) || | ||
954 | (0 == port) ) | ||
955 | { | ||
956 | return GNUNET_SYSERR; | ||
957 | } | ||
958 | if (GNUNET_OK == | ||
959 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
960 | service_name, | ||
961 | "HOSTNAME", | ||
962 | &hostname)) | ||
963 | { | ||
964 | /* We always assume remotes are up */ | ||
965 | ret = GNUNET_YES; | ||
966 | } | ||
967 | else | ||
968 | { | ||
969 | /* We look for evidence the service is up */ | ||
970 | ret = GNUNET_NO; | ||
971 | } | ||
972 | if ( (NULL == hostname) || | ||
973 | (0 == strcasecmp (hostname, | ||
974 | "localhost")) || | ||
975 | (0 == strcasecmp (hostname, | ||
976 | "ip6-localnet")) ) | ||
977 | { | ||
978 | /* service runs on loopback */ | ||
979 | struct sockaddr_in v4; | ||
980 | struct sockaddr_in6 v6; | ||
981 | int sock; | ||
982 | |||
983 | memset (&v4, 0, sizeof (v4)); | ||
984 | memset (&v6, 0, sizeof (v6)); | ||
985 | v4.sin_family = AF_INET; | ||
986 | v4.sin_port = htons ((uint16_t) port); | ||
987 | #if HAVE_SOCKADDR_IN_SUN_LEN | ||
988 | v4.sin_len = (u_char) sizeof(struct sockaddr_in); | ||
989 | #endif | ||
990 | GNUNET_assert (1 == inet_pton (AF_INET, | ||
991 | "127.0.0.1", | ||
992 | &v4.sin_addr)); | ||
993 | ret = GNUNET_NO; | ||
994 | sock = socket (AF_INET, | ||
995 | SOCK_STREAM, | ||
996 | 0); | ||
997 | if (-1 != sock) | ||
998 | { | ||
999 | if (0 != bind (sock, | ||
1000 | (struct sockaddr *) &v4, | ||
1001 | sizeof (v4))) | ||
1002 | { | ||
1003 | /* bind failed, so someone is listening! */ | ||
1004 | ret = GNUNET_YES; | ||
1005 | } | ||
1006 | (void) close (sock); | ||
1007 | } | ||
1008 | else | ||
1009 | { | ||
1010 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
1011 | "socket"); | ||
1012 | if (GNUNET_NO == ret) | ||
1013 | ret = GNUNET_SYSERR; | ||
1014 | } | ||
1015 | v6.sin6_family = AF_INET6; | ||
1016 | v6.sin6_port = htons ((uint16_t) port); | ||
1017 | #if HAVE_SOCKADDR_IN_SUN_LEN | ||
1018 | v6.sin6_len = (u_char) sizeof(struct sockaddr_in6); | ||
1019 | #endif | ||
1020 | inet_pton (AF_INET6, | ||
1021 | "::1", | ||
1022 | &v6.sin6_addr); | ||
1023 | sock = socket (AF_INET6, | ||
1024 | SOCK_STREAM, | ||
1025 | 0); | ||
1026 | if (-1 != sock) | ||
1027 | { | ||
1028 | if (0 != bind (sock, | ||
1029 | (struct sockaddr *) &v6, | ||
1030 | sizeof (v6))) | ||
1031 | { | ||
1032 | /* bind failed, so someone is listening! */ | ||
1033 | ret = GNUNET_YES; | ||
1034 | } | ||
1035 | (void) close (sock); | ||
1036 | } | ||
1037 | else | ||
1038 | { | ||
1039 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
1040 | "socket"); | ||
1041 | /* not changing 'ret' intentionally here, as | ||
1042 | v4 succeeding and v6 failing just means we | ||
1043 | should use v4 */ | ||
1044 | } | ||
1045 | } | ||
1046 | else | ||
1047 | { | ||
1048 | /* service running remotely */ | ||
1049 | ret = GNUNET_OK; | ||
1050 | } | ||
1051 | GNUNET_free (hostname); | ||
1052 | return ret; | ||
1053 | } | ||
1054 | |||
1055 | |||
1056 | struct GNUNET_MQ_Handle * | ||
1057 | GNUNET_CLIENT_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
1058 | const char *service_name, | ||
1059 | const struct GNUNET_MQ_MessageHandler *handlers, | ||
1060 | GNUNET_MQ_ErrorHandler error_handler, | ||
1061 | void *error_handler_cls) | ||
1062 | { | ||
1063 | struct ClientState *cstate; | ||
1064 | |||
1065 | if (GNUNET_OK != | ||
1066 | test_service_configuration (service_name, | ||
1067 | cfg)) | ||
1068 | return NULL; | ||
1069 | cstate = GNUNET_new (struct ClientState); | ||
1070 | cstate->service_name = GNUNET_strdup (service_name); | ||
1071 | cstate->cfg = cfg; | ||
1072 | cstate->retry_task = GNUNET_SCHEDULER_add_now (&start_connect, | ||
1073 | cstate); | ||
1074 | cstate->mst = GNUNET_MST_create (&recv_message, | ||
1075 | cstate); | ||
1076 | if (GNUNET_YES == | ||
1077 | GNUNET_CONFIGURATION_have_value (cfg, | ||
1078 | service_name, | ||
1079 | "PORT")) | ||
1080 | { | ||
1081 | if (! ((GNUNET_OK != | ||
1082 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
1083 | service_name, | ||
1084 | "PORT", | ||
1085 | &cstate->port)) || | ||
1086 | (cstate->port > 65535) || | ||
1087 | (GNUNET_OK != | ||
1088 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
1089 | service_name, | ||
1090 | "HOSTNAME", | ||
1091 | &cstate->hostname))) && | ||
1092 | (0 == strlen (cstate->hostname))) | ||
1093 | { | ||
1094 | GNUNET_free (cstate->hostname); | ||
1095 | cstate->hostname = NULL; | ||
1096 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1097 | _ ("Need a non-empty hostname for service `%s'.\n"), | ||
1098 | service_name); | ||
1099 | } | ||
1100 | } | ||
1101 | cstate->mq = GNUNET_MQ_queue_for_callbacks (&connection_client_send_impl, | ||
1102 | &connection_client_destroy_impl, | ||
1103 | &connection_client_cancel_impl, | ||
1104 | cstate, | ||
1105 | handlers, | ||
1106 | error_handler, | ||
1107 | error_handler_cls); | ||
1108 | return cstate->mq; | ||
1109 | } | ||
1110 | |||
1111 | |||
1112 | /* end of client.c */ | ||