2 This file is part of GNUnet.
3 Copyright (C) 2001-2016 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
23 * @brief code for access to services
24 * @author Christian Grothoff
26 * Generic TCP code for reliable, record-oriented TCP
27 * connections between clients and service providers.
30 #include "gnunet_protocols.h"
31 #include "gnunet_util_lib.h"
32 #include "gnunet_resolver_service.h"
33 #include "gnunet_socks.h"
36 #define LOG(kind, ...) GNUNET_log_from(kind, "util-client", __VA_ARGS__)
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.
44 #define CONNECT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 5)
49 * Internal state for a client connected to a GNUnet service.
55 * During connect, we try multiple possible IP addresses
56 * to find out which one might work.
60 * This is a linked list.
62 struct AddressProbe *next;
65 * This is a doubly-linked list.
67 struct AddressProbe *prev;
70 * The address; do not free (allocated at the end of this struct).
72 const struct sockaddr *addr;
75 * Underlying OS's socket.
77 struct GNUNET_NETWORK_Handle *sock;
80 * Connection for which we are probing.
82 struct ClientState *cstate;
90 * Task waiting for the connection to finish connecting.
92 struct GNUNET_SCHEDULER_Task *task;
97 * Internal state for a client connected to a GNUnet service.
101 * The connection handle, NULL if not live
103 struct GNUNET_NETWORK_Handle *sock;
106 * Handle to a pending DNS lookup request, NULL if DNS is finished.
108 struct GNUNET_RESOLVER_RequestHandle *dns_active;
113 const struct GNUNET_CONFIGURATION_Handle *cfg;
116 * Linked list of sockets we are currently trying out
119 struct AddressProbe *ap_head;
122 * Linked list of sockets we are currently trying out
125 struct AddressProbe *ap_tail;
128 * Name of the service we interact with.
138 * Next message to transmit to the service. NULL for none.
140 const struct GNUNET_MessageHeader *msg;
143 * Task for trying to connect to the service.
145 struct GNUNET_SCHEDULER_Task *retry_task;
148 * Task for sending messages to the service.
150 struct GNUNET_SCHEDULER_Task *send_task;
153 * Task for sending messages to the service.
155 struct GNUNET_SCHEDULER_Task *recv_task;
158 * Tokenizer for inbound messages.
160 struct GNUNET_MessageStreamTokenizer *mst;
163 * Message queue under our control.
165 struct GNUNET_MQ_Handle *mq;
168 * Timeout for receiving a response (absolute time).
170 struct GNUNET_TIME_Absolute receive_timeout;
173 * Current value for our incremental back-off (for
176 struct GNUNET_TIME_Relative back_off;
179 * TCP port (0 for disabled).
181 unsigned long long port;
184 * Offset in the message where we are for transmission.
189 * How often have we tried to connect?
191 unsigned int attempts;
194 * Are we supposed to die? #GNUNET_SYSERR if destruction must be
195 * deferred, #GNUNET_NO by default, #GNUNET_YES if destruction was
203 * Try to connect to the service.
205 * @param cls the `struct ClientState` to try to connect to the service
208 start_connect(void *cls);
212 * We've failed for good to establish a connection (timeout or
213 * no more addresses to try).
215 * @param cstate the connection we tried to establish
218 connect_fail_continuation(struct ClientState *cstate)
220 GNUNET_break(NULL == cstate->ap_head);
221 GNUNET_break(NULL == cstate->ap_tail);
222 GNUNET_break(NULL == cstate->dns_active);
223 GNUNET_break(NULL == cstate->sock);
224 GNUNET_assert(NULL == cstate->send_task);
225 GNUNET_assert(NULL == cstate->recv_task);
226 // GNUNET_assert (NULL == cstate->proxy_handshake);
228 cstate->back_off = GNUNET_TIME_STD_BACKOFF(cstate->back_off);
229 LOG(GNUNET_ERROR_TYPE_DEBUG,
230 "Failed to establish connection to `%s', no further addresses to try, will try again in %s.\n",
231 cstate->service_name,
232 GNUNET_STRINGS_relative_time_to_string(cstate->back_off,
235 = GNUNET_SCHEDULER_add_delayed(cstate->back_off,
242 * We are ready to send a message to the service.
244 * @param cls the `struct ClientState` with the `msg` to transmit
247 transmit_ready(void *cls)
249 struct ClientState *cstate = cls;
253 int notify_in_flight;
255 cstate->send_task = NULL;
256 if (GNUNET_YES == cstate->in_destroy)
258 pos = (const char *)cstate->msg;
259 len = ntohs(cstate->msg->size);
260 GNUNET_assert(cstate->msg_off < len);
261 LOG(GNUNET_ERROR_TYPE_DEBUG,
262 "message of type %u trying to send with socket %p (MQ: %p\n",
263 ntohs(cstate->msg->type),
268 ret = GNUNET_NETWORK_socket_send(cstate->sock,
269 &pos[cstate->msg_off],
270 len - cstate->msg_off);
273 LOG(GNUNET_ERROR_TYPE_WARNING,
274 "Error during sending message of type %u\n",
275 ntohs(cstate->msg->type));
278 LOG(GNUNET_ERROR_TYPE_DEBUG,
279 "Retrying message of type %u\n",
280 ntohs(cstate->msg->type));
283 GNUNET_MQ_inject_error(cstate->mq,
284 GNUNET_MQ_ERROR_WRITE);
287 notify_in_flight = (0 == cstate->msg_off);
288 cstate->msg_off += ret;
289 if (cstate->msg_off < len)
291 LOG(GNUNET_ERROR_TYPE_DEBUG,
292 "rescheduling message of type %u\n",
293 ntohs(cstate->msg->type));
295 = GNUNET_SCHEDULER_add_write_net(GNUNET_TIME_UNIT_FOREVER_REL,
299 if (notify_in_flight)
300 GNUNET_MQ_impl_send_in_flight(cstate->mq);
303 LOG(GNUNET_ERROR_TYPE_DEBUG,
304 "sending message of type %u successful\n",
305 ntohs(cstate->msg->type));
307 GNUNET_MQ_impl_send_continue(cstate->mq);
312 * We have received a full message, pass to the MQ dispatcher.
313 * Called by the tokenizer via #receive_ready().
315 * @param cls the `struct ClientState`
316 * @param msg message we received.
317 * @return #GNUNET_OK on success,
318 * #GNUNET_NO to stop further processing due to disconnect (no error)
319 * #GNUNET_SYSERR to stop further processing due to error
322 recv_message(void *cls,
323 const struct GNUNET_MessageHeader *msg)
325 struct ClientState *cstate = cls;
327 if (GNUNET_YES == cstate->in_destroy)
329 LOG(GNUNET_ERROR_TYPE_DEBUG,
330 "Received message of type %u and size %u from %s\n",
333 cstate->service_name);
334 GNUNET_MQ_inject_message(cstate->mq,
336 if (GNUNET_YES == cstate->in_destroy)
343 * Cancel all remaining connect attempts
345 * @param cstate handle of the client state to process
348 cancel_aps(struct ClientState *cstate)
350 struct AddressProbe *pos;
352 while (NULL != (pos = cstate->ap_head))
354 GNUNET_break(GNUNET_OK ==
355 GNUNET_NETWORK_socket_close(pos->sock));
356 GNUNET_SCHEDULER_cancel(pos->task);
357 GNUNET_CONTAINER_DLL_remove(cstate->ap_head,
366 * Implement the destruction of a message queue. Implementations must
367 * not free @a mq, but should take care of @a impl_state.
369 * @param mq the message queue to destroy
370 * @param impl_state our `struct ClientState`
373 connection_client_destroy_impl(struct GNUNET_MQ_Handle *mq,
376 struct ClientState *cstate = impl_state;
379 if (NULL != cstate->dns_active)
381 GNUNET_RESOLVER_request_cancel(cstate->dns_active);
382 cstate->dns_active = NULL;
384 if (NULL != cstate->send_task)
386 GNUNET_SCHEDULER_cancel(cstate->send_task);
387 cstate->send_task = NULL;
389 if (NULL != cstate->retry_task)
391 GNUNET_SCHEDULER_cancel(cstate->retry_task);
392 cstate->retry_task = NULL;
394 if (GNUNET_SYSERR == cstate->in_destroy)
396 /* defer destruction */
397 cstate->in_destroy = GNUNET_YES;
401 if (NULL != cstate->recv_task)
403 GNUNET_SCHEDULER_cancel(cstate->recv_task);
404 cstate->recv_task = NULL;
406 if (NULL != cstate->sock)
408 LOG(GNUNET_ERROR_TYPE_DEBUG,
409 "destroying socket: %p\n",
411 GNUNET_NETWORK_socket_close(cstate->sock);
414 GNUNET_free(cstate->service_name);
415 GNUNET_free_non_null(cstate->hostname);
416 GNUNET_MST_destroy(cstate->mst);
422 * This function is called once we have data ready to read.
424 * @param cls `struct ClientState` with connection to read from
427 receive_ready(void *cls)
429 struct ClientState *cstate = cls;
432 cstate->recv_task = NULL;
433 cstate->in_destroy = GNUNET_SYSERR;
434 ret = GNUNET_MST_read(cstate->mst,
438 if (GNUNET_SYSERR == ret)
440 if (NULL != cstate->mq)
441 GNUNET_MQ_inject_error(cstate->mq,
442 GNUNET_MQ_ERROR_READ);
443 if (GNUNET_YES == cstate->in_destroy)
444 connection_client_destroy_impl(cstate->mq,
448 if (GNUNET_YES == cstate->in_destroy)
450 connection_client_destroy_impl(cstate->mq,
454 cstate->in_destroy = GNUNET_NO;
456 = GNUNET_SCHEDULER_add_read_net(GNUNET_TIME_UNIT_FOREVER_REL,
464 * We've succeeded in establishing a connection.
466 * @param cstate the connection we tried to establish
469 connect_success_continuation(struct ClientState *cstate)
471 GNUNET_assert(NULL == cstate->recv_task);
473 = GNUNET_SCHEDULER_add_read_net(GNUNET_TIME_UNIT_FOREVER_REL,
477 if (NULL != cstate->msg)
479 GNUNET_assert(NULL == cstate->send_task);
481 = GNUNET_SCHEDULER_add_write_net(GNUNET_TIME_UNIT_FOREVER_REL,
490 * Try connecting to the server using UNIX domain sockets.
492 * @param service_name name of service to connect to
493 * @param cfg configuration to use
494 * @return NULL on error, socket connected to UNIX otherwise
496 static struct GNUNET_NETWORK_Handle *
497 try_unixpath(const char *service_name,
498 const struct GNUNET_CONFIGURATION_Handle *cfg)
501 struct GNUNET_NETWORK_Handle *sock;
503 struct sockaddr_un s_un;
507 GNUNET_CONFIGURATION_get_value_filename(cfg,
511 (0 < strlen(unixpath)))
513 /* We have a non-NULL unixpath, need to validate it */
514 if (strlen(unixpath) >= sizeof(s_un.sun_path))
516 LOG(GNUNET_ERROR_TYPE_WARNING,
517 _("UNIXPATH `%s' too long, maximum length is %llu\n"),
519 (unsigned long long)sizeof(s_un.sun_path));
520 unixpath = GNUNET_NETWORK_shorten_unixpath(unixpath);
521 LOG(GNUNET_ERROR_TYPE_INFO,
522 _("Using `%s' instead\n"),
524 if (NULL == unixpath)
530 s_un.sun_family = AF_UNIX;
531 GNUNET_strlcpy(s_un.sun_path,
533 sizeof(s_un.sun_path));
538 abstract = GNUNET_CONFIGURATION_get_value_yesno(cfg,
540 "USE_ABSTRACT_SOCKETS");
541 if (GNUNET_YES == abstract)
542 s_un.sun_path[0] = '\0';
545 #if HAVE_SOCKADDR_UN_SUN_LEN
546 s_un.sun_len = (u_char)sizeof(struct sockaddr_un);
548 sock = GNUNET_NETWORK_socket_create(AF_UNIX,
551 if ((NULL != sock) &&
553 GNUNET_NETWORK_socket_connect(sock,
554 (struct sockaddr *)&s_un,
556 (EINPROGRESS == errno)))
558 LOG(GNUNET_ERROR_TYPE_DEBUG,
559 "Successfully connected to unixpath `%s'!\n",
561 GNUNET_free(unixpath);
565 GNUNET_NETWORK_socket_close(sock);
567 GNUNET_free_non_null(unixpath);
574 * Scheduler let us know that we're either ready to write on the
575 * socket OR connect timed out. Do the right thing.
577 * @param cls the `struct AddressProbe *` with the address that we are probing
580 connect_probe_continuation(void *cls)
582 struct AddressProbe *ap = cls;
583 struct ClientState *cstate = ap->cstate;
584 const struct GNUNET_SCHEDULER_TaskContext *tc;
589 GNUNET_assert(NULL != ap->sock);
590 GNUNET_CONTAINER_DLL_remove(cstate->ap_head,
595 tc = GNUNET_SCHEDULER_get_task_context();
596 if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) ||
598 GNUNET_NETWORK_socket_getsockopt(ap->sock,
605 GNUNET_break(GNUNET_OK ==
606 GNUNET_NETWORK_socket_close(ap->sock));
608 if ((NULL == cstate->ap_head) &&
609 // (NULL == cstate->proxy_handshake) &&
610 (NULL == cstate->dns_active))
611 connect_fail_continuation(cstate);
614 LOG(GNUNET_ERROR_TYPE_DEBUG,
615 "Connection to `%s' succeeded!\n",
616 cstate->service_name);
617 /* trigger jobs that waited for the connection */
618 GNUNET_assert(NULL == cstate->sock);
619 cstate->sock = ap->sock;
622 connect_success_continuation(cstate);
627 * Try to establish a connection given the specified address.
628 * This function is called by the resolver once we have a DNS reply.
630 * @param cls our `struct ClientState *`
631 * @param addr address to try, NULL for "last call"
632 * @param addrlen length of @a addr
635 try_connect_using_address(void *cls,
636 const struct sockaddr *addr,
639 struct ClientState *cstate = cls;
640 struct AddressProbe *ap;
644 cstate->dns_active = NULL;
645 if ((NULL == cstate->ap_head) &&
646 // (NULL == cstate->proxy_handshake) &&
647 (NULL == cstate->sock))
648 connect_fail_continuation(cstate);
651 if (NULL != cstate->sock)
652 return; /* already connected */
654 LOG(GNUNET_ERROR_TYPE_DEBUG,
655 "Trying to connect using address `%s:%u'\n",
659 ap = GNUNET_malloc(sizeof(struct AddressProbe) + addrlen);
660 ap->addr = (const struct sockaddr *)&ap[1];
661 GNUNET_memcpy(&ap[1],
664 ap->addrlen = addrlen;
667 switch (ap->addr->sa_family)
670 ((struct sockaddr_in *)ap->addr)->sin_port = htons(cstate->port);
674 ((struct sockaddr_in6 *)ap->addr)->sin6_port = htons(cstate->port);
680 return; /* not supported by us */
682 ap->sock = GNUNET_NETWORK_socket_create(ap->addr->sa_family,
685 if (NULL == ap->sock)
688 return; /* not supported by OS */
691 GNUNET_NETWORK_socket_connect(ap->sock,
694 (EINPROGRESS != errno))
696 /* maybe refused / unsupported address, try next */
697 GNUNET_log_strerror(GNUNET_ERROR_TYPE_INFO,
699 GNUNET_break(GNUNET_OK ==
700 GNUNET_NETWORK_socket_close(ap->sock));
704 GNUNET_CONTAINER_DLL_insert(cstate->ap_head,
707 ap->task = GNUNET_SCHEDULER_add_write_net(CONNECT_RETRY_TIMEOUT,
709 &connect_probe_continuation,
715 * Test whether the configuration has proper values for connection
716 * (UNIXPATH || (PORT && HOSTNAME)).
718 * @param service_name name of service to connect to
719 * @param cfg configuration to use
720 * @return #GNUNET_OK if the configuration is valid, #GNUNET_SYSERR if not
723 test_service_configuration(const char *service_name,
724 const struct GNUNET_CONFIGURATION_Handle *cfg)
726 int ret = GNUNET_SYSERR;
727 char *hostname = NULL;
728 unsigned long long port;
731 char *unixpath = NULL;
734 GNUNET_CONFIGURATION_get_value_filename(cfg,
738 (0 < strlen(unixpath)))
740 else if ((GNUNET_OK ==
741 GNUNET_CONFIGURATION_have_value(cfg,
745 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR,
748 _("not a valid filename"));
749 return GNUNET_SYSERR; /* UNIXPATH specified but invalid! */
751 GNUNET_free_non_null(unixpath);
755 GNUNET_CONFIGURATION_have_value(cfg,
759 GNUNET_CONFIGURATION_get_value_number(cfg,
766 GNUNET_CONFIGURATION_get_value_string(cfg,
770 (0 != strlen(hostname)))
772 GNUNET_free_non_null(hostname);
778 * Try to connect to the service.
780 * @param cls the `struct ClientState` to try to connect to the service
783 start_connect(void *cls)
785 struct ClientState *cstate = cls;
787 cstate->retry_task = NULL;
789 /* Never use a local source if a proxy is configured */
791 GNUNET_SOCKS_check_service(cstate->service_name,
794 socks_connect(cstate);
799 if ((0 == (cstate->attempts++ % 2)) ||
800 (0 == cstate->port) ||
801 (NULL == cstate->hostname))
803 /* on even rounds, try UNIX first, or always
804 if we do not have a DNS name and TCP port. */
805 cstate->sock = try_unixpath(cstate->service_name,
807 if (NULL != cstate->sock)
809 connect_success_continuation(cstate);
813 if ((NULL == cstate->hostname) ||
816 /* All options failed. Boo! */
817 connect_fail_continuation(cstate);
821 = GNUNET_RESOLVER_ip_get(cstate->hostname,
823 CONNECT_RETRY_TIMEOUT,
824 &try_connect_using_address,
830 * Implements the transmission functionality of a message queue.
832 * @param mq the message queue
833 * @param msg the message to send
834 * @param impl_state our `struct ClientState`
837 connection_client_send_impl(struct GNUNET_MQ_Handle *mq,
838 const struct GNUNET_MessageHeader *msg,
841 struct ClientState *cstate = impl_state;
844 /* only one message at a time allowed */
845 GNUNET_assert(NULL == cstate->msg);
846 GNUNET_assert(NULL == cstate->send_task);
849 if (NULL == cstate->sock)
851 LOG(GNUNET_ERROR_TYPE_DEBUG,
852 "message of type %u waiting for socket\n",
854 return; /* still waiting for connection */
857 = GNUNET_SCHEDULER_add_write_net(GNUNET_TIME_UNIT_FOREVER_REL,
865 * Cancel the currently sent message.
867 * @param mq message queue
868 * @param impl_state our `struct ClientState`
871 connection_client_cancel_impl(struct GNUNET_MQ_Handle *mq,
874 struct ClientState *cstate = impl_state;
877 GNUNET_assert(NULL != cstate->msg);
878 GNUNET_assert(0 == cstate->msg_off);
880 if (NULL != cstate->send_task)
882 GNUNET_SCHEDULER_cancel(cstate->send_task);
883 cstate->send_task = NULL;
889 * Create a message queue to connect to a GNUnet service.
890 * If handlers are specfied, receive messages from the connection.
892 * @param cfg our configuration
893 * @param service_name name of the service to connect to
894 * @param handlers handlers for receiving messages, can be NULL
895 * @param error_handler error handler
896 * @param error_handler_cls closure for the @a error_handler
897 * @return the message queue, NULL on error
899 struct GNUNET_MQ_Handle *
900 GNUNET_CLIENT_connect(const struct GNUNET_CONFIGURATION_Handle *cfg,
901 const char *service_name,
902 const struct GNUNET_MQ_MessageHandler *handlers,
903 GNUNET_MQ_ErrorHandler error_handler,
904 void *error_handler_cls)
906 struct ClientState *cstate;
909 test_service_configuration(service_name,
912 cstate = GNUNET_new(struct ClientState);
913 cstate->service_name = GNUNET_strdup(service_name);
915 cstate->retry_task = GNUNET_SCHEDULER_add_now(&start_connect,
917 cstate->mst = GNUNET_MST_create(&recv_message,
920 GNUNET_CONFIGURATION_have_value(cfg,
925 GNUNET_CONFIGURATION_get_value_number(cfg,
929 (cstate->port > 65535) ||
931 GNUNET_CONFIGURATION_get_value_string(cfg,
934 &cstate->hostname))) &&
935 (0 == strlen(cstate->hostname)))
937 GNUNET_free(cstate->hostname);
938 cstate->hostname = NULL;
939 LOG(GNUNET_ERROR_TYPE_WARNING,
940 _("Need a non-empty hostname for service `%s'.\n"),
944 cstate->mq = GNUNET_MQ_queue_for_callbacks(&connection_client_send_impl,
945 &connection_client_destroy_impl,
946 &connection_client_cancel_impl,
954 /* end of client.c */