X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fnetwork.c;h=08121928db6ee69312b571f4c48878ab68aeebeb;hb=8dfd7bda2f139e2dac27e804167eedc3d227453e;hp=9b61bd8c2e2b3c2f925d40b43728ab805f002316;hpb=70e6847205a9f9b9b660be2a173d5bc309eaa58d;p=oweals%2Fgnunet.git diff --git a/src/util/network.c b/src/util/network.c index 9b61bd8c2..08121928d 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -19,1285 +19,1246 @@ */ /** - * @file util/network/network.c - * @brief basic, low-level TCP networking interface - * @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. - * - * TODO: - * - can we merge receive_ready and receive_again? - * - can we integrate the nth.timeout_task with the write_task's timeout? + * @file util/network.c + * @brief basic, low-level networking interface + * @author Nils Durner */ #include "platform.h" -#include "gnunet_common.h" -#include "gnunet_network_lib.h" -#include "gnunet_scheduler_lib.h" +#include "gnunet_disk_lib.h" +#include "disk.h" +#include "gnunet_container_lib.h" #define DEBUG_NETWORK GNUNET_NO -/** - * List of address families to give as hints to - * getaddrinfo, in reverse order of preference. - */ -static int address_families[] = - { AF_INET, AF_INET6, AF_UNSPEC }; - -struct GNUNET_NETWORK_TransmitHandle -{ - - /** - * Function to call if the send buffer has notify_size - * bytes available. - */ - GNUNET_NETWORK_TransmitReadyNotify notify_ready; - - /** - * Closure for notify_ready. - */ - void *notify_ready_cls; - - /** - * Our socket handle. - */ - struct GNUNET_NETWORK_SocketHandle *sh; - - /** - * Timeout for receiving (in absolute time). - */ - struct GNUNET_TIME_Absolute transmit_timeout; - - /** - * Task called on timeout. - */ - GNUNET_SCHEDULER_TaskIdentifier timeout_task; - - /** - * At what number of bytes available in the - * write buffer should the notify method be called? - */ - size_t notify_size; +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif -}; -/** - * @brief handle for a network socket - */ -struct GNUNET_NETWORK_SocketHandle +struct GNUNET_NETWORK_Handle { +#ifndef MINGW + int fd; - /** - * Scheduler that was used for the connect task. - */ - struct GNUNET_SCHEDULER_Handle *sched; - - /** - * Address information for connect (may be NULL). - */ - struct addrinfo *ai; +#else + SOCKET fd; +#endif /** - * Index for the next struct addrinfo for connect attempts (may be NULL) + * Address family / domain. */ - struct addrinfo *ai_pos; + int af; +}; - /** - * Network address of the other end-point, may be NULL. - */ - struct sockaddr *addr; - /** - * Pointer to the hostname if socket was - * created using DNS lookup, otherwise NULL. - */ - char *hostname; +struct GNUNET_NETWORK_FDSet +{ /** - * Pointer to our write buffer. + * Maximum number of any socket socket descriptor in the set (plus one) */ - char *write_buffer; + int nsds; /** - * Size of our write buffer. + * Bitset with the descriptors. */ - size_t write_buffer_size; + fd_set sds; +#ifdef WINDOWS /** - * Current write-offset in write buffer (where - * would we write next). + * Linked list of handles */ - size_t write_buffer_off; + struct GNUNET_CONTAINER_SList *handles; +#endif - /** - * Current read-offset in write buffer (how many - * bytes have already been send). - */ - size_t write_buffer_pos; +}; - /** - * Length of addr. - */ - socklen_t addrlen; +#ifndef FD_COPY +#define FD_COPY(s, d) (memcpy ((d), (s), sizeof (fd_set))) +#endif - /** - * Offset in our address family list - * that we used last. - */ - int af_fam_offset; - /** - * Connect task that we may need to wait for. - */ - GNUNET_SCHEDULER_TaskIdentifier connect_task; +/** + * Set if a socket should use blocking or non-blocking IO. + * @param fd socket + * @param doBlock blocking mode + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +static int +socket_set_blocking (struct GNUNET_NETWORK_Handle *fd, int doBlock) +{ - /** - * Read task that we may need to wait for. - */ - GNUNET_SCHEDULER_TaskIdentifier read_task; +#if MINGW + u_long mode; + mode = !doBlock; + if (ioctlsocket (fd->fd, FIONBIO, &mode) == SOCKET_ERROR) - /** - * Write task that we may need to wait for. - */ - GNUNET_SCHEDULER_TaskIdentifier write_task; + { + SetErrnoFromWinsockError (WSAGetLastError ()); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket"); + return GNUNET_SYSERR; + } + return GNUNET_OK; - /** - * The handle we return for GNUNET_NETWORK_notify_transmit_ready. - */ - struct GNUNET_NETWORK_TransmitHandle nth; +#else + /* not MINGW */ + int flags = fcntl (fd->fd, F_GETFL); + if (flags == -1) - /** - * Underlying OS's socket, set to -1 after fatal errors. - */ - int sock; + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl"); + return GNUNET_SYSERR; + } + if (doBlock) + flags &= ~O_NONBLOCK; - /** - * Port to connect to. - */ - uint16_t port; + else + flags |= O_NONBLOCK; + if (0 != fcntl (fd->fd, F_SETFL, flags)) - /** - * Function to call on data received, NULL - * if no receive is pending. - */ - GNUNET_NETWORK_Receiver receiver; + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +#endif +} - /** - * Closure for receiver. - */ - void *receiver_cls; - /** - * Timeout for receiving (in absolute time). - */ - struct GNUNET_TIME_Absolute receive_timeout; +#ifndef MINGW +/** + * Make a socket non-inheritable to child processes + * + * @param h the socket to make non-inheritable + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + * @warning Not implemented on Windows + */ +static int +socket_set_inheritable (const struct GNUNET_NETWORK_Handle *h) +{ + int i; + + i = fcntl (h->fd, F_GETFD); + if (i < 0) + return GNUNET_SYSERR; + if (i == (i | FD_CLOEXEC)) + return GNUNET_OK; + i |= FD_CLOEXEC; + if (fcntl (h->fd, F_SETFD, i) < 0) + return GNUNET_SYSERR; + return GNUNET_OK; +} +#endif - /** - * Maximum number of bytes to read - * (for receiving). - */ - size_t max; -}; +#ifdef DARWIN +/** + * The MSG_NOSIGNAL equivalent on Mac OS X + * + * @param h the socket to make non-delaying + */ +static void +socket_set_nosigpipe (const struct GNUNET_NETWORK_Handle *h) +{ + int abs_value = 1; + if (0 != + setsockopt (h->fd, SOL_SOCKET, SO_NOSIGPIPE, &abs_value, sizeof (abs_value))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); +} +#endif /** - * Create a socket handle by boxing an existing OS socket. The OS - * socket should henceforth be no longer used directly. - * GNUNET_socket_destroy will close it. + * Disable delays when sending data via the socket. + * (GNUnet makes sure that messages are as big as + * possible already). * - * @param sched scheduler to use - * @param osSocket existing socket to box - * @param maxbuf maximum write buffer size for the socket (use - * 0 for sockets that need no write buffers, such as listen sockets) - * @return the boxed socket handle + * @param h the socket to make non-delaying */ -struct GNUNET_NETWORK_SocketHandle * -GNUNET_NETWORK_socket_create_from_existing (struct GNUNET_SCHEDULER_Handle - *sched, int osSocket, - size_t maxbuf) +static void +socket_set_nodelay (const struct GNUNET_NETWORK_Handle *h) { - struct GNUNET_NETWORK_SocketHandle *ret; - ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf); - ret->write_buffer = (char *) &ret[1]; - ret->write_buffer_size = maxbuf; - ret->sock = osSocket; - ret->sched = sched; - return ret; +#ifndef WINDOWS + int value = 1; + if (0 != setsockopt (h->fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof (value))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); +#else + const char * abs_value = "1"; + if (0 != setsockopt (h->fd, IPPROTO_TCP, TCP_NODELAY, abs_value, sizeof (abs_value))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); +#endif } /** - * Create a socket handle by accepting on a listen socket. This - * function may block if the listen socket has no connection ready. + * accept a new connection on a socket * - * @param sched scheduler to use - * @param access function to use to check if access is allowed - * @param access_cls closure for access - * @param lsock listen socket - * @param maxbuf maximum write buffer size for the socket (use - * 0 for sockets that need no write buffers, such as listen sockets) - * @return the socket handle, NULL on error + * @param desc bound socket + * @param address address of the connecting peer, may be NULL + * @param address_len length of address + * @return client socket */ -struct GNUNET_NETWORK_SocketHandle * -GNUNET_NETWORK_socket_create_from_accept (struct GNUNET_SCHEDULER_Handle - *sched, - GNUNET_NETWORK_AccessCheck access, - void *access_cls, int lsock, - size_t maxbuf) +struct GNUNET_NETWORK_Handle * +GNUNET_NETWORK_socket_accept (const struct GNUNET_NETWORK_Handle *desc, + struct sockaddr *address, + socklen_t * address_len) { - struct GNUNET_NETWORK_SocketHandle *ret; - char addr[32]; - socklen_t addrlen; - int fd; - int aret; - struct sockaddr_in *v4; - struct sockaddr_in6 *v6; - struct sockaddr *sa; - void *uaddr; - - addrlen = sizeof (addr); - fd = accept (lsock, (struct sockaddr *) &addr, &addrlen); - if (fd == -1) + struct GNUNET_NETWORK_Handle *ret; + + ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle)); + ret->fd = accept (desc->fd, address, address_len); + ret->af = address->sa_family; + if (ret->fd == INVALID_SOCKET) { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "accept"); +#ifdef MINGW + SetErrnoFromWinsockError (WSAGetLastError ()); +#endif + GNUNET_free (ret); return NULL; } #ifndef MINGW - // FIXME NILS - if (0 != fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "fcntl"); -#endif - if (addrlen > sizeof (addr)) + if (ret->fd >= FD_SETSIZE) { - GNUNET_break (0); - GNUNET_break (0 == CLOSE (fd)); + GNUNET_break (0 == close (ret->fd)); + GNUNET_free (ret); + errno = EMFILE; return NULL; } +#endif + if (GNUNET_SYSERR == socket_set_blocking (ret, GNUNET_NO)) - 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); - } - else { - uaddr = GNUNET_malloc (addrlen); - memcpy (uaddr, addr, addrlen); - } - if ((access != NULL) && - (GNUNET_YES != (aret = access (access_cls, uaddr, addrlen)))) - { - if (aret == GNUNET_NO) - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Access denied to `%s'\n"), - GNUNET_a2s(uaddr, addrlen)); - GNUNET_break (0 == SHUTDOWN (fd, SHUT_RDWR)); - GNUNET_break (0 == CLOSE (fd)); - GNUNET_free (uaddr); + /* we might want to treat this one as fatal... */ + GNUNET_break (0); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ret)); return NULL; } -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Accepting connection from `%s'\n"), - GNUNET_a2s(uaddr, addrlen)); + +#ifndef MINGW + if (GNUNET_OK != socket_set_inheritable (ret)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "socket_set_inheritable"); #endif - ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf); - ret->write_buffer = (char *) &ret[1]; - ret->write_buffer_size = maxbuf; - ret->addr = uaddr; - ret->addrlen = addrlen; - ret->sock = fd; - ret->sched = sched; +#ifdef DARWIN + socket_set_nosigpipe (ret); +#endif +#ifdef AF_UNIX + if (address->sa_family != AF_UNIX) +#endif + socket_set_nodelay (ret); return ret; } + /** - * Obtain the network address of the other party. - * - * @param sock 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 + * Bind to a connected socket + * @param desc socket + * @param address address to be bound + * @param address_len length of address + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ int -GNUNET_NETWORK_socket_get_address (struct GNUNET_NETWORK_SocketHandle *sock, - void **addr, size_t * addrlen) +GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc, + const struct sockaddr *address, + socklen_t address_len) { - if ((sock->addr == NULL) || (sock->addrlen == 0)) - return GNUNET_NO; - *addr = GNUNET_malloc (sock->addrlen); - memcpy (*addr, sock->addr, sock->addrlen); - *addrlen = sock->addrlen; - return GNUNET_OK; + int ret; + +#ifdef IPV6_V6ONLY +#ifdef IPPROTO_IPV6 + const int on = 1; + if (desc->af == AF_INET6) + if (0 != setsockopt (desc->fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "setsockopt"); +#if 0 + /* is this needed or desired? or done elsewhere? */ + if (0 != setsockopt (desc->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "setsockopt"); +#endif +#endif +#endif + ret = bind (desc->fd, address, address_len); +#ifdef MINGW + if (SOCKET_ERROR == ret) + SetErrnoFromWinsockError (WSAGetLastError ()); +#else +#ifndef LINUX + if ( (ret == 0) && (address->sa_family == AF_UNIX)) + { + const struct sockaddr_un *un = (const struct sockaddr_un*) address; + if (0 != unlink (un->sun_path)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "unlink", + un->sun_path); + } +#endif +#endif + return ret == 0 ? GNUNET_OK : GNUNET_SYSERR; } /** - * Set if a socket should use blocking or non-blocking IO. - * - * @return GNUNET_OK on success, GNUNET_SYSERR on error + * Close a socket + * @param desc socket + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ -static int -socket_set_blocking (int handle, int doBlock) +int +GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc) { -#if MINGW - u_long mode; - mode = !doBlock; -#if HAVE_PLIBC_FD - if (ioctlsocket (plibc_fd_get_handle (handle), FIONBIO, &mode) == - SOCKET_ERROR) - { - SetErrnoFromWinsockError (WSAGetLastError ()); - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket"); - return GNUNET_SYSERR; - } -#else - if (ioctlsocket (handle, FIONBIO, &mode) == SOCKET_ERROR) - { - SetErrnoFromWinsockError (WSAGetLastError ()); - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket"); - return GNUNET_SYSERR; - } -#endif - /* store the blocking mode */ -#if HAVE_PLIBC_FD - plibc_fd_set_blocking (handle, doBlock); + int ret; + +#ifdef MINGW + ret = closesocket (desc->fd); + SetErrnoFromWinsockError (WSAGetLastError ()); #else - __win_SetHandleBlockingMode (handle, doBlock); + ret = close (desc->fd); #endif - return GNUNET_OK; + GNUNET_free (desc); + return (ret == 0) ? GNUNET_OK : GNUNET_SYSERR; +} + +/** + * Box a native socket (and check that it is a socket). + * + * @param fd socket to box + * @return NULL on error (including not supported on target platform) + */ +struct GNUNET_NETWORK_Handle * +GNUNET_NETWORK_socket_box_native (int fd) +{ +#if MINGW + return NULL; #else - /* not MINGW */ - int flags = fcntl (handle, F_GETFL); - if (flags == -1) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl"); - return GNUNET_SYSERR; - } - if (doBlock) - flags &= ~O_NONBLOCK; - else - flags |= O_NONBLOCK; - if (0 != fcntl (handle, F_SETFL, flags)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl"); - return GNUNET_SYSERR; - } - return GNUNET_OK; + struct GNUNET_NETWORK_Handle *ret; + + if (fcntl (fd, F_GETFD) < 0) + return NULL; /* invalid FD */ + ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle)); + ret->fd = fd; + ret->af = AF_UNSPEC; + return ret; #endif } /** - * Perform a DNS lookup for the hostname associated - * with the current socket, iterating over the address - * families as specified in the "address_families" array. + * Connect a socket + * @param desc socket + * @param address peer address + * @param address_len length of address + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ -static void -try_lookup (struct GNUNET_NETWORK_SocketHandle *sock) +int +GNUNET_NETWORK_socket_connect (const struct GNUNET_NETWORK_Handle *desc, + const struct sockaddr *address, + socklen_t address_len) { - struct addrinfo hints; - int ec; + int ret; + ret = connect (desc->fd, address, address_len); + +#ifdef MINGW + if (SOCKET_ERROR == ret) - while ( (sock->ai_pos == NULL) && - (sock->af_fam_offset > 0) ) { - if (sock->ai != NULL) - freeaddrinfo (sock->ai); - memset (&hints, 0, sizeof (hints)); - hints.ai_family = address_families[--sock->af_fam_offset]; - hints.ai_socktype = SOCK_STREAM; - if (0 != (ec = getaddrinfo (sock->hostname, NULL, &hints, &sock->ai))) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK, - "`%s' failed for hostname `%s': %s\n", - "getaddrinfo", sock->hostname, gai_strerror (ec)); - sock->ai = NULL; - } - sock->ai_pos = sock->ai; + SetErrnoFromWinsockError (WSAGetLastError ()); + if (errno == EWOULDBLOCK) + errno = EINPROGRESS; } +#endif + return ret == 0 ? GNUNET_OK : GNUNET_SYSERR; } /** - * Initiate asynchronous TCP connect request. + * Get socket options * - * @param sock what socket to connect - * @return GNUNET_SYSERR error (no more addresses to try) + * @param desc socket + * @param level protocol level of the option + * @param optname identifier of the option + * @param optval options + * @param optlen length of optval + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ -static int -try_connect (struct GNUNET_NETWORK_SocketHandle *sock) +int +GNUNET_NETWORK_socket_getsockopt (const struct GNUNET_NETWORK_Handle *desc, + int level, int optname, void *optval, + socklen_t * optlen) { - int s; + int ret; + ret = getsockopt (desc->fd, level, optname, optval, optlen); + +#ifdef MINGW + if (ret == 0 && level == SOL_SOCKET && optname == SO_ERROR) + *((int *) optval) = GetErrnoFromWinsockError (*((int *) optval)); + + else if (SOCKET_ERROR == ret) + SetErrnoFromWinsockError (WSAGetLastError ()); - if (sock->addr != NULL) - { - GNUNET_free (sock->addr); - sock->addr = NULL; - sock->addrlen = 0; - } - while (1) - { - if (sock->ai_pos == NULL) - try_lookup (sock); - if (sock->ai_pos == NULL) - { - /* no more addresses to try, fatal! */ - return GNUNET_SYSERR; - } - switch (sock->ai_pos->ai_family) - { - case AF_INET: - ((struct sockaddr_in *) sock->ai_pos->ai_addr)->sin_port = - htons (sock->port); - break; - case AF_INET6: - ((struct sockaddr_in6 *) sock->ai_pos->ai_addr)->sin6_port = - htons (sock->port); - break; - default: - sock->ai_pos = sock->ai_pos->ai_next; - continue; - } - s = SOCKET (sock->ai_pos->ai_family, SOCK_STREAM, 0); - if (s == -1) - { - /* maybe unsupported address family, try next */ - GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "socket"); - sock->ai_pos = sock->ai_pos->ai_next; - continue; - } -#ifndef MINGW - // FIXME NILS - if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "fcntl"); #endif - if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO)) - { - /* we'll treat this one as fatal */ - GNUNET_break (0 == CLOSE (s)); - return GNUNET_SYSERR; - } -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Trying to connect to `%s'\n"), - GNUNET_a2s(sock->ai_pos->ai_addr, - sock->ai_pos->ai_addrlen)); + return ret == 0 ? GNUNET_OK : GNUNET_SYSERR; +} + + +/** + * Listen on a socket + * @param desc socket + * @param backlog length of the listen queue + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + */ +int +GNUNET_NETWORK_socket_listen (const struct GNUNET_NETWORK_Handle *desc, + int backlog) +{ + int ret; + ret = listen (desc->fd, backlog); + +#ifdef MINGW + if (SOCKET_ERROR == ret) + SetErrnoFromWinsockError (WSAGetLastError ()); + #endif - if ((0 != CONNECT (s, - sock->ai_pos->ai_addr, - sock->ai_pos->ai_addrlen)) && (errno != EINPROGRESS)) - { - /* maybe refused / unsupported address, try next */ - GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect"); - GNUNET_break (0 == CLOSE (s)); - sock->ai_pos = sock->ai_pos->ai_next; - continue; - } - break; - } - /* got one! copy address information! */ - sock->addrlen = sock->ai_pos->ai_addrlen; - sock->addr = GNUNET_malloc (sock->addrlen); - memcpy (sock->addr, sock->ai_pos->ai_addr, sock->addrlen); - sock->ai_pos = sock->ai_pos->ai_next; - sock->sock = s; - return GNUNET_OK; + return ret == 0 ? GNUNET_OK : GNUNET_SYSERR; } /** - * Scheduler let us know that we're either ready to - * write on the socket OR connect timed out. Do the - * right thing. + * How much data is available to be read on this descriptor? + * + * Returns GNUNET_NO if no data is available, or on error! + * @param desc socket */ -static void -connect_continuation (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +ssize_t +GNUNET_NETWORK_socket_recvfrom_amount (const struct GNUNET_NETWORK_Handle + *desc) { - struct GNUNET_NETWORK_SocketHandle *sock = cls; - unsigned int len; int error; - /* nobody needs to wait for us anymore... */ - sock->connect_task = GNUNET_SCHEDULER_NO_TASK; - /* Note: write-ready does NOT mean connect succeeded, - we need to use getsockopt to be sure */ - len = sizeof (error); - errno = 0; - error = 0; - if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) || - (0 != getsockopt (sock->sock, SOL_SOCKET, SO_ERROR, &error, &len)) || - (error != 0) || (errno != 0)) - { -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Failed to establish TCP connection to `%s'\n", - GNUNET_a2s(sock->addr, sock->addrlen)); + /* How much is there to be read? */ +#ifndef WINDOWS + int pending; + error = ioctl (desc->fd, FIONREAD, &pending); + if (error == 0) +#else + u_long pending; + error = ioctlsocket (desc->fd, FIONREAD, &pending); + if (error != SOCKET_ERROR) #endif - /* connect failed / timed out */ - GNUNET_break (0 == CLOSE (sock->sock)); - sock->sock = -1; - if (GNUNET_SYSERR == try_connect (sock)) - { - /* failed for good */ -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Failed to establish TCP connection, no further addresses to try.\n"); + return pending; + else + return GNUNET_NO; +} + +/** + * Read data from a connected socket (always non-blocking). + * @param desc socket + * @param buffer buffer + * @param length length of buffer + * @param src_addr either the source to recv from, or all zeroes + * to be filled in by recvfrom + * @param addrlen length of the addr + */ +ssize_t +GNUNET_NETWORK_socket_recvfrom (const struct GNUNET_NETWORK_Handle * desc, + void *buffer, size_t length, + struct sockaddr * src_addr, + socklen_t * addrlen) +{ + int ret; + int flags; + flags = 0; + +#ifdef MSG_DONTWAIT + flags |= MSG_DONTWAIT; + #endif - /* connect failed / timed out */ - GNUNET_break (sock->ai_pos == NULL); - freeaddrinfo (sock->ai); - sock->ai = NULL; - return; - } - sock->connect_task = GNUNET_SCHEDULER_add_write (tc->sched, GNUNET_NO, /* abort on shutdown */ - GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT, - sock->sock, - &connect_continuation, - sock); - return; - } - /* connect succeeded! clean up "ai" */ -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connection to `%s' succeeded!\n", - GNUNET_a2s(sock->addr, sock->addrlen)); + ret = recvfrom (desc->fd, buffer, length, flags, src_addr, addrlen); +#ifdef MINGW + if (SOCKET_ERROR == ret) + SetErrnoFromWinsockError (WSAGetLastError ()); +#endif + return ret; +} + + +/** + * Read data from a connected socket (always non-blocking). + * @param desc socket + * @param buffer buffer + * @param length length of buffer + */ +ssize_t +GNUNET_NETWORK_socket_recv (const struct GNUNET_NETWORK_Handle * desc, + void *buffer, size_t length) +{ + int ret; + int flags; + flags = 0; + +#ifdef MSG_DONTWAIT + flags |= MSG_DONTWAIT; #endif - freeaddrinfo (sock->ai); - sock->ai_pos = NULL; - sock->ai = NULL; + ret = recv (desc->fd, buffer, length, flags); +#ifdef MINGW + if (SOCKET_ERROR == ret) + SetErrnoFromWinsockError (WSAGetLastError ()); +#endif + return ret; } /** - * Create a socket 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. + * Send data (always non-blocking). * - * @param sched scheduler to use - * @param hostname name of the host to connect to - * @param port port to connect to - * @param maxbuf maximum write buffer size for the socket (use - * 0 for sockets that need no write buffers, such as listen sockets) - * @return the socket handle + * @param desc socket + * @param buffer data to send + * @param length size of the buffer + * @return number of bytes sent, GNUNET_SYSERR on error */ -struct GNUNET_NETWORK_SocketHandle * -GNUNET_NETWORK_socket_create_from_connect (struct GNUNET_SCHEDULER_Handle - *sched, const char *hostname, - uint16_t port, size_t maxbuf) +ssize_t +GNUNET_NETWORK_socket_send (const struct GNUNET_NETWORK_Handle * desc, + const void *buffer, size_t length) { - struct GNUNET_NETWORK_SocketHandle *ret; - - ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf); - ret->sock = -1; - ret->sched = sched; - ret->write_buffer = (char *) &ret[1]; - ret->write_buffer_size = maxbuf; - ret->port = port; - ret->af_fam_offset = sizeof (address_families) / sizeof(address_families[0]); - ret->hostname = GNUNET_strdup (hostname); - if (GNUNET_SYSERR == try_connect (ret)) - { - if (NULL != ret->ai) - freeaddrinfo (ret->ai); - GNUNET_free (ret->hostname); - GNUNET_free (ret); - return NULL; - } - ret->connect_task = GNUNET_SCHEDULER_add_write (sched, GNUNET_NO, /* abort on shutdown */ - GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT, - ret->sock, - &connect_continuation, ret); - return ret; + int ret; + int flags; + flags = 0; + +#ifdef MSG_DONTWAIT + flags |= MSG_DONTWAIT; + +#endif /* */ +#ifdef MSG_NOSIGNAL + flags |= MSG_NOSIGNAL; + +#endif /* */ + ret = send (desc->fd, buffer, length, flags); +#ifdef MINGW + if (SOCKET_ERROR == ret) + SetErrnoFromWinsockError (WSAGetLastError ()); + +#endif /* */ + return ret; } /** - * Create a socket 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. + * Send data to a particular destination (always non-blocking). + * This function only works for UDP sockets. * - * @param sched scheduler to use - * @param af_family address family to use - * @param serv_addr server address - * @param addrlen length of server address - * @param maxbuf maximum write buffer size for the socket (use - * 0 for sockets that need no write buffers, such as listen sockets) - * @return the socket handle + * @param desc socket + * @param message data to send + * @param length size of the data + * @param dest_addr destination address + * @param dest_len length of address + * @return number of bytes sent, GNUNET_SYSERR on error */ -struct GNUNET_NETWORK_SocketHandle * -GNUNET_NETWORK_socket_create_from_sockaddr (struct GNUNET_SCHEDULER_Handle - *sched, int af_family, - const struct sockaddr *serv_addr, - socklen_t addrlen, size_t maxbuf) +ssize_t +GNUNET_NETWORK_socket_sendto (const struct GNUNET_NETWORK_Handle * desc, + const void *message, size_t length, + const struct sockaddr * dest_addr, + socklen_t dest_len) { - int s; - struct GNUNET_NETWORK_SocketHandle *ret; + int ret; + int flags; + flags = 0; - s = SOCKET (af_family, SOCK_STREAM, 0); - if (s == -1) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING | - GNUNET_ERROR_TYPE_BULK, "socket"); - return NULL; - } -#ifndef MINGW - if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "fcntl"); +#ifdef MSG_DONTWAIT + flags |= MSG_DONTWAIT; #endif - if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO)) - { - /* we'll treat this one as fatal */ - GNUNET_break (0 == CLOSE (s)); - return NULL; - } -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Trying to connect to `%s'\n"), - GNUNET_a2s(serv_addr, addrlen)); +#ifdef MSG_NOSIGNAL + flags |= MSG_NOSIGNAL; +#endif + ret = sendto (desc->fd, message, length, flags, dest_addr, dest_len); +#ifdef MINGW + if (SOCKET_ERROR == ret) + SetErrnoFromWinsockError (WSAGetLastError ()); #endif - if ((0 != CONNECT (s, serv_addr, addrlen)) && (errno != EINPROGRESS)) - { - /* maybe refused / unsupported address, try next */ - GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect"); - GNUNET_break (0 == CLOSE (s)); - return NULL; - } - ret = GNUNET_NETWORK_socket_create_from_existing (sched, s, maxbuf); - ret->addr = GNUNET_malloc (addrlen); - memcpy (ret->addr, serv_addr, addrlen); - ret->addrlen = addrlen; return ret; } /** - * Check if socket is valid (no fatal errors have happened so far). - * Note that a socket that is still trying to connect is considered - * valid. - * - * @param sock socket to check - * @return GNUNET_YES if valid, GNUNET_NO otherwise + * Set socket option + * @param fd socket + * @param level protocol level of the option + * @param option_name option identifier + * @param option_value value to set + * @param option_len size of option_value + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ int -GNUNET_NETWORK_socket_check (struct GNUNET_NETWORK_SocketHandle *sock) +GNUNET_NETWORK_socket_setsockopt (struct GNUNET_NETWORK_Handle *fd, + int level, int option_name, + const void *option_value, + socklen_t option_len) { - if (sock->ai != NULL) - return GNUNET_YES; /* still trying to connect */ - return (sock->sock == -1) ? GNUNET_NO : GNUNET_YES; + int ret; + + ret = setsockopt (fd->fd, level, option_name, option_value, option_len); +#ifdef MINGW + if (SOCKET_ERROR == ret) + SetErrnoFromWinsockError (WSAGetLastError ()); +#endif + return ret == 0 ? GNUNET_OK : GNUNET_SYSERR; } /** - * Scheduler let us know that the connect task is finished (or was - * cancelled due to shutdown). Now really clean up. + * Create a new socket. Configure it for non-blocking IO and + * mark it as non-inheritable to child processes (set the + * close-on-exec flag). + * + * @param domain domain of the socket + * @param type socket type + * @param protocol network protocol + * @return new socket, NULL on error */ -static void -destroy_continuation (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +struct GNUNET_NETWORK_Handle * +GNUNET_NETWORK_socket_create (int domain, int type, int protocol) { - struct GNUNET_NETWORK_SocketHandle *sock = cls; - GNUNET_NETWORK_TransmitReadyNotify notify; - - if (sock->write_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_add_after (sock->sched, - GNUNET_YES, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sock->write_task, - &destroy_continuation, sock); - return; - } - if (sock->sock != -1) + struct GNUNET_NETWORK_Handle *ret; + ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle)); + ret->af = domain; + ret->fd = socket (domain, type, protocol); + if (INVALID_SOCKET == ret->fd) { -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down socket.\n"); +#ifdef MINGW + SetErrnoFromWinsockError (WSAGetLastError ()); #endif - SHUTDOWN (sock->sock, SHUT_RDWR); + GNUNET_free (ret); + return NULL; } - if (sock->read_task != GNUNET_SCHEDULER_NO_TASK) + +#ifndef MINGW + if (ret->fd >= FD_SETSIZE) { - GNUNET_SCHEDULER_add_after (sock->sched, - GNUNET_YES, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sock->read_task, - &destroy_continuation, sock); - return; + GNUNET_break (0 == close (ret->fd)); + GNUNET_free (ret); + errno = EMFILE; + return NULL; } - if (NULL != (notify = sock->nth.notify_ready)) + +#endif + if (GNUNET_SYSERR == socket_set_blocking (ret, GNUNET_NO)) { - sock->nth.notify_ready = NULL; - notify (sock->nth.notify_ready_cls, 0, NULL); - if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); - sock->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK; - } + /* we might want to treat this one as fatal... */ + GNUNET_break (0); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ret)); + return NULL; } - if (sock->sock != -1) - GNUNET_break (0 == CLOSE (sock->sock)); - GNUNET_free_non_null (sock->addr); - if (sock->ai != NULL) - freeaddrinfo (sock->ai); - GNUNET_free_non_null (sock->hostname); - GNUNET_free (sock); + +#ifndef MINGW + if (GNUNET_OK != socket_set_inheritable (ret)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "socket_set_inheritable"); +#endif +#ifdef DARWIN + socket_set_nosigpipe (ret); +#endif + if ( (type == SOCK_STREAM) +#ifdef AF_UNIX + && (domain != AF_UNIX) +#endif + ) + socket_set_nodelay (ret); + return ret; } /** - * Close the socket and free associated resources. Pending - * transmissions are simply dropped. A pending receive call will be - * called with an error code of "EPIPE". - * - * @param sock socket to destroy + * Shut down socket operations + * @param desc socket + * @param how type of shutdown + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ -void -GNUNET_NETWORK_socket_destroy (struct GNUNET_NETWORK_SocketHandle *sock) +int +GNUNET_NETWORK_socket_shutdown (struct GNUNET_NETWORK_Handle *desc, int how) { - if (sock->write_buffer_off == 0) - sock->ai_pos = NULL; /* if we're still trying to connect and have - no message pending, stop trying! */ - GNUNET_assert (sock->sched != NULL); - GNUNET_SCHEDULER_add_after (sock->sched, - GNUNET_YES, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sock->connect_task, - &destroy_continuation, sock); + int ret; + + ret = shutdown (desc->fd, how); +#ifdef MINGW + if (ret != 0) + SetErrnoFromWinsockError (WSAGetLastError ()); +#endif + return ret == 0 ? GNUNET_OK : GNUNET_SYSERR; } + /** - * Tell the receiver callback that a timeout was reached. + * Reset FD set + * @param fds fd set */ -static void -signal_timeout (struct GNUNET_NETWORK_SocketHandle *sh) +void +GNUNET_NETWORK_fdset_zero (struct GNUNET_NETWORK_FDSet *fds) { - GNUNET_NETWORK_Receiver receiver; - -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Network signals time out to receiver!\n"); + FD_ZERO (&fds->sds); + fds->nsds = 0; +#ifdef MINGW + GNUNET_CONTAINER_slist_clear (fds->handles); #endif - GNUNET_assert (NULL != (receiver = sh->receiver)); - sh->receiver = NULL; - receiver (sh->receiver_cls, NULL, 0, NULL, 0, 0); +} + +/** + * Add a socket to the FD set + * @param fds fd set + * @param desc socket to add + */ +void +GNUNET_NETWORK_fdset_set (struct GNUNET_NETWORK_FDSet *fds, + const struct GNUNET_NETWORK_Handle *desc) +{ + FD_SET (desc->fd, &fds->sds); + if (desc->fd + 1 > fds->nsds) + fds->nsds = desc->fd + 1; } /** - * Tell the receiver callback that we had an IO error. + * Check whether a socket is part of the fd set + * @param fds fd set + * @param desc socket + * @return 0 if the FD is not set */ -static void -signal_error (struct GNUNET_NETWORK_SocketHandle *sh, int errcode) +int +GNUNET_NETWORK_fdset_isset (const struct GNUNET_NETWORK_FDSet *fds, + const struct GNUNET_NETWORK_Handle *desc) { - GNUNET_NETWORK_Receiver receiver; - GNUNET_assert (NULL != (receiver = sh->receiver)); - sh->receiver = NULL; - receiver (sh->receiver_cls, NULL, 0, sh->addr, sh->addrlen, errcode); + return FD_ISSET (desc->fd, &fds->sds); } /** - * This function is called once we either timeout - * or have data ready to read. + * Add one fd set to another + * @param dst the fd set to add to + * @param src the fd set to add from */ -static void -receive_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +void +GNUNET_NETWORK_fdset_add (struct GNUNET_NETWORK_FDSet *dst, + const struct GNUNET_NETWORK_FDSet *src) { - struct GNUNET_NETWORK_SocketHandle *sh = cls; - struct GNUNET_TIME_Absolute now; - char buffer[sh->max]; - ssize_t ret; - GNUNET_NETWORK_Receiver receiver; - - sh->read_task = GNUNET_SCHEDULER_NO_TASK; - now = GNUNET_TIME_absolute_get (); - if ((now.value > sh->receive_timeout.value) || - (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) || - (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))) - { -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receive encounters error: time out...\n"); + int nfds; + for (nfds = src->nsds; nfds > 0; nfds--) + if (FD_ISSET (nfds, &src->sds)) + + { + FD_SET (nfds, &dst->sds); + if (nfds + 1 > dst->nsds) + dst->nsds = nfds + 1; + } +#ifdef MINGW + GNUNET_CONTAINER_slist_append (dst->handles, src->handles); #endif - signal_timeout (sh); - return; - } - if (sh->sock == -1) - { - /* connect failed for good */ -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receive encounters error, socket closed...\n"); -#endif - signal_error (sh, ECONNREFUSED); - return; - } - GNUNET_assert (FD_ISSET (sh->sock, tc->read_ready)); -RETRY: - ret = RECV (sh->sock, buffer, sh->max, -#ifndef MINGW - // FIXME MINGW - MSG_DONTWAIT -#else - 0 -#endif - ); - if (ret == -1) - { - if (errno == EINTR) - goto RETRY; -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Error receiving: %s\n", STRERROR (errno)); -#endif - signal_error (sh, errno); - return; - } -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "receive_ready read %u/%u bytes from `%s'!\n", - (unsigned int) ret, - sh->max, - GNUNET_a2s(sh->addr, sh->addrlen)); -#endif - GNUNET_assert (NULL != (receiver = sh->receiver)); - sh->receiver = NULL; - receiver (sh->receiver_cls, buffer, ret, sh->addr, sh->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). + * Copy one fd set to another + * + * @param to destination + * @param from source */ -static void -receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +void +GNUNET_NETWORK_fdset_copy (struct GNUNET_NETWORK_FDSet *to, + const struct GNUNET_NETWORK_FDSet *from) { - struct GNUNET_NETWORK_SocketHandle *sh = cls; - struct GNUNET_TIME_Absolute now; + FD_COPY (&from->sds, &to->sds); + to->nsds = from->nsds; - sh->read_task = GNUNET_SCHEDULER_NO_TASK; - if ((sh->sock == -1) && - (sh->connect_task == GNUNET_SCHEDULER_NO_TASK)) - { - /* not connected and no longer trying */ -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receive encounters error, socket closed...\n"); -#endif - signal_error (sh, ECONNREFUSED); - return; - } - now = GNUNET_TIME_absolute_get (); - if ((now.value > sh->receive_timeout.value) || - (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))) - { -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receive encounters error: time out...\n"); +#ifdef MINGW + GNUNET_CONTAINER_slist_clear (to->handles); + GNUNET_CONTAINER_slist_append (to->handles, from->handles); #endif - signal_timeout (sh); - return; - } - if (sh->connect_task != GNUNET_SCHEDULER_NO_TASK) - { - /* connect was retried */ - sh->read_task = GNUNET_SCHEDULER_add_after (tc->sched, - GNUNET_YES, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sh->connect_task, - &receive_again, sh); - return; - } - /* connect succeeded, wait for data! */ - sh->read_task = GNUNET_SCHEDULER_add_read (tc->sched, - GNUNET_YES, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sh->connect_task, - GNUNET_TIME_absolute_get_remaining - (sh->receive_timeout), - sh->sock, &receive_ready, - sh); } +int +GNUNET_NETWORK_get_fd (struct GNUNET_NETWORK_Handle *desc) +{ + return desc->fd; +} /** - * Receive data from the given socket. Note that this function will - * call "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 - * call receive again until the receiver callback has been invoked). + * Copy a native fd set * - * @param sched scheduler to use - * @param sock socket handle - * @param max maximum number of bytes to read - * @param timeout maximum amount of time to wait (use -1 for "forever") - * @param receiver function to call with received data - * @param receiver_cls closure for receiver - * @return scheduler task ID used for receiving, GNUNET_SCHEDULER_NO_TASK on error + * @param to destination + * @param from native source set + * @param nfds the biggest socket number in from + 1 */ -GNUNET_SCHEDULER_TaskIdentifier -GNUNET_NETWORK_receive (struct GNUNET_NETWORK_SocketHandle *sock, - size_t max, - struct GNUNET_TIME_Relative timeout, - GNUNET_NETWORK_Receiver receiver, void *receiver_cls) +void +GNUNET_NETWORK_fdset_copy_native (struct GNUNET_NETWORK_FDSet *to, + const fd_set * from, int nfds) { - struct GNUNET_SCHEDULER_TaskContext tc; - - GNUNET_assert ((sock->read_task == GNUNET_SCHEDULER_NO_TASK) && - (sock->receiver == NULL)); - sock->receiver = receiver; - sock->receiver_cls = receiver_cls; - sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); - sock->max = max; - memset (&tc, 0, sizeof (tc)); - tc.sched = sock->sched; - tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE; - receive_again (sock, &tc); - return sock->read_task; + FD_COPY (from, &to->sds); + to->nsds = nfds; } /** - * Cancel receive job on the given socket. Note that the - * receiver callback must not have been called yet in order - * for the cancellation to be valid. + * Set a native fd in a set * - * @param sock socket handle - * @param task task identifier returned from the receive call - * @return closure of the original receiver callback + * @param to destination + * @param nfd native FD to set */ -void * -GNUNET_NETWORK_receive_cancel (struct GNUNET_NETWORK_SocketHandle *sock, - GNUNET_SCHEDULER_TaskIdentifier task) +void GNUNET_NETWORK_fdset_set_native (struct GNUNET_NETWORK_FDSet *to, + int nfd) { - GNUNET_assert (sock->read_task == task); - GNUNET_assert (sock == GNUNET_SCHEDULER_cancel (sock->sched, task)); - sock->read_task = GNUNET_SCHEDULER_NO_TASK; - sock->receiver = NULL; - return sock->receiver_cls; + FD_SET (nfd, &to->sds); + to->nsds = GNUNET_MAX (nfd + 1, to->nsds); } /** - * Try to call the transmit notify method (check if we do - * have enough space available first)! + * Test native fd in a set * - * @param sock socket for which we should do this processing - * @return GNUNET_YES if we were able to call notify + * @param to set to test, NULL for empty set + * @param nfd native FD to test, or -1 for none + * @return GNUNET_YES if FD is set in the set */ -static int -process_notify (struct GNUNET_NETWORK_SocketHandle *sock) +int +GNUNET_NETWORK_fdset_test_native (const struct GNUNET_NETWORK_FDSet *to, + int nfd) { - size_t used; - size_t avail; - size_t size; - GNUNET_NETWORK_TransmitReadyNotify notify; - - GNUNET_assert (sock->write_task == GNUNET_SCHEDULER_NO_TASK); - if (NULL == (notify = sock->nth.notify_ready)) - return GNUNET_NO; - used = sock->write_buffer_off - sock->write_buffer_pos; - avail = sock->write_buffer_size - used; - size = sock->nth.notify_size; - if (sock->nth.notify_size > avail) + if ( (nfd == -1) || (to == NULL) ) return GNUNET_NO; - sock->nth.notify_ready = NULL; - if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); - sock->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK; - } - 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; - } - GNUNET_assert (sock->write_buffer_size - sock->write_buffer_off >= size); - size = notify (sock->nth.notify_ready_cls, - sock->write_buffer_size - sock->write_buffer_off, - &sock->write_buffer[sock->write_buffer_off]); - sock->write_buffer_off += size; - return GNUNET_YES; + return FD_ISSET (nfd, &to->sds) ? GNUNET_YES : GNUNET_NO; } /** - * 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. + * Add a file handle to the fd set + * @param fds fd set + * @param h the file handle to add */ -static void -transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +void +GNUNET_NETWORK_fdset_handle_set (struct GNUNET_NETWORK_FDSet *fds, + const struct GNUNET_DISK_FileHandle *h) { - struct GNUNET_NETWORK_SocketHandle *sock = cls; - GNUNET_NETWORK_TransmitReadyNotify notify; +#ifdef MINGW + GNUNET_CONTAINER_slist_add (fds->handles, + GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, + h, sizeof (struct GNUNET_DISK_FileHandle)); + +#else + int fd; + GNUNET_DISK_internal_file_handle_ (h, &fd, sizeof (int)); + FD_SET (fd, &fds->sds); + if (fd + 1 > fds->nsds) + fds->nsds = fd + 1; -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmit fails, time out reached.\n"); #endif - notify = sock->nth.notify_ready; - sock->nth.notify_ready = NULL; - notify (sock->nth.notify_ready_cls, 0, NULL); } -static void -transmit_error (struct GNUNET_NETWORK_SocketHandle *sock) +/** + * Check if a file handle is part of an fd set + * @param fds fd set + * @param h file handle + * @return GNUNET_YES if the file handle is part of the set + */ +int +GNUNET_NETWORK_fdset_handle_isset (const struct GNUNET_NETWORK_FDSet *fds, + const struct GNUNET_DISK_FileHandle *h) { - if (sock->nth.notify_ready == NULL) - return; /* nobody to tell about it */ - if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); - sock->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK; - } - transmit_timeout (sock, NULL); + +#ifdef MINGW + return GNUNET_CONTAINER_slist_contains (fds->handles, h, + sizeof (struct GNUNET_DISK_FileHandle)); +#else + return FD_ISSET (h->fd, &fds->sds); +#endif } /** - * 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. + * Checks if two fd sets overlap + * @param fds1 first fd set + * @param fds2 second fd set + * @return GNUNET_YES if they do overlap, GNUNET_NO otherwise */ -static void -transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +int +GNUNET_NETWORK_fdset_overlap (const struct GNUNET_NETWORK_FDSet *fds1, + const struct GNUNET_NETWORK_FDSet *fds2) { - struct GNUNET_NETWORK_SocketHandle *sock = cls; - ssize_t ret; - size_t have; +#ifndef MINGW + int nfds; - GNUNET_assert (sock->write_task != GNUNET_SCHEDULER_NO_TASK); - sock->write_task = GNUNET_SCHEDULER_NO_TASK; - if (sock->connect_task != GNUNET_SCHEDULER_NO_TASK) + nfds = fds1->nsds; + if (nfds > fds2->nsds) + nfds = fds2->nsds; + while (nfds > 0) { - /* still waiting for connect */ - GNUNET_assert (sock->write_task == - GNUNET_SCHEDULER_NO_TASK); - sock->write_task = - GNUNET_SCHEDULER_add_delayed (tc->sched, GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sock->connect_task, - GNUNET_TIME_UNIT_ZERO, &transmit_ready, - sock); - return; + nfds--; + if (FD_ISSET (nfds, &fds1->sds) && FD_ISSET (nfds, &fds2->sds)) + return GNUNET_YES; } - if ( (sock->sock == -1) || - ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) && - (0 == (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) && - (!FD_ISSET (sock->sock, tc->write_ready))) ) +#else + struct GNUNET_CONTAINER_SList_Iterator *it; + struct GNUNET_DISK_FileHandle *h; + int i; + int j; + + /*This code is somewhat hacky, we are not supposed to know what's + inside of fd_set; also the O(n^2) is really bad... */ + + for (i = 0; i < fds1->sds.fd_count; i++) + { + for (j = 0; j < fds2->sds.fd_count; j++) { -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Could not satisfy pending transmission request, socket closed or connect failed.\n")); -#endif - if (-1 != sock->sock) - { - SHUTDOWN (sock->sock, SHUT_RDWR); - GNUNET_break (0 == CLOSE (sock->sock)); - sock->sock = -1; - } - transmit_error (sock); - return; /* connect failed for good, we're finished */ + if (fds1->sds.fd_array[i] == fds2->sds.fd_array[j]) + return GNUNET_YES; } - if ((tc->write_ready == NULL) || (!FD_ISSET (sock->sock, tc->write_ready))) + } + it = GNUNET_CONTAINER_slist_begin (fds1->handles); + while (GNUNET_CONTAINER_slist_end (it) != GNUNET_YES) { - /* special circumstances (in particular, - PREREQ_DONE after connect): not yet ready to write, - but no "fatal" error either. Hence retry. */ - goto SCHEDULE_WRITE; + h = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (it, NULL); + if (GNUNET_CONTAINER_slist_contains + (fds2->handles, h, sizeof (struct GNUNET_DISK_FileHandle))) + { + GNUNET_CONTAINER_slist_iter_destroy (it); + return GNUNET_YES; + } + GNUNET_CONTAINER_slist_next (it); } - GNUNET_assert (sock->write_buffer_off >= sock->write_buffer_pos); - process_notify (sock); - have = sock->write_buffer_off - sock->write_buffer_pos; - if (have == 0) + GNUNET_CONTAINER_slist_iter_destroy (it); +#endif + return GNUNET_NO; +} + + +/** + * Creates an fd set + * @return a new fd set + */ +struct GNUNET_NETWORK_FDSet * +GNUNET_NETWORK_fdset_create () +{ + struct GNUNET_NETWORK_FDSet *fds; + fds = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_FDSet)); +#ifdef MINGW + fds->handles = GNUNET_CONTAINER_slist_create (); +#endif + GNUNET_NETWORK_fdset_zero (fds); + return fds; +} + + +/** + * Releases the associated memory of an fd set + * @param fds fd set + */ +void +GNUNET_NETWORK_fdset_destroy (struct GNUNET_NETWORK_FDSet *fds) +{ +#ifdef MINGW + GNUNET_CONTAINER_slist_destroy (fds->handles); +#endif + GNUNET_free (fds); +} + +/** + * Check if sockets meet certain conditions + * @param rfds set of sockets to be checked for readability + * @param wfds set of sockets to be checked for writability + * @param efds set of sockets to be checked for exceptions + * @param timeout relative value when to return + * @return number of selected sockets, GNUNET_SYSERR on error + */ +int +GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds, + struct GNUNET_NETWORK_FDSet *wfds, + struct GNUNET_NETWORK_FDSet *efds, + const struct GNUNET_TIME_Relative timeout) +{ + int nfds; +#ifdef MINGW + int handles; +#endif + nfds = 0; +#ifdef MINGW + handles = 0; +#endif + if (NULL != rfds) { - /* no data ready for writing, terminate write loop */ - return; + nfds = rfds->nsds; +#ifdef MINGW + handles = GNUNET_CONTAINER_slist_count (rfds->handles); +#endif } -RETRY: - ret = SEND (sock->sock, - &sock->write_buffer[sock->write_buffer_pos], - have, -#ifndef MINGW - // FIXME NILS - MSG_DONTWAIT | MSG_NOSIGNAL -#else - 0 + if (NULL != wfds) + { + nfds = GNUNET_MAX (nfds, wfds->nsds); +#ifdef MINGW + handles += GNUNET_CONTAINER_slist_count (wfds->handles); #endif - ); - if (ret == -1) + } + if (NULL != efds) { - if (errno == EINTR) - goto RETRY; -#if DEBUG_NETWORK - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send"); + nfds = GNUNET_MAX (nfds, efds->nsds); +#ifdef MINGW + handles += GNUNET_CONTAINER_slist_count (efds->handles); #endif - SHUTDOWN (sock->sock, SHUT_RDWR); - GNUNET_break (0 == CLOSE (sock->sock)); - sock->sock = -1; - transmit_error (sock); - return; } -#if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "transmit_ready transmitted %u/%u bytes to `%s'\n", - (unsigned int) ret, - have, - GNUNET_a2s(sock->addr, sock->addrlen)); + + struct timeval tv; + tv.tv_sec = timeout.rel_value / GNUNET_TIME_UNIT_SECONDS.rel_value; + tv.tv_usec = + 1000 * (timeout.rel_value - (tv.tv_sec * GNUNET_TIME_UNIT_SECONDS.rel_value)); + if ((nfds == 0) && (timeout.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value) +#ifdef MINGW + && handles == 0 #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; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Fatal internal logic error, process hangs in `%s' (abort with CTRL-C)!\n"), + "select"); + GNUNET_break (0); } - if ((sock->write_buffer_off == 0) && (NULL == sock->nth.notify_ready)) - return; /* all data sent! */ - /* not done writing, schedule more */ -SCHEDULE_WRITE: - if (sock->write_task == GNUNET_SCHEDULER_NO_TASK) - sock->write_task = - GNUNET_SCHEDULER_add_write (tc->sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_TIME_absolute_get_remaining (sock->nth.transmit_timeout), - sock->sock, &transmit_ready, sock); -} +#ifndef MINGW + return select (nfds, + (rfds != NULL) ? &rfds->sds : NULL, + (wfds != NULL) ? &wfds->sds : NULL, + (efds != NULL) ? &efds->sds : NULL, + (timeout.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value) + ? NULL : &tv); +#else + DWORD limit; + fd_set sock_read, sock_write, sock_except; + fd_set aread, awrite, aexcept; + struct GNUNET_CONTAINER_SList *handles_read, *handles_write, + *handles_except; -/** - * 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. - * - * @param sock socket - * @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)? - * @param notify function to call - * @param notify_cls closure for notify - * @return non-NULL if the notify callback was queued, - * NULL if we are already going to notify someone else (busy) - */ -struct GNUNET_NETWORK_TransmitHandle * -GNUNET_NETWORK_notify_transmit_ready (struct GNUNET_NETWORK_SocketHandle - *sock, size_t size, - struct GNUNET_TIME_Relative timeout, - GNUNET_NETWORK_TransmitReadyNotify - notify, void *notify_cls) -{ - if (sock->nth.notify_ready != NULL) - return NULL; - GNUNET_assert (notify != NULL); - GNUNET_assert (sock->write_buffer_size >= size); + int i; + struct timeval tvslice; + int retcode; + DWORD ms_total; + +#define SAFE_FD_ISSET(fd, set) (set != NULL && FD_ISSET(fd, set)) + + /* calculate how long we need to wait in milliseconds */ + if (timeout.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value) + ms_total = INFINITE; + + else + ms_total = timeout.rel_value / GNUNET_TIME_UNIT_MILLISECONDS.rel_value; + + /* select() may be used as a portable way to sleep */ + if (!(rfds || wfds || efds)) - if ((sock->sock == -1) && - (sock->connect_task == GNUNET_SCHEDULER_NO_TASK)) { + Sleep (ms_total); + return 0; + } + + handles_read = GNUNET_CONTAINER_slist_create (); + handles_write = GNUNET_CONTAINER_slist_create (); + handles_except = GNUNET_CONTAINER_slist_create (); + + if (rfds) + sock_read = rfds->sds; + else + FD_ZERO (&sock_read); + if (wfds) + sock_write = wfds->sds; + else + FD_ZERO (&sock_write); + if (efds) + sock_except = efds->sds; + else + FD_ZERO (&sock_except); + + /* multiplex between winsock select() and waiting on the handles */ + FD_ZERO (&aread); + FD_ZERO (&awrite); + FD_ZERO (&aexcept); + limit = GetTickCount () + ms_total; + + do + { + retcode = 0; + if (nfds > 0) + + { + + /* overwrite the zero'd sets here; the select call + * will clear those that are not active */ + FD_COPY (&sock_read, &aread); + FD_COPY (&sock_write, &awrite); + FD_COPY (&sock_except, &aexcept); + tvslice.tv_sec = 0; + tvslice.tv_usec = 100000; + if ((retcode = + select (nfds + 1, &aread, &awrite, &aexcept, + &tvslice)) == SOCKET_ERROR) + + { + SetErrnoFromWinsockError (WSAGetLastError ()); + if (errno == ENOTSOCK) + errno = EBADF; + #if DEBUG_NETWORK - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmission request of size %u fails, connection failed.\n", - size); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select"); + #endif - notify (notify_cls, 0, NULL); - return &sock->nth; + goto select_loop_end; + } + } + + /* Poll read pipes */ + if (rfds) + + { + struct GNUNET_CONTAINER_SList_Iterator *i; + for (i = GNUNET_CONTAINER_slist_begin (rfds->handles); + GNUNET_CONTAINER_slist_end (i) != GNUNET_YES; + GNUNET_CONTAINER_slist_next (i)) + + { + struct GNUNET_DISK_FileHandle *fh; + DWORD dwBytes; + fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (i, NULL); + if (fh->type == GNUNET_PIPE) + { + if (!PeekNamedPipe (fh->h, NULL, 0, NULL, &dwBytes, NULL)) + { + retcode = -1; + SetErrnoFromWinError (GetLastError ()); + + #if DEBUG_NETWORK + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "PeekNamedPipe"); + + #endif + goto select_loop_end; + } + else if (dwBytes) + + { + GNUNET_CONTAINER_slist_add (handles_read, + GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, + fh, sizeof (struct GNUNET_DISK_FileHandle)); + retcode++; + } + } + else + { + /* Should we wait for more bytes to read here (in case of previous EOF)? */ + GNUNET_CONTAINER_slist_add (handles_read, + GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, + fh, sizeof (struct GNUNET_DISK_FileHandle)); + } + } + GNUNET_CONTAINER_slist_iter_destroy (i); + } + + /* Poll for faulty pipes */ + if (efds) + + { + struct GNUNET_CONTAINER_SList_Iterator *i; + for (i = GNUNET_CONTAINER_slist_begin (efds->handles); + GNUNET_CONTAINER_slist_end (i) != GNUNET_YES; + GNUNET_CONTAINER_slist_next (i)) + + { + struct GNUNET_DISK_FileHandle *fh; + DWORD dwBytes; + + fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (i, NULL); + if (fh->type == GNUNET_PIPE) + { + if (!PeekNamedPipe (fh->h, NULL, 0, NULL, &dwBytes, NULL)) + + { + GNUNET_CONTAINER_slist_add (handles_except, + GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, + fh, sizeof (struct GNUNET_DISK_FileHandle)); + retcode++; + } + } + } + GNUNET_CONTAINER_slist_iter_destroy (i); + } + + if (wfds) + { + GNUNET_CONTAINER_slist_append (handles_write, wfds->handles); + retcode += GNUNET_CONTAINER_slist_count (wfds->handles); + } + + /* Check for closed sockets */ + for (i = 0; i < nfds; i++) + + { + if (SAFE_FD_ISSET (i, &sock_read)) + + { + struct sockaddr addr; + int len; + if (getpeername (i, &addr, &len) == SOCKET_ERROR) + + { + int err, len; + len = sizeof (err); + if (getsockopt + (i, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == 0 + && err == WSAENOTCONN) + + { + if (!SAFE_FD_ISSET (i, &aread)) + + { + FD_SET (i, &aread); + retcode++; + } + } + } + } + } + select_loop_end: + if (retcode == 0 && nfds == 0) + Sleep (GNUNET_MIN (100, limit - GetTickCount ())); } - 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); - sock->nth.timeout_task = GNUNET_SCHEDULER_add_delayed (sock->sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_SCHEDULER_NO_TASK, - timeout, - &transmit_timeout, - sock); - if (sock->write_task == GNUNET_SCHEDULER_NO_TASK) + while (retcode == 0 && (ms_total == INFINITE || GetTickCount () < limit)); + + if (retcode != -1) { - if (sock->connect_task == GNUNET_SCHEDULER_NO_TASK) - sock->write_task = GNUNET_SCHEDULER_add_write (sock->sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_TIME_absolute_get_remaining (sock->nth.transmit_timeout), - sock->sock, - &transmit_ready, sock); - else - sock->write_task = GNUNET_SCHEDULER_add_delayed (sock->sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sock->connect_task, - GNUNET_TIME_UNIT_ZERO, - &transmit_ready, sock); + if (rfds) + { + GNUNET_NETWORK_fdset_zero (rfds); + GNUNET_NETWORK_fdset_copy_native (rfds, &aread, retcode); + GNUNET_CONTAINER_slist_clear (rfds->handles); + GNUNET_CONTAINER_slist_append (rfds->handles, handles_read); + } + if (wfds) + { + GNUNET_NETWORK_fdset_zero (wfds); + GNUNET_NETWORK_fdset_copy_native (wfds, &awrite, retcode); + GNUNET_CONTAINER_slist_clear (wfds->handles); + GNUNET_CONTAINER_slist_append (wfds->handles, handles_write); + } + if (efds) + { + GNUNET_NETWORK_fdset_zero (efds); + GNUNET_NETWORK_fdset_copy_native (efds, &aexcept, retcode); + GNUNET_CONTAINER_slist_clear (efds->handles); + GNUNET_CONTAINER_slist_append (efds->handles, handles_except); + } } - return &sock->nth; -} + GNUNET_CONTAINER_slist_destroy (handles_read); + GNUNET_CONTAINER_slist_destroy (handles_write); + GNUNET_CONTAINER_slist_destroy (handles_except); -/** - * Cancel the specified transmission-ready - * notification. - */ -void -GNUNET_NETWORK_notify_transmit_ready_cancel (struct - GNUNET_NETWORK_TransmitHandle *h) -{ - GNUNET_assert (h->notify_ready != NULL); - GNUNET_SCHEDULER_cancel (h->sh->sched, h->timeout_task); - h->timeout_task = GNUNET_SCHEDULER_NO_TASK; - h->notify_ready = NULL; + return retcode; +#endif } -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif +/* end of network.c */