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