/* This file is part of GNUnet. Copyright (C) 2009-2013 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @file util/connection.c * @brief TCP connection management * @author Christian Grothoff * * This code is rather complex. Only modify it if you * 1) Have a NEW testcase showing that the new code * is needed and correct * 2) All EXISTING testcases pass with the new code * These rules should apply in general, but for this * module they are VERY, VERY important. */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_resolver_service.h" /** * Timeout we use on TCP connect before trying another * result from the DNS resolver. Actual value used * is this value divided by the number of address families. * Default is 5s. */ #define CONNECT_RETRY_TIMEOUT \ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) #define LOG_STRERROR(kind, syscall) \ GNUNET_log_from_strerror (kind, "util-connection", syscall) /** * Transmission handle. There can only be one for each connection. */ struct GNUNET_CONNECTION_TransmitHandle { /** * Function to call if the send buffer has notify_size * bytes available. */ GNUNET_CONNECTION_TransmitReadyNotify notify_ready; /** * Closure for notify_ready. */ void *notify_ready_cls; /** * Our connection handle. */ struct GNUNET_CONNECTION_Handle *connection; /** * Timeout for receiving (in absolute time). */ struct GNUNET_TIME_Absolute transmit_timeout; /** * Task called on timeout. */ struct GNUNET_SCHEDULER_Task *timeout_task; /** * At what number of bytes available in the * write buffer should the notify method be called? */ size_t notify_size; }; /** * During connect, we try multiple possible IP addresses * to find out which one might work. */ struct AddressProbe { /** * This is a linked list. */ struct AddressProbe *next; /** * This is a doubly-linked list. */ struct AddressProbe *prev; /** * The address; do not free (allocated at the end of this struct). */ const struct sockaddr *addr; /** * Underlying OS's socket. */ struct GNUNET_NETWORK_Handle *sock; /** * Connection for which we are probing. */ struct GNUNET_CONNECTION_Handle *connection; /** * Lenth of addr. */ socklen_t addrlen; /** * Task waiting for the connection to finish connecting. */ struct GNUNET_SCHEDULER_Task *task; }; /** * @brief handle for a network connection */ struct GNUNET_CONNECTION_Handle { /** * Configuration to use. */ const struct GNUNET_CONFIGURATION_Handle *cfg; /** * Linked list of sockets we are currently trying out * (during connect). */ struct AddressProbe *ap_head; /** * Linked list of sockets we are currently trying out * (during connect). */ struct AddressProbe *ap_tail; /** * Network address of the other end-point, may be NULL. */ struct sockaddr *addr; /** * Pointer to the hostname if connection was * created using DNS lookup, otherwise NULL. */ char *hostname; /** * Underlying OS's socket, set to NULL after fatal errors. */ struct GNUNET_NETWORK_Handle *sock; /** * Function to call on data received, NULL if no receive is pending. */ GNUNET_CONNECTION_Receiver receiver; /** * Closure for @e receiver. */ void *receiver_cls; /** * Pointer to our write buffer. */ char *write_buffer; /** * Current size of our @e write_buffer. */ size_t write_buffer_size; /** * Current write-offset in @e write_buffer (where * would we write next). */ size_t write_buffer_off; /** * Current read-offset in @e write_buffer (how many * bytes have already been sent). */ size_t write_buffer_pos; /** * Length of @e addr. */ socklen_t addrlen; /** * Read task that we may need to wait for. */ struct GNUNET_SCHEDULER_Task *read_task; /** * Write task that we may need to wait for. */ struct GNUNET_SCHEDULER_Task *write_task; /** * Handle to a pending DNS lookup request. */ struct GNUNET_RESOLVER_RequestHandle *dns_active; /** * The handle we return for #GNUNET_CONNECTION_notify_transmit_ready(). */ struct GNUNET_CONNECTION_TransmitHandle nth; /** * Timeout for receiving (in absolute time). */ struct GNUNET_TIME_Absolute receive_timeout; /** * Maximum number of bytes to read (for receiving). */ size_t max; /** * Port to connect to. */ uint16_t port; /** * When shutdown, do not ever actually close the socket, but * free resources. Only should ever be set if using program * termination as a signal (because only then will the leaked * socket be freed!) */ int8_t persist; /** * Usually 0. Set to 1 if this handle is in use, and should * #GNUNET_CONNECTION_destroy() be called right now, the action needs * to be deferred by setting it to -1. */ int8_t destroy_later; /** * Handle to subsequent connection after proxy handshake completes, */ struct GNUNET_CONNECTION_Handle *proxy_handshake; }; /** * Set the persist option on this connection handle. Indicates * that the underlying socket or fd should never really be closed. * Used for indicating process death. * * @param connection the connection to set persistent */ void GNUNET_CONNECTION_persist_ (struct GNUNET_CONNECTION_Handle *connection) { connection->persist = GNUNET_YES; } /** * Disable the "CORK" feature for communication with the given connection, * forcing the OS to immediately flush the buffer on transmission * instead of potentially buffering multiple messages. Essentially * reduces the OS send buffers to zero. * Used to make sure that the last messages sent through the connection * reach the other side before the process is terminated. * * @param connection the connection to make flushing and blocking * @return #GNUNET_OK on success */ int GNUNET_CONNECTION_disable_corking (struct GNUNET_CONNECTION_Handle *connection) { return GNUNET_NETWORK_socket_disable_corking (connection->sock); } /** * Create a connection handle by boxing an existing OS socket. The OS * socket should henceforth be no longer used directly. * #GNUNET_connection_destroy() will close it. * * @param osSocket existing socket to box * @return the boxed connection handle */ struct GNUNET_CONNECTION_Handle * GNUNET_CONNECTION_create_from_existing (struct GNUNET_NETWORK_Handle *osSocket) { struct GNUNET_CONNECTION_Handle *connection; connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); connection->write_buffer_size = GNUNET_MIN_MESSAGE_SIZE; connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); connection->sock = osSocket; return connection; } /** * Create a connection handle by accepting on a listen socket. This * function may block if the listen socket has no connection ready. * * @param access_cb function to use to check if access is allowed * @param access_cb_cls closure for @a access_cb * @param lsock listen socket * @return the connection handle, NULL on error */ struct GNUNET_CONNECTION_Handle * GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access_cb, void *access_cb_cls, struct GNUNET_NETWORK_Handle *lsock) { struct GNUNET_CONNECTION_Handle *connection; char addr[128]; socklen_t addrlen; struct GNUNET_NETWORK_Handle *sock; int aret; struct sockaddr_in *v4; struct sockaddr_in6 *v6; struct sockaddr *sa; void *uaddr; #ifdef SO_PEERCRED struct ucred uc; socklen_t olen; #endif struct GNUNET_CONNECTION_Credentials *gcp; #if HAVE_GETPEEREID || defined(SO_PEERCRED) || HAVE_GETPEERUCRED struct GNUNET_CONNECTION_Credentials gc; gc.uid = 0; gc.gid = 0; #endif addrlen = sizeof(addr); sock = GNUNET_NETWORK_socket_accept (lsock, (struct sockaddr *) &addr, &addrlen); if (NULL == sock) { if (EAGAIN != errno) LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "accept"); return NULL; } if ((addrlen > sizeof(addr)) || (addrlen < sizeof(sa_family_t))) { GNUNET_break (0); GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); return NULL; } sa = (struct sockaddr *) addr; v6 = (struct sockaddr_in6 *) addr; if ((AF_INET6 == sa->sa_family) && (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr))) { /* convert to V4 address */ v4 = GNUNET_new (struct sockaddr_in); memset (v4, 0, sizeof(struct sockaddr_in)); v4->sin_family = AF_INET; #if HAVE_SOCKADDR_IN_SIN_LEN v4->sin_len = (u_char) sizeof(struct sockaddr_in); #endif GNUNET_memcpy (&v4->sin_addr, &((char *) &v6->sin6_addr)[sizeof(struct in6_addr) - sizeof(struct in_addr)], sizeof(struct in_addr)); v4->sin_port = v6->sin6_port; uaddr = v4; addrlen = sizeof(struct sockaddr_in); } else { uaddr = GNUNET_malloc (addrlen); GNUNET_memcpy (uaddr, addr, addrlen); } gcp = NULL; if (AF_UNIX == sa->sa_family) { #if HAVE_GETPEEREID /* most BSDs */ if (0 == getpeereid (GNUNET_NETWORK_get_fd (sock), &gc.uid, &gc.gid)) gcp = &gc; #else #ifdef SO_PEERCRED /* largely traditional GNU/Linux */ olen = sizeof(uc); if ((0 == getsockopt (GNUNET_NETWORK_get_fd (sock), SOL_SOCKET, SO_PEERCRED, &uc, &olen)) && (olen == sizeof(uc))) { gc.uid = uc.uid; gc.gid = uc.gid; gcp = &gc; } #else #if HAVE_GETPEERUCRED /* this is for Solaris 10 */ ucred_t *uc; uc = NULL; if (0 == getpeerucred (GNUNET_NETWORK_get_fd (sock), &uc)) { gc.uid = ucred_geteuid (uc); gc.gid = ucred_getegid (uc); gcp = &gc; } ucred_free (uc); #endif #endif #endif } if ((NULL != access_cb) && (GNUNET_YES != (aret = access_cb (access_cb_cls, gcp, uaddr, addrlen)))) { if (GNUNET_NO == aret) LOG (GNUNET_ERROR_TYPE_INFO, _ ("Access denied to `%s'\n"), GNUNET_a2s (uaddr, addrlen)); GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_shutdown (sock, SHUT_RDWR)); GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); GNUNET_free (uaddr); return NULL; } connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); connection->write_buffer_size = GNUNET_MIN_MESSAGE_SIZE; connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); connection->addr = uaddr; connection->addrlen = addrlen; connection->sock = sock; LOG (GNUNET_ERROR_TYPE_INFO, _ ("Accepting connection from `%s': %p\n"), GNUNET_a2s (uaddr, addrlen), connection); return connection; } /** * Obtain the network address of the other party. * * @param connection the client to get the address for * @param addr where to store the address * @param addrlen where to store the length of the @a addr * @return #GNUNET_OK on success */ int GNUNET_CONNECTION_get_address (struct GNUNET_CONNECTION_Handle *connection, void **addr, size_t *addrlen) { if ((NULL == connection->addr) || (0 == connection->addrlen)) return GNUNET_NO; *addr = GNUNET_malloc (connection->addrlen); GNUNET_memcpy (*addr, connection->addr, connection->addrlen); *addrlen = connection->addrlen; return GNUNET_OK; } /** * Tell the receiver callback that we had an IO error. * * @param connection connection to signal error * @param errcode error code to send */ static void signal_receive_error (struct GNUNET_CONNECTION_Handle *connection, int errcode) { GNUNET_CONNECTION_Receiver receiver; LOG (GNUNET_ERROR_TYPE_DEBUG, "Receive encounters error (%s), connection closed (%p)\n", strerror (errcode), connection); GNUNET_assert (NULL != (receiver = connection->receiver)); connection->receiver = NULL; receiver (connection->receiver_cls, NULL, 0, connection->addr, connection->addrlen, errcode); } /** * Tell the receiver callback that a timeout was reached. * * @param connection connection to signal for */ static void signal_receive_timeout (struct GNUNET_CONNECTION_Handle *connection) { GNUNET_CONNECTION_Receiver receiver; LOG (GNUNET_ERROR_TYPE_DEBUG, "Connection signals timeout to receiver (%p)!\n", connection); GNUNET_assert (NULL != (receiver = connection->receiver)); connection->receiver = NULL; receiver (connection->receiver_cls, NULL, 0, NULL, 0, 0); } /** * We failed to transmit data to the service, signal the error. * * @param connection handle that had trouble * @param ecode error code (errno) */ static void signal_transmit_error (struct GNUNET_CONNECTION_Handle *connection, int ecode) { GNUNET_CONNECTION_TransmitReadyNotify notify; LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmission encounterd error (%s), connection closed (%p)\n", strerror (ecode), connection); if (NULL != connection->sock) { (void) GNUNET_NETWORK_socket_shutdown (connection->sock, SHUT_RDWR); GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (connection->sock)); connection->sock = NULL; GNUNET_assert (NULL == connection->write_task); } if (NULL != connection->read_task) { /* send errors trigger read errors... */ GNUNET_SCHEDULER_cancel (connection->read_task); connection->read_task = NULL; signal_receive_timeout (connection); return; } if (NULL == connection->nth.notify_ready) return; /* nobody to tell about it */ notify = connection->nth.notify_ready; connection->nth.notify_ready = NULL; notify (connection->nth.notify_ready_cls, 0, NULL); } /** * We've failed for good to establish a connection (timeout or * no more addresses to try). * * @param connection the connection we tried to establish */ static void connect_fail_continuation (struct GNUNET_CONNECTION_Handle *connection) { LOG (GNUNET_ERROR_TYPE_INFO, "Failed to establish TCP connection to `%s:%u', no further addresses to try.\n", connection->hostname, connection->port); GNUNET_break (NULL == connection->ap_head); GNUNET_break (NULL == connection->ap_tail); GNUNET_break (GNUNET_NO == connection->dns_active); GNUNET_break (NULL == connection->sock); GNUNET_assert (NULL == connection->write_task); GNUNET_assert (NULL == connection->proxy_handshake); /* signal errors for jobs that used to wait on the connection */ connection->destroy_later = 1; if (NULL != connection->receiver) signal_receive_error (connection, ECONNREFUSED); if (NULL != connection->nth.notify_ready) { GNUNET_assert (NULL != connection->nth.timeout_task); GNUNET_SCHEDULER_cancel (connection->nth.timeout_task); connection->nth.timeout_task = NULL; signal_transmit_error (connection, ECONNREFUSED); } if (-1 == connection->destroy_later) { /* do it now */ connection->destroy_later = 0; GNUNET_CONNECTION_destroy (connection); return; } connection->destroy_later = 0; } /** * We are ready to transmit (or got a timeout). * * @param cls our connection handle */ static void transmit_ready (void *cls); /** * This function is called once we either timeout or have data ready * to read. * * @param cls connection to read from */ static void receive_ready (void *cls); /** * We've succeeded in establishing a connection. * * @param connection the connection we tried to establish */ static void connect_success_continuation (struct GNUNET_CONNECTION_Handle *connection) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Connection to `%s' succeeded! (%p)\n", GNUNET_a2s (connection->addr, connection->addrlen), connection); /* trigger jobs that waited for the connection */ if (NULL != connection->receiver) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Connection succeeded, starting with receiving data (%p)\n", connection); GNUNET_assert (NULL == connection->read_task); connection->read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining ( connection->receive_timeout), connection->sock, &receive_ready, connection); } if (NULL != connection->nth.notify_ready) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Connection succeeded, starting with sending data (%p)\n", connection); GNUNET_assert (connection->nth.timeout_task != NULL); GNUNET_SCHEDULER_cancel (connection->nth.timeout_task); connection->nth.timeout_task = NULL; GNUNET_assert (connection->write_task == NULL); connection->write_task = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining ( connection->nth.transmit_timeout), connection->sock, &transmit_ready, connection); } } /** * Scheduler let us know that we're either ready to write on the * socket OR connect timed out. Do the right thing. * * @param cls the `struct AddressProbe *` with the address that we are probing */ static void connect_probe_continuation (void *cls) { struct AddressProbe *ap = cls; struct GNUNET_CONNECTION_Handle *connection = ap->connection; const struct GNUNET_SCHEDULER_TaskContext *tc; struct AddressProbe *pos; int error; socklen_t len; GNUNET_assert (NULL != ap->sock); GNUNET_CONTAINER_DLL_remove (connection->ap_head, connection->ap_tail, ap); len = sizeof(error); errno = 0; error = 0; tc = GNUNET_SCHEDULER_get_task_context (); if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) || (GNUNET_OK != GNUNET_NETWORK_socket_getsockopt (ap->sock, SOL_SOCKET, SO_ERROR, &error, &len)) || (0 != error)) { GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock)); GNUNET_free (ap); if ((NULL == connection->ap_head) && (GNUNET_NO == connection->dns_active) && (NULL == connection->proxy_handshake)) connect_fail_continuation (connection); return; } GNUNET_assert (NULL == connection->sock); connection->sock = ap->sock; GNUNET_assert (NULL == connection->addr); connection->addr = GNUNET_malloc (ap->addrlen); GNUNET_memcpy (connection->addr, ap->addr, ap->addrlen); connection->addrlen = ap->addrlen; GNUNET_free (ap); /* cancel all other attempts */ while (NULL != (pos = connection->ap_head)) { GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock)); GNUNET_SCHEDULER_cancel (pos->task); GNUNET_CONTAINER_DLL_remove (connection->ap_head, connection->ap_tail, pos); GNUNET_free (pos); } connect_success_continuation (connection); } /** * Try to establish a connection given the specified address. * This function is called by the resolver once we have a DNS reply. * * @param cls our `struct GNUNET_CONNECTION_Handle *` * @param addr address to try, NULL for "last call" * @param addrlen length of @a addr */ static void try_connect_using_address (void *cls, const struct sockaddr *addr, socklen_t addrlen) { struct GNUNET_CONNECTION_Handle *connection = cls; struct AddressProbe *ap; struct GNUNET_TIME_Relative delay; if (NULL == addr) { connection->dns_active = NULL; if ((NULL == connection->ap_head) && (NULL == connection->sock) && (NULL == connection->proxy_handshake)) connect_fail_continuation (connection); return; } if (NULL != connection->sock) return; /* already connected */ GNUNET_assert (NULL == connection->addr); /* try to connect */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect using address `%s:%u/%s:%u'\n", connection->hostname, connection->port, GNUNET_a2s (addr, addrlen), connection->port); ap = GNUNET_malloc (sizeof(struct AddressProbe) + addrlen); ap->addr = (const struct sockaddr *) &ap[1]; GNUNET_memcpy (&ap[1], addr, addrlen); ap->addrlen = addrlen; ap->connection = connection; switch (ap->addr->sa_family) { case AF_INET: ((struct sockaddr_in *) ap->addr)->sin_port = htons (connection->port); break; case AF_INET6: ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (connection->port); break; default: GNUNET_break (0); GNUNET_free (ap); return; /* not supported by us */ } ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family, SOCK_STREAM, 0); if (NULL == ap->sock) { GNUNET_free (ap); return; /* not supported by OS */ } LOG (GNUNET_ERROR_TYPE_INFO, "Trying to connect to `%s' (%p)\n", GNUNET_a2s (ap->addr, ap->addrlen), connection); if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (ap->sock, ap->addr, ap->addrlen)) && (EINPROGRESS != errno)) { /* maybe refused / unsupported address, try next */ LOG_STRERROR (GNUNET_ERROR_TYPE_INFO, "connect"); GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock)); GNUNET_free (ap); return; } GNUNET_CONTAINER_DLL_insert (connection->ap_head, connection->ap_tail, ap); delay = CONNECT_RETRY_TIMEOUT; if (NULL != connection->nth.notify_ready) delay = GNUNET_TIME_relative_min (delay, GNUNET_TIME_absolute_get_remaining ( connection->nth.transmit_timeout)); if (NULL != connection->receiver) delay = GNUNET_TIME_relative_min (delay, GNUNET_TIME_absolute_get_remaining ( connection->receive_timeout)); ap->task = GNUNET_SCHEDULER_add_write_net (delay, ap->sock, &connect_probe_continuation, ap); } /** * Create a connection handle by (asynchronously) connecting to a host. * This function returns immediately, even if the connection has not * yet been established. This function only creates TCP connections. * * @param cfg configuration to use * @param hostname name of the host to connect to * @param port port to connect to * @return the connection handle */ struct GNUNET_CONNECTION_Handle * GNUNET_CONNECTION_create_from_connect ( const struct GNUNET_CONFIGURATION_Handle *cfg, const char *hostname, uint16_t port) { struct GNUNET_CONNECTION_Handle *connection; GNUNET_assert (0 < strlen (hostname)); /* sanity check */ connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); connection->cfg = cfg; connection->write_buffer_size = GNUNET_MIN_MESSAGE_SIZE; connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); connection->port = port; connection->hostname = GNUNET_strdup (hostname); connection->dns_active = GNUNET_RESOLVER_ip_get (connection->hostname, AF_UNSPEC, CONNECT_RETRY_TIMEOUT, &try_connect_using_address, connection); return connection; } /** * Create a connection handle by connecting to a UNIX domain service. * This function returns immediately, even if the connection has not * yet been established. This function only creates UNIX connections. * * @param cfg configuration to use * @param unixpath path to connect to * @return the connection handle, NULL on systems without UNIX support */ struct GNUNET_CONNECTION_Handle * GNUNET_CONNECTION_create_from_connect_to_unixpath ( const struct GNUNET_CONFIGURATION_Handle *cfg, const char *unixpath) { #ifdef AF_UNIX struct GNUNET_CONNECTION_Handle *connection; struct sockaddr_un *un; GNUNET_assert (0 < strlen (unixpath)); /* sanity check */ un = GNUNET_new (struct sockaddr_un); un->sun_family = AF_UNIX; GNUNET_strlcpy (un->sun_path, unixpath, sizeof(un->sun_path)); #ifdef __linux__ { int abstract; abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg, "TESTING", "USE_ABSTRACT_SOCKETS"); if (GNUNET_YES == abstract) un->sun_path[0] = '\0'; } #endif #if HAVE_SOCKADDR_UN_SUN_LEN un->sun_len = (u_char) sizeof(struct sockaddr_un); #endif connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); connection->cfg = cfg; connection->write_buffer_size = GNUNET_MIN_MESSAGE_SIZE; connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); connection->port = 0; connection->hostname = NULL; connection->addr = (struct sockaddr *) un; connection->addrlen = sizeof(struct sockaddr_un); connection->sock = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); if (NULL == connection->sock) { GNUNET_free (connection->addr); GNUNET_free (connection->write_buffer); GNUNET_free (connection); return NULL; } if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (connection->sock, connection->addr, connection->addrlen)) && (EINPROGRESS != errno)) { /* Just return; we expect everything to work eventually so don't fail HARD */ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (connection->sock)); connection->sock = NULL; return connection; } connect_success_continuation (connection); return connection; #else return NULL; #endif } /** * Create a connection handle by (asynchronously) connecting to a host. * This function returns immediately, even if the connection has not * yet been established. This function only creates TCP connections. * * @param s socket to connect * @param serv_addr server address * @param addrlen length of @a serv_addr * @return the connection handle */ struct GNUNET_CONNECTION_Handle * GNUNET_CONNECTION_connect_socket (struct GNUNET_NETWORK_Handle *s, const struct sockaddr *serv_addr, socklen_t addrlen) { struct GNUNET_CONNECTION_Handle *connection; if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (s, serv_addr, addrlen)) && (EINPROGRESS != errno)) { /* maybe refused / unsupported address, try next */ LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG, "connect"); LOG (GNUNET_ERROR_TYPE_DEBUG, "Attempt to connect to `%s' failed\n", GNUNET_a2s (serv_addr, addrlen)); GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (s)); return NULL; } connection = GNUNET_CONNECTION_create_from_existing (s); connection->addr = GNUNET_malloc (addrlen); GNUNET_memcpy (connection->addr, serv_addr, addrlen); connection->addrlen = addrlen; LOG (GNUNET_ERROR_TYPE_INFO, "Trying to connect to `%s' (%p)\n", GNUNET_a2s (serv_addr, addrlen), connection); return connection; } /** * Create a connection handle by creating a socket and * (asynchronously) connecting to a host. This function returns * immediately, even if the connection has not yet been established. * This function only creates TCP connections. * * @param af_family address family to use * @param serv_addr server address * @param addrlen length of @a serv_addr * @return the connection handle */ struct GNUNET_CONNECTION_Handle * GNUNET_CONNECTION_create_from_sockaddr (int af_family, const struct sockaddr *serv_addr, socklen_t addrlen) { struct GNUNET_NETWORK_Handle *s; s = GNUNET_NETWORK_socket_create (af_family, SOCK_STREAM, 0); if (NULL == s) { LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "socket"); return NULL; } return GNUNET_CONNECTION_connect_socket (s, serv_addr, addrlen); } /** * Check if connection is valid (no fatal errors have happened so far). * Note that a connection that is still trying to connect is considered * valid. * * @param connection connection to check * @return #GNUNET_YES if valid, #GNUNET_NO otherwise */ int GNUNET_CONNECTION_check (struct GNUNET_CONNECTION_Handle *connection) { if ((NULL != connection->ap_head) || (NULL != connection->dns_active) || (NULL != connection->proxy_handshake)) return GNUNET_YES; /* still trying to connect */ if ((0 != connection->destroy_later) || (NULL == connection->sock)) return GNUNET_NO; return GNUNET_YES; } /** * Close the connection and free associated resources. There must * not be any pending requests for reading or writing to the * connection at this time. * * @param connection connection to destroy */ void GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *connection) { struct AddressProbe *pos; if (0 != connection->destroy_later) { connection->destroy_later = -1; return; } LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down connection (%p)\n", connection); GNUNET_assert (NULL == connection->nth.notify_ready); GNUNET_assert (NULL == connection->receiver); if (NULL != connection->write_task) { GNUNET_SCHEDULER_cancel (connection->write_task); connection->write_task = NULL; connection->write_buffer_off = 0; } if (NULL != connection->read_task) { GNUNET_SCHEDULER_cancel (connection->read_task); connection->read_task = NULL; } if (NULL != connection->nth.timeout_task) { GNUNET_SCHEDULER_cancel (connection->nth.timeout_task); connection->nth.timeout_task = NULL; } connection->nth.notify_ready = NULL; if (NULL != connection->dns_active) { GNUNET_RESOLVER_request_cancel (connection->dns_active); connection->dns_active = NULL; } if (NULL != connection->proxy_handshake) { /* GNUNET_CONNECTION_destroy (connection->proxy_handshake); */ connection->proxy_handshake->destroy_later = -1; connection->proxy_handshake = NULL; /* Not leaked ??? */ } while (NULL != (pos = connection->ap_head)) { GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock)); GNUNET_SCHEDULER_cancel (pos->task); GNUNET_CONTAINER_DLL_remove (connection->ap_head, connection->ap_tail, pos); GNUNET_free (pos); } if ((NULL != connection->sock) && (GNUNET_YES != connection->persist)) { if ((GNUNET_OK != GNUNET_NETWORK_socket_shutdown (connection->sock, SHUT_RDWR)) && (ENOTCONN != errno) && (ECONNRESET != errno)) LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "shutdown"); } if (NULL != connection->sock) { if (GNUNET_YES != connection->persist) { GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (connection->sock)); } else { GNUNET_NETWORK_socket_free_memory_only_ ( connection->sock); /* at least no memory leak (we deliberately * leak the socket in this special case) ... */ } } GNUNET_free (connection->addr); GNUNET_free (connection->hostname); GNUNET_free (connection->write_buffer); GNUNET_free (connection); } /** * This function is called once we either timeout * or have data ready to read. * * @param cls connection to read from */ static void receive_ready (void *cls) { struct GNUNET_CONNECTION_Handle *connection = cls; const struct GNUNET_SCHEDULER_TaskContext *tc; char buffer[connection->max]; ssize_t ret; GNUNET_CONNECTION_Receiver receiver; connection->read_task = NULL; tc = GNUNET_SCHEDULER_get_task_context (); if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Receive from `%s' encounters error: timeout (%s, %p)\n", GNUNET_a2s (connection->addr, connection->addrlen), GNUNET_STRINGS_relative_time_to_string ( GNUNET_TIME_absolute_get_duration ( connection->receive_timeout), GNUNET_YES), connection); signal_receive_timeout (connection); return; } if (NULL == connection->sock) { /* connect failed for good */ signal_receive_error (connection, ECONNREFUSED); return; } GNUNET_assert (GNUNET_NETWORK_fdset_isset (tc->read_ready, connection->sock)); RETRY: ret = GNUNET_NETWORK_socket_recv (connection->sock, buffer, connection->max); if (-1 == ret) { if (EINTR == errno) goto RETRY; signal_receive_error (connection, errno); return; } LOG (GNUNET_ERROR_TYPE_DEBUG, "receive_ready read %u/%u bytes from `%s' (%p)!\n", (unsigned int) ret, connection->max, GNUNET_a2s (connection->addr, connection->addrlen), connection); GNUNET_assert (NULL != (receiver = connection->receiver)); connection->receiver = NULL; receiver (connection->receiver_cls, buffer, ret, connection->addr, connection->addrlen, 0); } /** * Receive data from the given connection. Note that this function * will call @a receiver asynchronously using the scheduler. It will * "immediately" return. Note that there MUST only be one active * receive call per connection at any given point in time (so do not * call receive again until the receiver callback has been invoked). * * @param connection connection handle * @param max maximum number of bytes to read * @param timeout maximum amount of time to wait * @param receiver function to call with received data * @param receiver_cls closure for @a receiver * @return #GNUNET_SYSERR if @a connection died (receiver was * called with error) */ int GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *connection, size_t max, struct GNUNET_TIME_Relative timeout, GNUNET_CONNECTION_Receiver receiver, void *receiver_cls) { GNUNET_assert ((NULL == connection->read_task) && (NULL == connection->receiver)); GNUNET_assert (NULL != receiver); connection->receiver = receiver; connection->receiver_cls = receiver_cls; connection->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); connection->max = max; if (NULL != connection->sock) { connection->read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining ( connection->receive_timeout), connection->sock, &receive_ready, connection); return GNUNET_OK; } if ((NULL == connection->dns_active) && (NULL == connection->ap_head) && (NULL == connection->proxy_handshake)) { connection->receiver = NULL; receiver (receiver_cls, NULL, 0, NULL, 0, ETIMEDOUT); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Cancel receive job on the given connection. Note that the * receiver callback must not have been called yet in order * for the cancellation to be valid. * * @param connection connection handle * @return closure of the original receiver callback closure */ void * GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *connection) { if (NULL != connection->read_task) { GNUNET_assert (connection == GNUNET_SCHEDULER_cancel (connection->read_task)); connection->read_task = NULL; } connection->receiver = NULL; return connection->receiver_cls; } /** * Try to call the transmit notify method (check if we do * have enough space available first)! * * @param connection connection for which we should do this processing * @return #GNUNET_YES if we were able to call notify */ static int process_notify (struct GNUNET_CONNECTION_Handle *connection) { size_t used; size_t avail; size_t size; GNUNET_CONNECTION_TransmitReadyNotify notify; LOG (GNUNET_ERROR_TYPE_DEBUG, "process_notify is running\n"); GNUNET_assert (NULL == connection->write_task); if (NULL == (notify = connection->nth.notify_ready)) { LOG (GNUNET_ERROR_TYPE_DEBUG, "No one to notify\n"); return GNUNET_NO; } used = connection->write_buffer_off - connection->write_buffer_pos; avail = connection->write_buffer_size - used; size = connection->nth.notify_size; if (size > avail) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Not enough buffer\n"); return GNUNET_NO; } connection->nth.notify_ready = NULL; if (connection->write_buffer_size - connection->write_buffer_off < size) { /* need to compact */ memmove (connection->write_buffer, &connection->write_buffer[connection->write_buffer_pos], used); connection->write_buffer_off -= connection->write_buffer_pos; connection->write_buffer_pos = 0; } avail = connection->write_buffer_size - connection->write_buffer_off; GNUNET_assert (avail >= size); size = notify (connection->nth.notify_ready_cls, avail, &connection->write_buffer[connection->write_buffer_off]); GNUNET_assert (size <= avail); if (0 != size) connection->write_buffer_off += size; return GNUNET_YES; } /** * Task invoked by the scheduler when a call to transmit * is timing out (we never got enough buffer space to call * the callback function before the specified timeout * expired). * * This task notifies the client about the timeout. * * @param cls the `struct GNUNET_CONNECTION_Handle` */ static void transmit_timeout (void *cls) { struct GNUNET_CONNECTION_Handle *connection = cls; GNUNET_CONNECTION_TransmitReadyNotify notify; connection->nth.timeout_task = NULL; LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmit to `%s:%u/%s' fails, time out reached (%p).\n", connection->hostname, connection->port, GNUNET_a2s (connection->addr, connection->addrlen), connection); notify = connection->nth.notify_ready; GNUNET_assert (NULL != notify); connection->nth.notify_ready = NULL; notify (connection->nth.notify_ready_cls, 0, NULL); } /** * Task invoked by the scheduler when we failed to connect * at the time of being asked to transmit. * * This task notifies the client about the error. * * @param cls the `struct GNUNET_CONNECTION_Handle` */ static void connect_error (void *cls) { struct GNUNET_CONNECTION_Handle *connection = cls; GNUNET_CONNECTION_TransmitReadyNotify notify; LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmission request of size %u fails (%s/%u), connection failed (%p).\n", connection->nth.notify_size, connection->hostname, connection->port, connection); connection->write_task = NULL; notify = connection->nth.notify_ready; connection->nth.notify_ready = NULL; notify (connection->nth.notify_ready_cls, 0, NULL); } /** * We are ready to transmit (or got a timeout). * * @param cls our connection handle */ static void transmit_ready (void *cls) { struct GNUNET_CONNECTION_Handle *connection = cls; GNUNET_CONNECTION_TransmitReadyNotify notify; const struct GNUNET_SCHEDULER_TaskContext *tc; ssize_t ret; size_t have; LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_ready running (%p).\n", connection); GNUNET_assert (NULL != connection->write_task); connection->write_task = NULL; GNUNET_assert (NULL == connection->nth.timeout_task); tc = GNUNET_SCHEDULER_get_task_context (); if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmit to `%s' fails, time out reached (%p).\n", GNUNET_a2s (connection->addr, connection->addrlen), connection); notify = connection->nth.notify_ready; GNUNET_assert (NULL != notify); connection->nth.notify_ready = NULL; notify (connection->nth.notify_ready_cls, 0, NULL); return; } GNUNET_assert (NULL != connection->sock); if (NULL == tc->write_ready) { /* special circumstances (in particular, PREREQ_DONE after * connect): not yet ready to write, but no "fatal" error either. * Hence retry. */ goto SCHEDULE_WRITE; } if (! GNUNET_NETWORK_fdset_isset (tc->write_ready, connection->sock)) { GNUNET_assert (NULL == connection->write_task); /* special circumstances (in particular, shutdown): not yet ready * to write, but no "fatal" error either. Hence retry. */ goto SCHEDULE_WRITE; } GNUNET_assert (connection->write_buffer_off >= connection->write_buffer_pos); if ((NULL != connection->nth.notify_ready) && (connection->write_buffer_size < connection->nth.notify_size)) { connection->write_buffer = GNUNET_realloc (connection->write_buffer, connection->nth.notify_size); connection->write_buffer_size = connection->nth.notify_size; } process_notify (connection); have = connection->write_buffer_off - connection->write_buffer_pos; if (0 == have) { /* no data ready for writing, terminate write loop */ return; } GNUNET_assert (have <= connection->write_buffer_size); GNUNET_assert (have + connection->write_buffer_pos <= connection->write_buffer_size); GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size); RETRY: ret = GNUNET_NETWORK_socket_send (connection->sock, &connection ->write_buffer[connection->write_buffer_pos], have); if (-1 == ret) { if (EINTR == errno) goto RETRY; if (NULL != connection->write_task) { GNUNET_SCHEDULER_cancel (connection->write_task); connection->write_task = NULL; } signal_transmit_error (connection, errno); return; } LOG (GNUNET_ERROR_TYPE_DEBUG, "Connection transmitted %u/%u bytes to `%s' (%p)\n", (unsigned int) ret, have, GNUNET_a2s (connection->addr, connection->addrlen), connection); connection->write_buffer_pos += ret; if (connection->write_buffer_pos == connection->write_buffer_off) { /* transmitted all pending data */ connection->write_buffer_pos = 0; connection->write_buffer_off = 0; } if ((0 == connection->write_buffer_off) && (NULL == connection->nth.notify_ready)) return; /* all data sent! */ /* not done writing, schedule more */ SCHEDULE_WRITE: LOG (GNUNET_ERROR_TYPE_DEBUG, "Re-scheduling transmit_ready (more to do) (%p).\n", connection); have = connection->write_buffer_off - connection->write_buffer_pos; GNUNET_assert ((NULL != connection->nth.notify_ready) || (have > 0)); if (NULL == connection->write_task) connection->write_task = GNUNET_SCHEDULER_add_write_net ((connection->nth.notify_ready == NULL) ? GNUNET_TIME_UNIT_FOREVER_REL : GNUNET_TIME_absolute_get_remaining ( connection->nth.transmit_timeout), connection->sock, &transmit_ready, connection); } /** * Ask the connection to call us once the specified number of bytes * are free in the transmission buffer. Will never call the @a notify * callback in this task, but always first go into the scheduler. * * @param connection connection * @param size number of bytes to send * @param timeout after how long should we give up (and call * @a notify with buf NULL and size 0)? * @param notify function to call * @param notify_cls closure for @a notify * @return non-NULL if the notify callback was queued, * NULL if we are already going to notify someone else (busy) */ struct GNUNET_CONNECTION_TransmitHandle * GNUNET_CONNECTION_notify_transmit_ready ( struct GNUNET_CONNECTION_Handle *connection, size_t size, struct GNUNET_TIME_Relative timeout, GNUNET_CONNECTION_TransmitReadyNotify notify, void *notify_cls) { if (NULL != connection->nth.notify_ready) { GNUNET_assert (0); return NULL; } GNUNET_assert (NULL != notify); GNUNET_assert (size < GNUNET_MAX_MESSAGE_SIZE); GNUNET_assert (connection->write_buffer_off <= connection->write_buffer_size); GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size); GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_off); connection->nth.notify_ready = notify; connection->nth.notify_ready_cls = notify_cls; connection->nth.connection = connection; connection->nth.notify_size = size; connection->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout); GNUNET_assert (NULL == connection->nth.timeout_task); if ((NULL == connection->sock) && (NULL == connection->ap_head) && (NULL == connection->dns_active) && (NULL == connection->proxy_handshake)) { if (NULL != connection->write_task) GNUNET_SCHEDULER_cancel (connection->write_task); connection->write_task = GNUNET_SCHEDULER_add_now (&connect_error, connection); return &connection->nth; } if (NULL != connection->write_task) return &connection->nth; /* previous transmission still in progress */ if (NULL != connection->sock) { /* connected, try to transmit now */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Scheduling transmission (%p).\n", connection); connection->write_task = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining ( connection->nth.transmit_timeout), connection->sock, &transmit_ready, connection); return &connection->nth; } /* not yet connected, wait for connection */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Need to wait to schedule transmission for connection, adding timeout task (%p).\n", connection); connection->nth.timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, &transmit_timeout, connection); return &connection->nth; } /** * Cancel the specified transmission-ready notification. * * @param th notification to cancel */ void GNUNET_CONNECTION_notify_transmit_ready_cancel ( struct GNUNET_CONNECTION_TransmitHandle *th) { GNUNET_assert (NULL != th->notify_ready); th->notify_ready = NULL; if (NULL != th->timeout_task) { GNUNET_SCHEDULER_cancel (th->timeout_task); th->timeout_task = NULL; } if (NULL != th->connection->write_task) { GNUNET_SCHEDULER_cancel (th->connection->write_task); th->connection->write_task = NULL; } } /** * Create a connection to be proxied using a given connection. * * @param cph connection to proxy server * @return connection to be proxied */ struct GNUNET_CONNECTION_Handle * GNUNET_CONNECTION_create_proxied_from_handshake ( struct GNUNET_CONNECTION_Handle *cph) { struct GNUNET_CONNECTION_Handle *proxied = GNUNET_CONNECTION_create_from_existing (NULL); proxied->proxy_handshake = cph; return proxied; } /** * Activate proxied connection and destroy initial proxy handshake connection. * There must not be any pending requests for reading or writing to the * proxy hadshake connection at this time. * * @param proxied connection connection to proxy server */ void GNUNET_CONNECTION_acivate_proxied (struct GNUNET_CONNECTION_Handle *proxied) { struct GNUNET_CONNECTION_Handle *cph = proxied->proxy_handshake; GNUNET_assert (NULL != cph); GNUNET_assert (NULL == proxied->sock); GNUNET_assert (NULL != cph->sock); proxied->sock = cph->sock; cph->sock = NULL; GNUNET_CONNECTION_destroy (cph); connect_success_continuation (proxied); } /* end of connection.c */