X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fconnection.c;h=5aa277546a3eadf0230e4680fc1293d2cb77c4c0;hb=265d10682af1afce58988be998d62e1849a3e545;hp=9769c3cba08b4852fc8046d61f101ffd55207ab5;hpb=acd5a863d4092349cea5ba19c1567d911ed93189;p=oweals%2Fgnunet.git diff --git a/src/util/connection.c b/src/util/connection.c index 9769c3cba..5aa277546 100644 --- a/src/util/connection.c +++ b/src/util/connection.c @@ -1,10 +1,10 @@ /* This file is part of GNUnet. - (C) 2009 Christian Grothoff (and other contributing authors) + 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 General Public License as published - by the Free Software Foundation; either version 2, or (at your + by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but @@ -14,8 +14,8 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** @@ -30,42 +30,14 @@ * These rules should apply in general, but for this * module they are VERY, VERY important. */ - #include "platform.h" -#include "gnunet_common.h" -#include "gnunet_connection_lib.h" -#include "gnunet_container_lib.h" +#include "gnunet_util_lib.h" #include "gnunet_resolver_service.h" -#include "gnunet_scheduler_lib.h" -#include "gnunet_server_lib.h" -#define DEBUG_CONNECTION GNUNET_NO -/** - * Possible functions to call after connect failed or succeeded. - */ -enum ConnectContinuations -{ - /** - * Call nothing. - */ - COCO_NONE = 0, - - /** - * Call "receive_again". - */ - COCO_RECEIVE_AGAIN = 1, - - /** - * Call "transmit_ready". - */ - COCO_TRANSMIT_READY = 2, - - /** - * Call "destroy_continuation". - */ - COCO_DESTROY_CONTINUATION = 4 -}; +#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) + +#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall) /** @@ -86,9 +58,9 @@ struct GNUNET_CONNECTION_TransmitHandle void *notify_ready_cls; /** - * Our socket handle. + * Our connection handle. */ - struct GNUNET_CONNECTION_Handle *sh; + struct GNUNET_CONNECTION_Handle *connection; /** * Timeout for receiving (in absolute time). @@ -98,7 +70,7 @@ struct GNUNET_CONNECTION_TransmitHandle /** * Task called on timeout. */ - GNUNET_SCHEDULER_TaskIdentifier timeout_task; + struct GNUNET_SCHEDULER_Task * timeout_task; /** * At what number of bytes available in the @@ -111,7 +83,7 @@ struct GNUNET_CONNECTION_TransmitHandle /** * During connect, we try multiple possible IP addresses - * to find out which one might work. + * to find out which one might work. */ struct AddressProbe { @@ -139,7 +111,7 @@ struct AddressProbe /** * Connection for which we are probing. */ - struct GNUNET_CONNECTION_Handle *h; + struct GNUNET_CONNECTION_Handle *connection; /** * Lenth of addr. @@ -147,14 +119,14 @@ struct AddressProbe socklen_t addrlen; /** - * Task waiting for the socket to finish connecting. + * Task waiting for the connection to finish connecting. */ - GNUNET_SCHEDULER_TaskIdentifier task; + struct GNUNET_SCHEDULER_Task * task; }; /** - * @brief handle for a network socket + * @brief handle for a network connection */ struct GNUNET_CONNECTION_Handle { @@ -165,13 +137,13 @@ struct GNUNET_CONNECTION_Handle const struct GNUNET_CONFIGURATION_Handle *cfg; /** - * Linked list of sockets we are currently trying out + * Linked list of sockets we are currently trying out * (during connect). */ struct AddressProbe *ap_head; /** - * Linked list of sockets we are currently trying out + * Linked list of sockets we are currently trying out * (during connect). */ struct AddressProbe *ap_tail; @@ -182,7 +154,7 @@ struct GNUNET_CONNECTION_Handle struct sockaddr *addr; /** - * Pointer to the hostname if socket was + * Pointer to the hostname if connection was * created using DNS lookup, otherwise NULL. */ char *hostname; @@ -198,7 +170,7 @@ struct GNUNET_CONNECTION_Handle GNUNET_CONNECTION_Receiver receiver; /** - * Closure for receiver. + * Closure for @e receiver. */ void *receiver_cls; @@ -208,41 +180,36 @@ struct GNUNET_CONNECTION_Handle char *write_buffer; /** - * Current size of our write buffer. + * Current size of our @e write_buffer. */ size_t write_buffer_size; /** - * Current write-offset in write buffer (where + * Current write-offset in @e write_buffer (where * would we write next). */ size_t write_buffer_off; /** - * Current read-offset in write buffer (how many + * Current read-offset in @e write_buffer (how many * bytes have already been sent). */ size_t write_buffer_pos; /** - * Length of addr. + * Length of @e addr. */ socklen_t addrlen; /** * Read task that we may need to wait for. */ - GNUNET_SCHEDULER_TaskIdentifier read_task; + struct GNUNET_SCHEDULER_Task *read_task; /** * Write task that we may need to wait for. */ - GNUNET_SCHEDULER_TaskIdentifier write_task; - - /** - * Destroy task (if already scheduled). - */ - GNUNET_SCHEDULER_TaskIdentifier destroy_task; + struct GNUNET_SCHEDULER_Task *write_task; /** * Handle to a pending DNS lookup request. @@ -259,21 +226,11 @@ struct GNUNET_CONNECTION_Handle */ struct GNUNET_TIME_Absolute receive_timeout; - /** - * Functions to call after connect failed or succeeded. - */ - enum ConnectContinuations ccs; - /** * Maximum number of bytes to read (for receiving). */ size_t max; - /** - * Ignore GNUNET_SCHEDULER_REASON_SHUTDOWN for this socket. - */ - int ignore_shutdown; - /** * Port to connect to. */ @@ -285,58 +242,91 @@ struct GNUNET_CONNECTION_Handle * termination as a signal (because only then will the leaked * socket be freed!) */ - int16_t persist; + 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 sock the connection to set persistent + * @param connection the connection to set persistent */ -void GNUNET_CONNECTION_persist_(struct GNUNET_CONNECTION_Handle *sock) +void +GNUNET_CONNECTION_persist_ (struct GNUNET_CONNECTION_Handle *connection) { - sock->persist = GNUNET_YES; + connection->persist = GNUNET_YES; } + /** - * Create a socket handle by boxing an existing OS socket. The OS + * 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_socket_destroy will close it. + * #GNUNET_connection_destroy() will close it. * * @param osSocket existing socket to box - * @return the boxed socket handle + * @return the boxed connection handle */ struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_existing (struct GNUNET_NETWORK_Handle - *osSocket) +GNUNET_CONNECTION_create_from_existing (struct GNUNET_NETWORK_Handle *osSocket) { - struct GNUNET_CONNECTION_Handle *ret; - ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle)); - ret->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; - ret->write_buffer = GNUNET_malloc(ret->write_buffer_size); - ret->sock = osSocket; - return ret; + struct GNUNET_CONNECTION_Handle *connection; + + connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); + connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; + connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); + connection->sock = osSocket; + return connection; } /** - * Create a socket handle by accepting on a listen socket. This + * Create a connection handle by accepting on a listen socket. This * function may block if the listen socket has no connection ready. * - * @param access function to use to check if access is allowed - * @param access_cls closure for access + * @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 socket handle, NULL on error + * @return the connection handle, NULL on error */ struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access, - void *access_cls, +GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access_cb, + void *access_cb_cls, struct GNUNET_NETWORK_Handle *lsock) { - struct GNUNET_CONNECTION_Handle *ret; + struct GNUNET_CONNECTION_Handle *connection; char addr[128]; socklen_t addrlen; struct GNUNET_NETWORK_Handle *sock; @@ -345,8 +335,8 @@ GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access, struct sockaddr_in6 *v6; struct sockaddr *sa; void *uaddr; - struct GNUNET_CONNECTION_Credentials *gcp; - struct GNUNET_CONNECTION_Credentials gc; + struct GNUNET_CONNECTION_Credentials *gcp; + struct GNUNET_CONNECTION_Credentials gc; #ifdef SO_PEERCRED struct ucred uc; socklen_t olen; @@ -354,254 +344,266 @@ GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access, addrlen = sizeof (addr); sock = - GNUNET_NETWORK_socket_accept (lsock, (struct sockaddr *) &addr, &addrlen); + GNUNET_NETWORK_socket_accept (lsock, (struct sockaddr *) &addr, &addrlen); if (NULL == sock) - { - GNUNET_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; - } + { + 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 ((sa->sa_family == AF_INET6) && (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr))) - { - /* convert to V4 address */ - v4 = GNUNET_malloc (sizeof (struct sockaddr_in)); - memset (v4, 0, sizeof (struct sockaddr_in)); - v4->sin_family = AF_INET; - 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); - } + 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 + 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); - memcpy (uaddr, addr, addrlen); - } + { + uaddr = GNUNET_malloc (addrlen); + memcpy (uaddr, addr, addrlen); + } gcp = NULL; gc.uid = 0; gc.gid = 0; - if (sa->sa_family == AF_UNIX) - { + 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; + /* 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; - } + /* 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 - } + /* this is for Solaris 10 */ + ucred_t *uc; - if ((access != NULL) && - (GNUNET_YES != (aret = access (access_cls, gcp, uaddr, addrlen)))) + uc = NULL; + if (0 == getpeerucred (GNUNET_NETWORK_get_fd (sock), &uc)) { - if (aret == GNUNET_NO) - GNUNET_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; + gc.uid = ucred_geteuid (uc); + gc.gid = ucred_getegid (uc); + gcp = &gc; } - ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle)); - ret->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; - ret->write_buffer = GNUNET_malloc(ret->write_buffer_size); - ret->addr = uaddr; - ret->addrlen = addrlen; - ret->sock = sock; -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Accepting connection from `%s': %p\n"), - GNUNET_a2s (uaddr, addrlen), ret); + ucred_free (uc); +#endif +#endif #endif - return ret; + } + + 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_SERVER_MIN_BUFFER_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 sock the client to get the address for + * @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 address - * @return GNUNET_OK on success + * @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 *sock, - void **addr, size_t * addrlen) +GNUNET_CONNECTION_get_address (struct GNUNET_CONNECTION_Handle *connection, + void **addr, + size_t *addrlen) { - if ((sock->addr == NULL) || (sock->addrlen == 0)) + if ((NULL == connection->addr) || (0 == connection->addrlen)) return GNUNET_NO; - *addr = GNUNET_malloc (sock->addrlen); - memcpy (*addr, sock->addr, sock->addrlen); - *addrlen = sock->addrlen; + *addr = GNUNET_malloc (connection->addrlen); + memcpy (*addr, connection->addr, connection->addrlen); + *addrlen = connection->addrlen; return GNUNET_OK; } /** - * This function is called after establishing a connection either has - * succeeded or timed out. Note that it is possible that the attempt - * timed out and that we're immediately retrying. If we are retrying, - * we need to wait again (or timeout); if we succeeded, we need to - * wait for data (or timeout). + * Tell the receiver callback that we had an IO error. * - * @param cls our connection handle - * @param tc task context describing why we are here + * @param connection connection to signal error + * @param errcode error code to send */ static void -receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +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); +} /** - * Scheduler let us know that the connect task is finished (or was - * cancelled due to shutdown). Now really clean up. + * Tell the receiver callback that a timeout was reached. * - * @param cls our "struct GNUNET_CONNECTION_Handle *" - * @param tc unused + * @param connection connection to signal for */ static void -destroy_continuation (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +signal_receive_timeout (struct GNUNET_CONNECTION_Handle *connection) { - struct GNUNET_CONNECTION_Handle *sock = cls; - GNUNET_CONNECTION_TransmitReadyNotify notify; - struct AddressProbe *pos; + GNUNET_CONNECTION_Receiver receiver; - sock->destroy_task = GNUNET_SCHEDULER_NO_TASK; - GNUNET_assert (sock->dns_active == NULL); - if (0 != (sock->ccs & COCO_TRANSMIT_READY)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Destroy waits for CCS-TR to be done (%p)\n", sock); -#endif - sock->ccs |= COCO_DESTROY_CONTINUATION; - return; - } - if (sock->write_task != GNUNET_SCHEDULER_NO_TASK) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Destroy waits for write_task to be done (%p)\n", sock); -#endif - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->destroy_task); - sock->destroy_task - = GNUNET_SCHEDULER_add_after (sock->write_task, - &destroy_continuation, sock); - return; - } - if (0 != (sock->ccs & COCO_RECEIVE_AGAIN)) - { - sock->ccs |= COCO_DESTROY_CONTINUATION; - return; - } - if (sock->sock != NULL) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shutting down socket (%p)\n", sock); -#endif - if (sock->persist != GNUNET_YES) - { - if ( (GNUNET_YES != GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR)) && - (errno != ENOTCONN) && - (errno != ECONNRESET) ) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "shutdown"); - } - } - if (sock->read_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->destroy_task); - sock->destroy_task - = GNUNET_SCHEDULER_add_after (sock->read_task, - &destroy_continuation, sock); - return; - } -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Destroy actually runs (%p)!\n", sock); -#endif - while (NULL != (pos = sock->ap_head)) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock)); - GNUNET_SCHEDULER_cancel (pos->task); - GNUNET_CONTAINER_DLL_remove (sock->ap_head, sock->ap_tail, pos); - GNUNET_free (pos); - } - GNUNET_assert (sock->nth.timeout_task == GNUNET_SCHEDULER_NO_TASK); - GNUNET_assert (sock->ccs == COCO_NONE); - if (NULL != (notify = sock->nth.notify_ready)) - { - sock->nth.notify_ready = NULL; - notify (sock->nth.notify_ready_cls, 0, NULL); - } + 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); +} - if (sock->sock != NULL) - { - if (sock->persist != GNUNET_YES) - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock->sock)); - else - GNUNET_free (sock->sock); /* at least no memory leak (we deliberately - leak the socket in this special case) ... */ - } - GNUNET_free_non_null (sock->addr); - GNUNET_free_non_null (sock->hostname); - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->destroy_task); -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Freeing memory of connection %p.\n", sock); -#endif - GNUNET_free (sock->write_buffer); - GNUNET_free (sock); + +/** + * 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; +} + /** - * See if we are now connected. If not, wait longer for - * connect to succeed. If connected, we should be able - * to write now as well, unless we timed out. + * We are ready to transmit (or got a timeout). * * @param cls our connection handle * @param tc task context describing why we are here @@ -611,126 +613,54 @@ transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); /** - * We've failed for good to establish a connection. + * This function is called once we either timeout or have data ready + * to read. * - * @param h the connection we tried to establish + * @param cls connection to read from + * @param tc scheduler context */ static void -connect_fail_continuation (struct GNUNET_CONNECTION_Handle *h) -{ -#if DEBUG_CONNECTION - GNUNET_log ((0 != strncmp (h->hostname, - "localhost:", - 10)) - ? GNUNET_ERROR_TYPE_INFO - : GNUNET_ERROR_TYPE_WARNING, - _("Failed to establish TCP connection to `%s:%u', no further addresses to try.\n"), - h->hostname, h->port); -#endif - /* connect failed / timed out */ - GNUNET_break (h->ap_head == NULL); - GNUNET_break (h->ap_tail == NULL); - GNUNET_break (h->dns_active == GNUNET_NO); - GNUNET_break (h->sock == NULL); - - /* trigger jobs that used to wait on "connect_task" */ - if (0 != (h->ccs & COCO_RECEIVE_AGAIN)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connect_fail_continuation triggers receive_again (%p)\n", - h); -#endif - h->ccs -= COCO_RECEIVE_AGAIN; - h->read_task = GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_NO_TASK, - &receive_again, h); - } - if (0 != (h->ccs & COCO_TRANSMIT_READY)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connect_fail_continuation cancels timeout_task, triggers transmit_ready (%p)\n", - h); -#endif - GNUNET_assert (h->nth.timeout_task != GNUNET_SCHEDULER_NO_TASK); - GNUNET_SCHEDULER_cancel (h->nth.timeout_task); - h->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK; - h->ccs -= COCO_TRANSMIT_READY; - GNUNET_assert (h->write_task == GNUNET_SCHEDULER_NO_TASK); - h->write_task = GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_NO_TASK, - &transmit_ready, h); - } - if (0 != (h->ccs & COCO_DESTROY_CONTINUATION)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connect_fail_continuation runs destroy_continuation (%p)\n", - h); -#endif - h->ccs -= COCO_DESTROY_CONTINUATION; - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->destroy_task); - h->destroy_task - = GNUNET_SCHEDULER_add_now (&destroy_continuation, - h); - } -} +receive_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); /** * We've succeeded in establishing a connection. * - * @param h the connection we tried to establish + * @param connection the connection we tried to establish */ static void -connect_success_continuation (struct GNUNET_CONNECTION_Handle *h) +connect_success_continuation (struct GNUNET_CONNECTION_Handle *connection) { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connection to `%s' succeeded! (%p)\n", - GNUNET_a2s (h->addr, h->addrlen), h); -#endif + 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 (0 != (h->ccs & COCO_RECEIVE_AGAIN)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connect_success_continuation runs receive_again (%p)\n", - h); -#endif - h->ccs -= COCO_RECEIVE_AGAIN; - h->read_task = GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_NO_TASK, - &receive_again, h); - } - if (0 != (h->ccs & COCO_TRANSMIT_READY)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connect_success_continuation runs transmit_ready, cancels timeout_task (%p)\n", - h); -#endif - GNUNET_assert (h->nth.timeout_task != GNUNET_SCHEDULER_NO_TASK); - GNUNET_SCHEDULER_cancel (h->nth.timeout_task); - h->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK; - h->ccs -= COCO_TRANSMIT_READY; - GNUNET_assert (h->write_task == GNUNET_SCHEDULER_NO_TASK); - h->write_task = + 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 - (h->nth.transmit_timeout), h->sock, - &transmit_ready, h); - } - if (0 != (h->ccs & COCO_DESTROY_CONTINUATION)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connect_success_continuation runs destroy_continuation (%p)\n", - h); -#endif - h->ccs -= COCO_DESTROY_CONTINUATION; - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->destroy_task); - h->destroy_task - = GNUNET_SCHEDULER_add_now (&destroy_continuation, - h); - } + (connection->nth.transmit_timeout), connection->sock, + &transmit_ready, connection); + } } @@ -746,233 +676,233 @@ connect_probe_continuation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct AddressProbe *ap = cls; - struct GNUNET_CONNECTION_Handle *h = ap->h; + struct GNUNET_CONNECTION_Handle *connection = ap->connection; struct AddressProbe *pos; int error; socklen_t len; - GNUNET_assert (ap->sock != NULL); - GNUNET_CONTAINER_DLL_remove (h->ap_head, h->ap_tail, ap); + GNUNET_assert (NULL != ap->sock); + GNUNET_CONTAINER_DLL_remove (connection->ap_head, connection->ap_tail, ap); len = sizeof (error); errno = 0; error = 0; - if ( (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) || - (GNUNET_OK != - GNUNET_NETWORK_socket_getsockopt (ap->sock, SOL_SOCKET, SO_ERROR, - &error, &len)) || - (error != 0) ) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock)); - GNUNET_free (ap); - if ((NULL == h->ap_head) && (h->dns_active == GNUNET_NO)) - connect_fail_continuation (h); - return; - } - GNUNET_assert (h->sock == NULL); - h->sock = ap->sock; - GNUNET_assert (h->addr == NULL); - h->addr = GNUNET_malloc (ap->addrlen); - memcpy (h->addr, ap->addr, ap->addrlen); - h->addrlen = ap->addrlen; + 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); + memcpy (connection->addr, ap->addr, ap->addrlen); + connection->addrlen = ap->addrlen; GNUNET_free (ap); /* cancel all other attempts */ - while (NULL != (pos = h->ap_head)) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock)); - GNUNET_SCHEDULER_cancel (pos->task); - GNUNET_CONTAINER_DLL_remove (h->ap_head, h->ap_tail, pos); - GNUNET_free (pos); - } - connect_success_continuation (h); + 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 socket connection given the specified address. + * 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 cls our `struct GNUNET_CONNECTION_Handle *` * @param addr address to try, NULL for "last call" - * @param addrlen length of addr + * @param addrlen length of @a addr */ static void try_connect_using_address (void *cls, - const struct sockaddr *addr, socklen_t addrlen) + const struct sockaddr *addr, + socklen_t addrlen) { - struct GNUNET_CONNECTION_Handle *h = cls; + struct GNUNET_CONNECTION_Handle *connection = cls; struct AddressProbe *ap; struct GNUNET_TIME_Relative delay; - if (addr == NULL) - { - h->dns_active = NULL; - if (NULL == h->ap_head) - connect_fail_continuation (h); - return; - } - if (h->sock != NULL) + 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 (h->addr == NULL); + GNUNET_assert (NULL == connection->addr); /* try to connect */ -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Trying to connect using address `%s:%u/%s:%u'\n", - h->hostname, h->port, GNUNET_a2s (addr, addrlen), h->port); -#endif + 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]; memcpy (&ap[1], addr, addrlen); ap->addrlen = addrlen; - ap->h = h; + ap->connection = connection; switch (ap->addr->sa_family) - { - case AF_INET: - ((struct sockaddr_in *) ap->addr)->sin_port = htons (h->port); - break; - case AF_INET6: - ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (h->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 (ap->sock == NULL) - { - GNUNET_free (ap); - return; /* not supported by OS */ - } -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Trying to connect to `%s' (%p)\n"), - GNUNET_a2s (ap->addr, ap->addrlen), h); -#endif - if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (ap->sock, - ap->addr, - ap->addrlen)) && - (errno != EINPROGRESS)) - { - /* maybe refused / unsupported address, try next */ - GNUNET_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 (h->ap_head, h->ap_tail, ap); + { + 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 = GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT; - if (h->nth.notify_ready != NULL) - delay = GNUNET_TIME_relative_min (delay, - GNUNET_TIME_absolute_get_remaining - (h->nth.transmit_timeout)); - if (h->receiver != NULL) - delay = GNUNET_TIME_relative_min (delay, - GNUNET_TIME_absolute_get_remaining - (h->receive_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); + GNUNET_SCHEDULER_add_write_net (delay, ap->sock, + &connect_probe_continuation, ap); } /** - * Create a socket handle by (asynchronously) connecting to a host. + * 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 socket handle + * @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) +GNUNET_CONNECTION_create_from_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *hostname, + uint16_t port) { - struct GNUNET_CONNECTION_Handle *ret; + struct GNUNET_CONNECTION_Handle *connection; GNUNET_assert (0 < strlen (hostname)); /* sanity check */ - ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle)); - ret->cfg = cfg; - ret->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; - ret->write_buffer = GNUNET_malloc(ret->write_buffer_size); - ret->port = port; - ret->hostname = GNUNET_strdup (hostname); - ret->dns_active = GNUNET_RESOLVER_ip_get (cfg, - ret->hostname, - AF_UNSPEC, - GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT, - &try_connect_using_address, ret); - return ret; + connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); + connection->cfg = cfg; + connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_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, + GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT, + &try_connect_using_address, connection); + return connection; } /** - * Create a socket handle by connecting to a UNIX domain service. + * 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 socket handle, NULL on systems without UNIX support + * @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) +GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *unixpath) { #ifdef AF_UNIX - struct GNUNET_CONNECTION_Handle *ret; + struct GNUNET_CONNECTION_Handle *connection; struct sockaddr_un *un; - size_t slen; GNUNET_assert (0 < strlen (unixpath)); /* sanity check */ - un = GNUNET_malloc (sizeof (struct sockaddr_un)); + un = GNUNET_new (struct sockaddr_un); un->sun_family = AF_UNIX; - slen = strlen (unixpath) + 1; - if (slen >= sizeof (un->sun_path)) - slen = sizeof (un->sun_path) - 1; - memcpy (un->sun_path, - unixpath, - slen); - un->sun_path[slen] = '\0'; - slen += sizeof (sa_family_t); -#if LINUX - un->sun_path[0] = '\0'; - slen = sizeof (struct sockaddr_un); + strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1); +#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 - ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle)); - ret->cfg = cfg; - ret->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; - ret->write_buffer = GNUNET_malloc(ret->write_buffer_size); - ret->port = 0; - ret->hostname = NULL; - ret->addr = (struct sockaddr*) un; - ret->addrlen = slen; - ret->sock = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); - if (NULL == ret->sock) - { - GNUNET_free (ret->addr); - GNUNET_free (ret->write_buffer); - GNUNET_free (ret); - return NULL; - } - if (GNUNET_OK != GNUNET_NETWORK_socket_connect (ret->sock, - ret->addr, - ret->addrlen)) - { - /* Just return; we expect everything to work eventually so don't fail HARD */ - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ret->sock)); - GNUNET_free (ret->addr); - GNUNET_free (ret->write_buffer); - GNUNET_free (ret); - return NULL; - } - connect_success_continuation (ret); - return ret; +#if HAVE_SOCKADDR_IN_SIN_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_SERVER_MIN_BUFFER_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 @@ -980,14 +910,58 @@ GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct /** - * Create a socket handle by (asynchronously) connecting to a host. + * 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); + 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 server address - * @return the socket handle + * @param addrlen length of @a serv_addr + * @return the connection handle */ struct GNUNET_CONNECTION_Handle * GNUNET_CONNECTION_create_from_sockaddr (int af_family, @@ -995,332 +969,266 @@ GNUNET_CONNECTION_create_from_sockaddr (int af_family, socklen_t addrlen) { struct GNUNET_NETWORK_Handle *s; - struct GNUNET_CONNECTION_Handle *ret; - s = GNUNET_NETWORK_socket_create (af_family, SOCK_STREAM, 0); - if (s == NULL) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING | - GNUNET_ERROR_TYPE_BULK, "socket"); - return NULL; - } - if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (s, serv_addr, addrlen)) - && (errno != EINPROGRESS)) - { - /* maybe refused / unsupported address, try next */ - GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect"); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (s)); - return NULL; - } - ret = GNUNET_CONNECTION_create_from_existing (s); - ret->addr = GNUNET_malloc (addrlen); - memcpy (ret->addr, serv_addr, addrlen); - ret->addrlen = addrlen; -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Trying to connect to `%s' (%p)\n"), - GNUNET_a2s (serv_addr, addrlen), ret); -#endif - return ret; + 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 socket is valid (no fatal errors have happened so far). - * Note that a socket that is still trying to connect is considered + * 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 sock socket to check - * @return GNUNET_YES if valid, GNUNET_NO otherwise + * @param connection connection to check + * @return #GNUNET_YES if valid, #GNUNET_NO otherwise */ int -GNUNET_CONNECTION_check (struct GNUNET_CONNECTION_Handle *sock) +GNUNET_CONNECTION_check (struct GNUNET_CONNECTION_Handle *connection) { - if ((sock->ap_head != NULL) || (sock->dns_active != NULL)) + if ((NULL != connection->ap_head) || + (NULL != connection->dns_active) || + (NULL != connection->proxy_handshake)) return GNUNET_YES; /* still trying to connect */ - return (sock->sock == NULL) ? GNUNET_NO : GNUNET_YES; + if ( (0 != connection->destroy_later) || + (NULL == connection->sock) ) + return GNUNET_NO; + return GNUNET_YES; } /** - * Close the socket and free associated resources. Pending - * transmissions may be completed or dropped depending on the - * arguments. If a receive call is pending and should - * NOT be completed, 'GNUNET_CONNECTION_receive_cancel' - * should be called explicitly first. + * 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 sock socket to destroy - * @param finish_pending_write should pending writes be completed or aborted? - * (this applies to transmissions where the data has already been - * read from the application; all other transmissions should be - * aborted using 'GNUNET_CONNECTION_notify_transmit_ready_cancel'). + * @param connection connection to destroy */ void -GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *sock, - int finish_pending_write) +GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *connection) { - if (GNUNET_NO == finish_pending_write) + 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) { - if (sock->write_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (sock->write_task); - sock->write_task = GNUNET_SCHEDULER_NO_TASK; - sock->write_buffer_off = 0; - } + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (connection->sock)); } - if ((sock->write_buffer_off == 0) && (sock->dns_active != NULL)) + else { - GNUNET_RESOLVER_request_cancel (sock->dns_active); - sock->dns_active = NULL; + GNUNET_NETWORK_socket_free_memory_only_ (connection->sock); /* at least no memory leak (we deliberately + * leak the socket in this special case) ... */ } - - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->destroy_task); - sock->destroy_task - = GNUNET_SCHEDULER_add_now (&destroy_continuation, sock); -} - - -/** - * Tell the receiver callback that a timeout was reached. - */ -static void -signal_timeout (struct GNUNET_CONNECTION_Handle *sh) -{ - GNUNET_CONNECTION_Receiver receiver; - -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Network signals time out to receiver (%p)!\n", sh); -#endif - GNUNET_assert (NULL != (receiver = sh->receiver)); - sh->receiver = NULL; - receiver (sh->receiver_cls, NULL, 0, NULL, 0, 0); -} - - -/** - * Tell the receiver callback that we had an IO error. - */ -static void -signal_error (struct GNUNET_CONNECTION_Handle *sh, int errcode) -{ - GNUNET_CONNECTION_Receiver receiver; - GNUNET_assert (NULL != (receiver = sh->receiver)); - sh->receiver = NULL; - receiver (sh->receiver_cls, NULL, 0, sh->addr, sh->addrlen, errcode); + } + GNUNET_free_non_null (connection->addr); + GNUNET_free_non_null (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 + * @param tc scheduler context */ static void -receive_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +receive_ready (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct GNUNET_CONNECTION_Handle *sh = cls; - struct GNUNET_TIME_Absolute now; - char buffer[sh->max]; + struct GNUNET_CONNECTION_Handle *connection = cls; + char buffer[connection->max]; ssize_t ret; GNUNET_CONNECTION_Receiver receiver; - sh->read_task = GNUNET_SCHEDULER_NO_TASK; - if ( (GNUNET_YES == sh->ignore_shutdown) && - (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))) - { - /* ignore shutdown request, go again immediately */ -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Ignoring shutdown signal per configuration\n"); -#endif - sh->read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining - (sh->receive_timeout), - sh->sock, - &receive_ready, sh); - return; - } - now = GNUNET_TIME_absolute_get (); - if ((now.abs_value > sh->receive_timeout.abs_value) || - (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) || - (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))) - { -#if DEBUG_CONNECTION - if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receive from `%s' encounters error: time out by %llums... (%p)\n", - GNUNET_a2s (sh->addr, sh->addrlen), - GNUNET_TIME_absolute_get_duration (sh->receive_timeout). - rel_value, sh); -#endif - signal_timeout (sh); - return; - } - if (sh->sock == NULL) - { - /* connect failed for good */ -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receive encounters error, socket closed... (%p)\n", sh); -#endif - signal_error (sh, ECONNREFUSED); - return; - } - GNUNET_assert (GNUNET_NETWORK_fdset_isset (tc->read_ready, sh->sock)); + connection->read_task = NULL; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + /* ignore shutdown request, go again immediately */ + connection->read_task = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining + (connection->receive_timeout), connection->sock, + &receive_ready, connection); + return; + } + 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 (sh->sock, buffer, sh->max); - if (ret == -1) - { - if (errno == EINTR) - goto RETRY; -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Error receiving: %s\n", STRERROR (errno)); -#endif - signal_error (sh, errno); - return; - } -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "receive_ready read %u/%u bytes from `%s' (%p)!\n", - (unsigned int) ret, - sh->max, GNUNET_a2s (sh->addr, sh->addrlen), sh); -#endif - GNUNET_assert (NULL != (receiver = sh->receiver)); - sh->receiver = NULL; - receiver (sh->receiver_cls, buffer, ret, sh->addr, sh->addrlen, 0); + 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); } /** - * This function is called after establishing a connection either has - * succeeded or timed out. Note that it is possible that the attempt - * timed out and that we're immediately retrying. If we are retrying, - * we need to wait again (or timeout); if we succeeded, we need to - * wait for data (or timeout). - * - * @param cls our connection handle - * @param tc task context describing why we are here - */ -static void -receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct GNUNET_CONNECTION_Handle *sh = cls; - struct GNUNET_TIME_Absolute now; - - sh->read_task = GNUNET_SCHEDULER_NO_TASK; - if (sh->sock == NULL) - { - /* not connected and no longer trying */ -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receive encounters error, socket closed (%p)...\n", sh); -#endif - signal_error (sh, ECONNREFUSED); - return; - } - now = GNUNET_TIME_absolute_get (); - if ((now.abs_value > sh->receive_timeout.abs_value) || - (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receive encounters error: time out (%p)...\n", sh); -#endif - signal_timeout (sh); - return; - } - GNUNET_assert (sh->sock != NULL); - /* connect succeeded, wait for data! */ - sh->read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining - (sh->receive_timeout), - sh->sock, - &receive_ready, sh); -} - - -/** - * Receive data from the given socket. Note that this function will - * call "receiver" asynchronously using the scheduler. It will + * 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 socket at any given point in time (so do not + * receive call per connection at any given point in time (so do not * call receive again until the receiver callback has been invoked). * - * @param sock socket handle + * @param connection connection handle * @param max maximum number of bytes to read - * @param timeout maximum amount of time to wait (use -1 for "forever") + * @param timeout maximum amount of time to wait * @param receiver function to call with received data - * @param receiver_cls closure for receiver + * @param receiver_cls closure for @a receiver */ void -GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *sock, +GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *connection, size_t max, struct GNUNET_TIME_Relative timeout, GNUNET_CONNECTION_Receiver receiver, void *receiver_cls) { - struct GNUNET_SCHEDULER_TaskContext tc; - - GNUNET_assert ((sock->read_task == GNUNET_SCHEDULER_NO_TASK) && - (0 == (sock->ccs & COCO_RECEIVE_AGAIN)) && - (sock->receiver == NULL)); - sock->receiver = receiver; - sock->receiver_cls = receiver_cls; - sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); - sock->max = max; - if (sock->sock != NULL) - { - memset (&tc, 0, sizeof (tc)); - tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE; - receive_again (sock, &tc); - return; - } - if ((sock->dns_active == NULL) && (sock->ap_head == NULL)) - { - receiver (receiver_cls, NULL, 0, NULL, 0, ETIMEDOUT); - return; - } - sock->ccs += COCO_RECEIVE_AGAIN; + 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; + } + 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; + } } /** - * Configure this connection to ignore shutdown signals. - * - * @param sock socket handle - * @param do_ignore GNUNET_YES to ignore, GNUNET_NO to restore default - */ -void -GNUNET_CONNECTION_ignore_shutdown (struct GNUNET_CONNECTION_Handle *sock, - int do_ignore) -{ - sock->ignore_shutdown = do_ignore; -} - - -/** - * Cancel receive job on the given socket. Note that the + * 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 sock socket handle + * @param connection connection handle * @return closure of the original receiver callback closure */ void * -GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *sock) +GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *connection) { - if (sock->read_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_assert (sock == GNUNET_SCHEDULER_cancel (sock->read_task)); - sock->read_task = GNUNET_SCHEDULER_NO_TASK; - } - else - { - GNUNET_assert (0 != (sock->ccs & COCO_RECEIVE_AGAIN)); - sock->ccs -= COCO_RECEIVE_AGAIN; - } - sock->receiver = NULL; - return sock->receiver_cls; + 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; } @@ -1328,41 +1236,53 @@ GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *sock) * Try to call the transmit notify method (check if we do * have enough space available first)! * - * @param sock socket for which we should do this processing - * @return GNUNET_YES if we were able to call notify + * @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 *sock) +process_notify (struct GNUNET_CONNECTION_Handle *connection) { size_t used; size_t avail; size_t size; GNUNET_CONNECTION_TransmitReadyNotify notify; - GNUNET_assert (sock->write_task == GNUNET_SCHEDULER_NO_TASK); - if (NULL == (notify = sock->nth.notify_ready)) + 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 = sock->write_buffer_off - sock->write_buffer_pos; - avail = sock->write_buffer_size - used; - size = sock->nth.notify_size; + } + 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; - sock->nth.notify_ready = NULL; - if (sock->write_buffer_size - sock->write_buffer_off < size) - { - /* need to compact */ - memmove (sock->write_buffer, - &sock->write_buffer[sock->write_buffer_pos], used); - sock->write_buffer_off -= sock->write_buffer_pos; - sock->write_buffer_pos = 0; - } - avail = sock->write_buffer_size - sock->write_buffer_off; + } + 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 (sock->nth.notify_ready_cls, - avail, - &sock->write_buffer[sock->write_buffer_off]); + size = + notify (connection->nth.notify_ready_cls, avail, + &connection->write_buffer[connection->write_buffer_off]); GNUNET_assert (size <= avail); - sock->write_buffer_off += size; + if (0 != size) + connection->write_buffer_off += size; return GNUNET_YES; } @@ -1375,32 +1295,28 @@ process_notify (struct GNUNET_CONNECTION_Handle *sock) * * This task notifies the client about the timeout. * - * @param cls the 'struct GNUNET_CONNECTION_Handle' + * @param cls the `struct GNUNET_CONNECTION_Handle` * @param tc scheduler context */ static void -transmit_timeout (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +transmit_timeout (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct GNUNET_CONNECTION_Handle *sock = cls; + struct GNUNET_CONNECTION_Handle *connection = cls; GNUNET_CONNECTION_TransmitReadyNotify notify; -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "transmit_timeout running (%p)\n", sock); -#endif - sock->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK; -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmit to `%s:%u/%s' fails, time out reached (%p).\n", - sock->hostname, - sock->port, GNUNET_a2s (sock->addr, sock->addrlen), sock); -#endif - GNUNET_assert (0 != (sock->ccs & COCO_TRANSMIT_READY)); - sock->ccs -= COCO_TRANSMIT_READY; /* remove request */ - notify = sock->nth.notify_ready; - sock->nth.notify_ready = NULL; - notify (sock->nth.notify_ready_cls, 0, NULL); + 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); } @@ -1410,310 +1326,286 @@ transmit_timeout (void *cls, * * This task notifies the client about the error. * - * @param cls the 'struct GNUNET_CONNECTION_Handle' + * @param cls the `struct GNUNET_CONNECTION_Handle` * @param tc scheduler context */ static void -connect_error (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +connect_error (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct GNUNET_CONNECTION_Handle *sock = cls; + struct GNUNET_CONNECTION_Handle *connection = cls; GNUNET_CONNECTION_TransmitReadyNotify notify; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmission request of size %u fails (%s/%u), connection failed (%p).\n", - sock->nth.notify_size, - sock->hostname, - sock->port, - sock); -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmission request of size %u fails, connection failed (%p).\n", - sock->nth.notify_size, sock); -#endif - sock->write_task = GNUNET_SCHEDULER_NO_TASK; - notify = sock->nth.notify_ready; - sock->nth.notify_ready = NULL; - notify (sock->nth.notify_ready_cls, 0, NULL); + 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); } /** - * FIXME - * - * @param sock FIXME - */ -static void -transmit_error (struct GNUNET_CONNECTION_Handle *sock) -{ - GNUNET_CONNECTION_TransmitReadyNotify notify; - - if (NULL != sock->sock) - { - GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR); - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (sock->sock)); - sock->sock = NULL; - } - if (sock->read_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (sock->read_task); - sock->read_task = GNUNET_SCHEDULER_NO_TASK; - signal_timeout (sock); - return; - } - if (sock->nth.notify_ready == NULL) - return; /* nobody to tell about it */ - notify = sock->nth.notify_ready; - sock->nth.notify_ready = NULL; - notify (sock->nth.notify_ready_cls, 0, NULL); -} - - -/** - * See if we are now connected. If not, wait longer for - * connect to succeed. If connected, we should be able - * to write now as well, unless we timed out. + * We are ready to transmit (or got a timeout). * * @param cls our connection handle * @param tc task context describing why we are here */ static void -transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +transmit_ready (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct GNUNET_CONNECTION_Handle *sock = cls; + struct GNUNET_CONNECTION_Handle *connection = cls; GNUNET_CONNECTION_TransmitReadyNotify notify; ssize_t ret; size_t have; -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "transmit_ready running (%p).\n", sock); -#endif - GNUNET_assert (sock->write_task != GNUNET_SCHEDULER_NO_TASK); - sock->write_task = GNUNET_SCHEDULER_NO_TASK; - GNUNET_assert (sock->nth.timeout_task == GNUNET_SCHEDULER_NO_TASK); - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + 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); + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + if (NULL != connection->sock) + goto SCHEDULE_WRITE; /* ignore shutdown, go again immediately */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmit to `%s' fails, shutdown happened (%p).\n", + GNUNET_a2s (connection->addr, connection->addrlen), connection); + notify = connection->nth.notify_ready; + if (NULL != notify) { - if (sock->ignore_shutdown == GNUNET_YES) - goto SCHEDULE_WRITE; /* ignore shutdown, go again immediately */ -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmit to `%s' fails, shutdown happened (%p).\n", - GNUNET_a2s (sock->addr, sock->addrlen), sock); -#endif - notify = sock->nth.notify_ready; - sock->nth.notify_ready = NULL; - notify (sock->nth.notify_ready_cls, 0, NULL); - return; + connection->nth.notify_ready = NULL; + notify (connection->nth.notify_ready_cls, 0, NULL); } + return; + } if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmit to `%s' fails, time out reached (%p).\n", - GNUNET_a2s (sock->addr, sock->addrlen), sock); -#endif - notify = sock->nth.notify_ready; - sock->nth.notify_ready = NULL; - notify (sock->nth.notify_ready_cls, 0, NULL); - return; - } - GNUNET_assert (NULL != sock->sock); - if (tc->write_ready == NULL) - { - /* 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, sock->sock)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("Could not satisfy pending transmission request, socket closed or connect failed (%p).\n"), - sock); -#endif - transmit_error (sock); - return; /* connect failed for good, we're finished */ - } - GNUNET_assert (sock->write_buffer_off >= sock->write_buffer_pos); - if ( (sock->nth.notify_ready != NULL) && - (sock->write_buffer_size < sock->nth.notify_size) ) - { - sock->write_buffer = GNUNET_realloc(sock->write_buffer, - sock->nth.notify_size); - sock->write_buffer_size = sock->nth.notify_size; - } - process_notify (sock); - have = sock->write_buffer_off - sock->write_buffer_pos; - if (have == 0) - { - /* no data ready for writing, terminate write loop */ - return; - } - GNUNET_assert (have <= sock->write_buffer_size); - GNUNET_assert (have + sock->write_buffer_pos <= sock->write_buffer_size); - GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_size); + { + 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 (sock->sock, - &sock->write_buffer[sock-> - write_buffer_pos], - have); - if (ret == -1) + 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) { - if (errno == EINTR) - goto RETRY; -#if 0 - int en = errno; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Failed to send to `%s': %s\n"), - GNUNET_a2s (sock->addr, - sock->addrlen), - STRERROR (en)); -#endif -#if DEBUG_CONNECTION - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send"); -#endif - transmit_error (sock); - return; + GNUNET_SCHEDULER_cancel (connection->write_task); + connection->write_task = NULL; } -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "transmit_ready transmitted %u/%u bytes to `%s' (%p)\n", - (unsigned int) ret, - have, GNUNET_a2s (sock->addr, sock->addrlen), sock); -#endif - sock->write_buffer_pos += ret; - if (sock->write_buffer_pos == sock->write_buffer_off) - { - /* transmitted all pending data */ - sock->write_buffer_pos = 0; - sock->write_buffer_off = 0; - } - if ((sock->write_buffer_off == 0) && (NULL == sock->nth.notify_ready)) + 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: -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Re-scheduling transmit_ready (more to do) (%p).\n", sock); -#endif - if (sock->write_task == GNUNET_SCHEDULER_NO_TASK) - sock->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining - (sock->nth.transmit_timeout), - sock->sock, &transmit_ready, sock); + 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 socket to call us once the specified number of bytes - * are free in the transmission buffer. May call the notify - * method immediately if enough space is available. + * 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 sock socket + * @param connection connection * @param size number of bytes to send * @param timeout after how long should we give up (and call - * notify with buf NULL and size 0)? + * @a notify with buf NULL and size 0)? * @param notify function to call - * @param notify_cls closure for notify + * @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 - *sock, size_t size, +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 (sock->nth.notify_ready != NULL) - { - GNUNET_break (0); - return NULL; - } - GNUNET_assert (notify != NULL); + if (NULL != connection->nth.notify_ready) + { + GNUNET_assert (0); + return NULL; + } + GNUNET_assert (NULL != notify); GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE); - GNUNET_assert (sock->write_buffer_off <= sock->write_buffer_size); - GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_size); - GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_off); - sock->nth.notify_ready = notify; - sock->nth.notify_ready_cls = notify_cls; - sock->nth.sh = sock; - sock->nth.notify_size = size; - sock->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout); - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->nth.timeout_task); - if ((sock->sock == NULL) && - (sock->ap_head == NULL) && (sock->dns_active == NULL)) - { - if (sock->write_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (sock->write_task); - sock->write_task = GNUNET_SCHEDULER_add_now (&connect_error, sock); - return &sock->nth; - } - if (GNUNET_SCHEDULER_NO_TASK != sock->write_task) - return &sock->nth; - if (sock->sock != NULL) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Scheduling transmit_ready (%p).\n", sock); -#endif - sock->write_task = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining - (sock->nth. - transmit_timeout), - sock->sock, - &transmit_ready, - sock); - } - else - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "CCS-Scheduling transmit_ready, adding timeout task (%p).\n", - sock); -#endif - sock->ccs |= COCO_TRANSMIT_READY; - sock->nth.timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, - &transmit_timeout, - sock); - } - return &sock->nth; + 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. + * Cancel the specified transmission-ready notification. + * + * @param th notification to cancel */ void -GNUNET_CONNECTION_notify_transmit_ready_cancel (struct - GNUNET_CONNECTION_TransmitHandle - *h) +GNUNET_CONNECTION_notify_transmit_ready_cancel (struct GNUNET_CONNECTION_TransmitHandle *th) { - GNUNET_assert (h->notify_ready != NULL); - if (0 != (h->sh->ccs & COCO_TRANSMIT_READY)) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "notify_transmit_ready_cancel cancels timeout_task (%p)\n", - h); -#endif - GNUNET_SCHEDULER_cancel (h->timeout_task); - h->timeout_task = GNUNET_SCHEDULER_NO_TASK; - h->sh->ccs -= COCO_TRANSMIT_READY; - } - else - { - if (h->sh->write_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (h->sh->write_task); - h->sh->write_task = GNUNET_SCHEDULER_NO_TASK; - } - } - h->notify_ready = NULL; + 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 */