aboutsummaryrefslogtreecommitdiff
path: root/src/lib/util/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/client.c')
-rw-r--r--src/lib/util/client.c1112
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 */
52struct ClientState;
53
54
55/**
56 * During connect, we try multiple possible IP addresses
57 * to find out which one might work.
58 */
59struct 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 */
101struct 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 */
210static void
211start_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 */
220static void
221connect_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 */
249static void
250transmit_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
271RETRY:
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 */
334static enum GNUNET_GenericReturnValue
335recv_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 */
360static void
361cancel_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 */
385static void
386connection_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 */
439static void
440receive_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 */
482static void
483connect_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 */
510static struct GNUNET_NETWORK_Handle *
511try_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 */
582static void
583connect_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 */
637static void
638try_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 */
725static int
726test_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 */
786static void
787start_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 */
840static void
841connection_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 */
872static void
873connection_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 */
902int
903GNUNET_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
1056struct GNUNET_MQ_Handle *
1057GNUNET_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 */