From: Christian Grothoff Date: Thu, 1 Oct 2009 16:23:25 +0000 (+0000) Subject: moving resolver to util, making DNS lookups asynchronous in util X-Git-Tag: initial-import-from-subversion-38251~23443 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=f0aa7bc0a136b1e436018ef72e1c814f04c33379;p=oweals%2Fgnunet.git moving resolver to util, making DNS lookups asynchronous in util --- diff --git a/configure.ac b/configure.ac index 5b4e472b8..d692a1de4 100644 --- a/configure.ac +++ b/configure.ac @@ -652,7 +652,6 @@ src/include/Makefile src/include/gnunet_directories.h src/hostlist/Makefile src/peerinfo/Makefile -src/resolver/Makefile src/statistics/Makefile src/template/Makefile src/testing/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 7e92437c6..9cb49e2cc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,6 @@ SUBDIRS = \ fragmentation \ hello \ peerinfo \ - resolver \ statistics \ datacache \ datastore \ diff --git a/src/include/gnunet_connection_lib.h b/src/include/gnunet_connection_lib.h index d83da71ce..364413adc 100644 --- a/src/include/gnunet_connection_lib.h +++ b/src/include/gnunet_connection_lib.h @@ -134,6 +134,7 @@ struct GNUNET_CONNECTION_Handle * yet been established. This function only creates TCP connections. * * @param sched scheduler to use + * @param cfg configuration 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 @@ -141,12 +142,11 @@ struct GNUNET_CONNECTION_Handle * @return the socket handle */ struct GNUNET_CONNECTION_Handle - *GNUNET_CONNECTION_create_from_connect (struct - GNUNET_SCHEDULER_Handle - *sched, - const char *hostname, - uint16_t port, - size_t maxbuf); + *GNUNET_CONNECTION_create_from_connect (struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *hostname, + uint16_t port, + size_t maxbuf); @@ -220,9 +220,8 @@ void GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle * @param timeout maximum amount of time to wait * @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 */ -GNUNET_SCHEDULER_TaskIdentifier +void GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *sock, size_t max, struct GNUNET_TIME_Relative timeout, @@ -236,14 +235,11 @@ GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle * for the cancellation to be valid. * * @param sock socket handle - * @param task task identifier returned from the receive call - * @return closure of the original receiver callback + * @return closure of the original receiver callback closure */ void *GNUNET_CONNECTION_receive_cancel (struct - GNUNET_CONNECTION_Handle - *sock, - GNUNET_SCHEDULER_TaskIdentifier - task); + GNUNET_CONNECTION_Handle + *sock); /** diff --git a/src/include/gnunet_resolver_service.h b/src/include/gnunet_resolver_service.h index 6d96cc56f..b7fb1532a 100644 --- a/src/include/gnunet_resolver_service.h +++ b/src/include/gnunet_resolver_service.h @@ -60,7 +60,7 @@ typedef void (*GNUNET_RESOLVER_AddressCallback) (void *cls, * @param hostname the hostname to resolve * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" * @param callback function to call with addresses - * @param cls closure for callback + * @param callback_cls closure for callback * @param timeout how long to try resolving */ void @@ -69,7 +69,8 @@ GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched, const char *hostname, int domain, struct GNUNET_TIME_Relative timeout, - GNUNET_RESOLVER_AddressCallback callback, void *cls); + GNUNET_RESOLVER_AddressCallback callback, + void *callback_cls); /** diff --git a/src/include/gnunet_server_lib.h b/src/include/gnunet_server_lib.h index 632833035..135b1f196 100644 --- a/src/include/gnunet_server_lib.h +++ b/src/include/gnunet_server_lib.h @@ -253,11 +253,8 @@ struct GNUNET_SERVER_Client *GNUNET_SERVER_connect_socket (struct * @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 task identifier that can be used to cancel the receive, - * GNUNET_SCHEDULER_NO_TASK should be returned - * if the receiver function was already called */ -typedef GNUNET_SCHEDULER_TaskIdentifier +typedef void (*GNUNET_SERVER_ReceiveCallback) (void *cls, size_t max, struct GNUNET_TIME_Relative timeout, @@ -269,11 +266,8 @@ typedef GNUNET_SCHEDULER_TaskIdentifier * Cancel receive request. * * @param cls closure - * @param ti task identifier from the receive callback */ -typedef void (*GNUNET_SERVER_ReceiveCancelCallback) (void *cls, - GNUNET_SCHEDULER_TaskIdentifier - ti); +typedef void (*GNUNET_SERVER_ReceiveCancelCallback) (void *cls); /** diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index 870083baf..02c346ef0 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -55,7 +55,6 @@ libgnunet_plugin_transport_tcp_la_SOURCES = \ plugin_transport_tcp.c libgnunet_plugin_transport_tcp_la_LIBADD = \ $(top_builddir)/src/hello/libgnunethello.la \ - $(top_builddir)/src/resolver/libgnunetresolver.la \ $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ $(top_builddir)/src/util/libgnunetutil.la libgnunet_plugin_transport_tcp_la_LDFLAGS = \ diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 240c86247..f69c64b9f 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -46,6 +46,7 @@ libgnunetutil_la_SOURCES = \ plugin.c \ program.c \ pseudonym.c \ + resolver_api.c \ scheduler.c \ server.c \ server_tc.c \ @@ -66,6 +67,16 @@ libgnunetutil_la_LDFLAGS = \ -version-info 4:0:0 +bin_PROGRAMS = \ + gnunet-service-resolver + +gnunet_service_resolver_SOURCES = \ + gnunet-service-resolver.c +gnunet_service_resolver_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + + plugin_LTLIBRARIES = \ libgnunet_plugin_test.la @@ -106,6 +117,7 @@ check_PROGRAMS = \ test_plugin \ test_program \ test_pseudonym \ + test_resolver_api \ test_scheduler \ test_scheduler_delay \ test_server \ @@ -157,7 +169,7 @@ test_container_multihashmap_SOURCES = \ test_container_multihashmap.c test_container_multihashmap_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la - + test_container_heap_SOURCES = \ test_container_heap.c test_container_heap_LDADD = \ @@ -268,6 +280,11 @@ test_pseudonym_SOURCES = \ test_pseudonym_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la +test_resolver_api_SOURCES = \ + test_resolver_api.c +test_resolver_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + test_scheduler_SOURCES = \ test_scheduler.c test_scheduler_LDADD = \ @@ -319,4 +336,5 @@ EXTRA_DIST = \ test_container_meta_data_image.jpg \ test_program_data.conf \ test_pseudonym_data.conf \ + test_resolver_api_data.conf \ test_service_data.conf diff --git a/src/util/client.c b/src/util/client.c index 2da392fb4..bdb258b73 100644 --- a/src/util/client.c +++ b/src/util/client.c @@ -60,11 +60,6 @@ struct GNUNET_CLIENT_Connection */ char *service_name; - /** - * ID of task used for receiving. - */ - GNUNET_SCHEDULER_TaskIdentifier receiver_task; - /** * Handler for current receiver task. */ @@ -110,6 +105,14 @@ struct GNUNET_CLIENT_Connection */ int msg_complete; + /** + * Are we currently busy doing receive-processing? + * GNUNET_YES if so, GNUNET_NO if not, GNUNET_SYSERR + * if the handle should be destroyed as soon as the + * receive processing is done. + */ + int in_receive; + }; @@ -156,6 +159,7 @@ GNUNET_CLIENT_connect (struct GNUNET_SCHEDULER_Handle *sched, return NULL; } sock = GNUNET_CONNECTION_create_from_connect (sched, + cfg, hostname, port, GNUNET_SERVER_MAX_MESSAGE_SIZE); @@ -195,15 +199,19 @@ GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock) GNUNET_CONNECTION_destroy (sock->sock); sock->sock = NULL; sock->receiver_handler = NULL; - GNUNET_SCHEDULER_add_after (sock->sched, - GNUNET_YES, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sock->receiver_task, &finish_cleanup, sock); + if (sock->in_receive == GNUNET_YES) + sock->in_receive = GNUNET_SYSERR; + else + GNUNET_SCHEDULER_add_after (sock->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + &finish_cleanup, sock); } /** - * check if message is complete + * Check if message is complete */ static void check_complete (struct GNUNET_CLIENT_Connection *conn) @@ -238,8 +246,14 @@ receive_helper (void *cls, struct GNUNET_TIME_Relative remaining; GNUNET_assert (conn->msg_complete == GNUNET_NO); - conn->receiver_task = GNUNET_SCHEDULER_NO_TASK; - + if (GNUNET_SYSERR == conn->in_receive) + GNUNET_SCHEDULER_add_after (conn->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + &finish_cleanup, + conn); + conn->in_receive = GNUNET_NO; if ((available == 0) || (conn->sock == NULL) || (errCode != 0)) { /* signal timeout! */ @@ -291,8 +305,15 @@ receive_task (void *scls, const struct GNUNET_SCHEDULER_TaskContext *tc) char mbuf[msize]; struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader*) mbuf; + if (GNUNET_SYSERR == sock->in_receive) + GNUNET_SCHEDULER_add_after (sock->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + &finish_cleanup, + sock); + sock->in_receive = GNUNET_NO; GNUNET_assert (GNUNET_YES == sock->msg_complete); - sock->receiver_task = GNUNET_SCHEDULER_NO_TASK; GNUNET_assert (sock->received_pos >= msize); memcpy (msg, cmsg, msize); memmove (sock->received_buf, @@ -326,22 +347,21 @@ GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock, handler (handler_cls, NULL); return; } - GNUNET_assert (sock->receiver_task == - GNUNET_SCHEDULER_NO_TASK); sock->receiver_handler = handler; sock->receiver_handler_cls = handler_cls; sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); + sock->in_receive = GNUNET_YES; if (GNUNET_YES == sock->msg_complete) - sock->receiver_task = GNUNET_SCHEDULER_add_after (sock->sched, - GNUNET_YES, - GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_SCHEDULER_NO_TASK, - &receive_task, sock); + GNUNET_SCHEDULER_add_after (sock->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + &receive_task, sock); else - sock->receiver_task = GNUNET_CONNECTION_receive (sock->sock, - GNUNET_SERVER_MAX_MESSAGE_SIZE, - timeout, - &receive_helper, sock); + GNUNET_CONNECTION_receive (sock->sock, + GNUNET_SERVER_MAX_MESSAGE_SIZE, + timeout, + &receive_helper, sock); } diff --git a/src/util/connection.c b/src/util/connection.c index fa6542325..a3e9101ea 100644 --- a/src/util/connection.c +++ b/src/util/connection.c @@ -38,17 +38,43 @@ #include "platform.h" #include "gnunet_common.h" #include "gnunet_connection_lib.h" +#include "gnunet_container_lib.h" +#include "gnunet_resolver_service.h" #include "gnunet_scheduler_lib.h" #define DEBUG_CONNECTION GNUNET_NO + /** - * List of address families to give as hints to - * getaddrinfo, in reverse order of preference. + * Possible functions to call after connect failed or succeeded. */ -static int address_families[] = - { AF_INET, AF_INET6, AF_UNSPEC }; +enum ConnectContinuations + { + /** + * Call nothing. + */ + CC_NONE = 0, + + /** + * Call "receive_again". + */ + CC_RECEIVE_AGAIN = 1, + + /** + * Call "transmit_ready". + */ + CC_TRANSMIT_READY = 2, + + /** + * Call "destroy_continuation". + */ + CC_DESTROY_CONTINUATION = 4 + }; + +/** + * Transmission handle. There can only be one for each connection. + */ struct GNUNET_CONNECTION_TransmitHandle { @@ -86,6 +112,51 @@ struct GNUNET_CONNECTION_TransmitHandle }; + +/** + * During connect, we try multiple possible IP addresses + * to find out which one might work. + */ +struct AddressProbe +{ + + /** + * This is a linked list. + */ + struct AddressProbe *next; + + /** + * This is a doubly-linked list. + */ + struct AddressProbe *prev; + + /** + * The address; do not free (allocated at the end of this struct). + */ + const struct sockaddr *addr; + + /** + * Underlying OS's socket. + */ + struct GNUNET_NETWORK_Handle *sock; + + /** + * Connection for which we are probing. + */ + struct GNUNET_CONNECTION_Handle *h; + + /** + * Lenth of addr. + */ + socklen_t addrlen; + + /** + * Task waiting for the socket to finish connecting. + */ + GNUNET_SCHEDULER_TaskIdentifier task; +}; + + /** * @brief handle for a network socket */ @@ -98,14 +169,21 @@ struct GNUNET_CONNECTION_Handle struct GNUNET_SCHEDULER_Handle *sched; /** - * Address information for connect (may be NULL). + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Linked list of sockets we are currently trying out + * (during connect). */ - struct addrinfo *ai; + struct AddressProbe *ap_head; /** - * Index for the next struct addrinfo for connect attempts (may be NULL) + * Linked list of sockets we are currently trying out + * (during connect). */ - struct addrinfo *ai_pos; + struct AddressProbe *ap_tail; /** * Network address of the other end-point, may be NULL. @@ -118,6 +196,21 @@ struct GNUNET_CONNECTION_Handle */ char *hostname; + /** + * Underlying OS's socket, set to NULL after fatal errors. + */ + struct GNUNET_NETWORK_Handle *sock; + + /** + * Function to call on data received, NULL if no receive is pending. + */ + GNUNET_CONNECTION_Receiver receiver; + + /** + * Closure for receiver. + */ + void *receiver_cls; + /** * Pointer to our write buffer. */ @@ -145,17 +238,6 @@ struct GNUNET_CONNECTION_Handle */ socklen_t addrlen; - /** - * 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; - /** * Read task that we may need to wait for. */ @@ -172,36 +254,32 @@ struct GNUNET_CONNECTION_Handle struct GNUNET_CONNECTION_TransmitHandle nth; /** - * Underlying OS's socket, set to NULL after fatal errors. - */ - struct GNUNET_NETWORK_Handle *sock; - - /** - * Port to connect to. + * Timeout for receiving (in absolute time). */ - uint16_t port; - + struct GNUNET_TIME_Absolute receive_timeout; + /** - * Function to call on data received, NULL - * if no receive is pending. + * Functions to call after connect failed or succeeded. */ - GNUNET_CONNECTION_Receiver receiver; - + enum ConnectContinuations ccs; + /** - * Closure for receiver. + * Maximum number of bytes to read (for receiving). */ - void *receiver_cls; + size_t max; /** - * Timeout for receiving (in absolute time). + * Are we still waiting for DNS replies (on connect)? + * GNUNET_YES if we are, GNUNET_NO if we are not waiting for DNS, + * GNUNET_SYSERR if destroying the handle was deferred due to + * a pending DNS lookup. */ - struct GNUNET_TIME_Absolute receive_timeout; + int dns_active; /** - * Maximum number of bytes to read - * (for receiving). + * Port to connect to. */ - size_t max; + uint16_t port; }; @@ -351,230 +429,413 @@ GNUNET_CONNECTION_get_address (struct GNUNET_CONNECTION_Handle *sock, /** - * Perform a DNS lookup for the hostname associated - * with the current socket, iterating over the address - * families as specified in the "address_families" array. + * It is time to re-try connecting. + * + * @param cls the handle for the connection that should be re-tried + * @param tc unused scheduler taks context + */ +static void +retry_connect_continuation (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * 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 sock the socket for which to do the lookup + * @param cls our connection handle + * @param tc task context describing why we are here */ static void -try_lookup (struct GNUNET_CONNECTION_Handle *sock) -{ - struct addrinfo hints; - int ec; +receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); - GNUNET_assert (0 < strlen (sock->hostname)); /* sanity check */ - 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 - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("`%s' tries to resolve address family %d and hostname `%s:%u'\n"), - "getaddrinfo", - address_families[sock->af_fam_offset], - sock->hostname, - sock->port); -#endif - ec = getaddrinfo (sock->hostname, NULL, &hints, &sock->ai); -#if 0 - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("`%s' returned from resolving address family %d and hostname `%s:%u'\n"), - "getaddrinfo", - address_families[sock->af_fam_offset], - sock->hostname, - sock->port); + +/** + * Scheduler let us know that the connect task is finished (or was + * cancelled due to shutdown). Now really clean up. + * + * @param cls our "struct GNUNET_CONNECTION_Handle *" + * @param tc unused + */ +static void +destroy_continuation (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + + + +/** + * 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. + * + * @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); + + +/** + * We've failed for good to establish a connection. + * + * @param h the connection we tried to establish + */ +static void +connect_fail_continuation (struct GNUNET_CONNECTION_Handle *h) +{ +#if DEBUG_CONNECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to establish TCP connection to `%s:%u', no further addresses to try.\n", + h->hostname, + h->port); #endif - if (0 != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK, - _("`%s' failed for address family %d and hostname `%s:%u': %s\n"), - "getaddrinfo", - address_families[sock->af_fam_offset], - sock->hostname, - sock->port, - gai_strerror (ec)); - sock->ai = NULL; - } - sock->ai_pos = sock->ai; + /* 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); + + /* FIXME: trigger delayed reconnect attempt... */ + /* trigger jobs that used to wait on "connect_task" */ + if (0 != (h->ccs & CC_RECEIVE_AGAIN)) + { + h->ccs -= CC_RECEIVE_AGAIN; + h->read_task = GNUNET_SCHEDULER_add_after (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + &receive_again, + h); + } + if (0 != (h->ccs & CC_TRANSMIT_READY)) + { + h->ccs -= CC_TRANSMIT_READY; + h->write_task = GNUNET_SCHEDULER_add_after (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + &transmit_ready, + h); + } + if (0 != (h->ccs & CC_DESTROY_CONTINUATION)) + { + h->ccs -= CC_DESTROY_CONTINUATION; + GNUNET_SCHEDULER_add_continuation (h->sched, + GNUNET_NO, + &destroy_continuation, + h, + GNUNET_SCHEDULER_REASON_TIMEOUT); } } /** - * Initiate asynchronous TCP connect request. + * We've succeeded in establishing a connection. * - * @param sock what socket to connect - * @return GNUNET_SYSERR error (no more addresses to try) + * @param h the connection we tried to establish */ -static int -try_connect (struct GNUNET_CONNECTION_Handle *sock) +static void +connect_success_continuation (struct GNUNET_CONNECTION_Handle *h) { - struct GNUNET_NETWORK_Handle *s; - - if (sock->addr != NULL) +#if DEBUG_CONNECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection to `%s' succeeded!\n", + GNUNET_a2s(h->addr, h->addrlen)); +#endif + /* trigger jobs that waited for the connection */ + if (0 != (h->ccs & CC_RECEIVE_AGAIN)) { - GNUNET_free (sock->addr); - sock->addr = NULL; - sock->addrlen = 0; + h->ccs -= CC_RECEIVE_AGAIN; + h->read_task = GNUNET_SCHEDULER_add_after (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + &receive_again, + h); } - while (1) + if (0 != (h->ccs & CC_TRANSMIT_READY)) { - 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 = GNUNET_NETWORK_socket_socket (sock->ai_pos->ai_family, SOCK_STREAM, 0); - if (s == NULL) - { - /* 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 - if (GNUNET_OK != GNUNET_NETWORK_socket_set_inheritable (s)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "GNUNET_NETWORK_socket_set_inheritable"); -#endif - if (GNUNET_SYSERR == GNUNET_NETWORK_socket_set_blocking (s, GNUNET_NO)) - { - /* we'll treat this one as fatal */ - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (s)); - return GNUNET_SYSERR; - } -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Trying to connect to `%s'\n"), - GNUNET_a2s(sock->ai_pos->ai_addr, - sock->ai_pos->ai_addrlen)); -#endif - if ((GNUNET_OK != GNUNET_NETWORK_socket_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 (GNUNET_OK == GNUNET_NETWORK_socket_close (s)); - sock->ai_pos = sock->ai_pos->ai_next; - continue; - } - break; + h->ccs -= CC_TRANSMIT_READY; + h->write_task = + GNUNET_SCHEDULER_add_write_net (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_absolute_get_remaining (h->nth.transmit_timeout), + h->sock, &transmit_ready, h); + } + if (0 != (h->ccs & CC_DESTROY_CONTINUATION)) + { + h->ccs -= CC_DESTROY_CONTINUATION; + GNUNET_SCHEDULER_add_continuation (h->sched, + GNUNET_NO, + &destroy_continuation, + h, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); } - /* 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; } /** - * Scheduler let us know that we're either ready to - * write on the socket OR connect timed out. Do the - * right thing. + * Scheduler let us know that we're either ready to write on the + * socket OR connect timed out. Do the right thing. + * + * @param ap the address we were probing + * @param tc success or failure info about the connect attempt. */ static void -connect_continuation (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +connect_probe_continuation (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct GNUNET_CONNECTION_Handle *sock = cls; - struct GNUNET_TIME_Relative delay; + struct AddressProbe *ap = cls; + struct GNUNET_CONNECTION_Handle *h = ap->h; + struct AddressProbe *pos; + int error; unsigned int len; - int error; - GNUNET_assert (0 < strlen (sock->hostname)); /* sanity check */ - /* 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 */ + GNUNET_CONTAINER_DLL_remove (h->ap_head, h->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 (sock->sock, SOL_SOCKET, SO_ERROR, &error, &len)) || - (error != 0) || (errno != 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) || (errno != 0) ) + { + GNUNET_NETWORK_socket_close (ap->sock); + GNUNET_free (ap); + if ( (NULL == h->ap_head) && + (h->dns_active == GNUNET_NO) ) + connect_fail_continuation (h); + return; + } + 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; + GNUNET_free (ap); + /* cancel all other attempts */ + while (NULL != (pos = h->ap_head)) + { + GNUNET_NETWORK_socket_close (pos->sock); + GNUNET_SCHEDULER_cancel (h->sched, pos->task); + GNUNET_CONTAINER_DLL_remove (h->ap_head, h->ap_tail, pos); + GNUNET_free (pos); + } + connect_success_continuation (h); +} + + +/** + * Scheduler let us know that the connect task is finished (or was + * cancelled due to shutdown). Now really clean up. + * + * @param cls our "struct GNUNET_CONNECTION_Handle *" + * @param tc unused + */ +static void +destroy_continuation (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CONNECTION_Handle *sock = cls; + GNUNET_CONNECTION_TransmitReadyNotify notify; + + if (sock->dns_active == GNUNET_YES) + { + sock->dns_active = GNUNET_SYSERR; + return; + } + if (0 != (sock->ccs & CC_TRANSMIT_READY)) + { + sock->ccs |= CC_DESTROY_CONTINUATION; + return; + } + 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 != NULL) { #if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Failed to establish TCP connection to `%s:%u': %s\n", - GNUNET_a2s(sock->addr, sock->addrlen), - STRERROR (GNUNET_MAX (error, errno))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down socket.\n"); #endif - /* connect failed / timed out */ - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock->sock)); - sock->sock = NULL; - if (GNUNET_SYSERR == try_connect (sock)) + GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR); + } + if (0 != (sock->ccs & CC_RECEIVE_AGAIN)) + { + sock->ccs |= CC_DESTROY_CONTINUATION; + return; + } + if (sock->read_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_add_after (sock->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + sock->read_task, + &destroy_continuation, sock); + return; + } + if (NULL != (notify = sock->nth.notify_ready)) + { + sock->nth.notify_ready = NULL; + notify (sock->nth.notify_ready_cls, 0, NULL); + if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_TASK) { - /* failed for good */ -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Failed to establish TCP connection to `%s:%u', no further addresses to try.\n", - sock->hostname, - sock->port); -#endif - /* connect failed / timed out */ - GNUNET_break (sock->ai_pos == NULL); - freeaddrinfo (sock->ai); - sock->ai = NULL; - return; + GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); + sock->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK; } - delay = GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT; - if (sock->nth.notify_ready != NULL) - delay = GNUNET_TIME_relative_min (delay, - GNUNET_TIME_absolute_get_remaining (sock->nth.transmit_timeout)); - if (sock->receiver != NULL) - delay = GNUNET_TIME_relative_min (delay, - GNUNET_TIME_absolute_get_remaining (sock->receive_timeout)); - delay.value /= (1 + sock->af_fam_offset); -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Will try to connect to `%s' for %llu ms\n", - GNUNET_a2s (sock->addr, - sock->addrlen), - delay.value); -#endif - sock->connect_task = GNUNET_SCHEDULER_add_write_net (tc->sched, GNUNET_NO, /* abort on shutdown */ - GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_SCHEDULER_NO_TASK, - delay, - sock->sock, - &connect_continuation, - sock); + } + if (sock->sock != NULL) + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock->sock)); + GNUNET_free_non_null (sock->addr); + GNUNET_free_non_null (sock->hostname); + GNUNET_free (sock); +} + + +/** + * Try to establish a socket connection given the specified address. + * This function is called by the resolver once we have a DNS reply. + * + * @param cls our "struct GNUNET_CONNECTION_Handle *" + * @param addr address to try, NULL for "last call" + * @param addrlen length of addr + */ +static void +try_connect_using_address (void *cls, + const struct sockaddr * addr, + socklen_t addrlen) +{ + struct GNUNET_CONNECTION_Handle *h = cls; + struct AddressProbe *ap; + struct GNUNET_TIME_Relative delay; + + if (addr == NULL) + { + if (h->dns_active == GNUNET_SYSERR) + { + h->dns_active = GNUNET_NO; + GNUNET_SCHEDULER_add_after (h->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + h->read_task, + &destroy_continuation, + h); + return; + } + h->dns_active = GNUNET_NO; + if (NULL == h->ap_head) + connect_fail_continuation (h); return; } - /* connect succeeded! clean up "ai" */ + if (h->sock != NULL) + return; /* already connected */ + if (h->dns_active == GNUNET_SYSERR) + return; /* already destroyed */ + /* try to connect */ + 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; + + 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_socket (ap->addr->sa_family, SOCK_STREAM, 0); + if (ap->sock == NULL) + { + GNUNET_free (ap); + return; /* not supported by OS */ + } +#ifndef MINGW + if (GNUNET_OK != GNUNET_NETWORK_socket_set_inheritable (ap->sock)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "GNUNET_NETWORK_socket_set_inheritable"); +#endif + if (GNUNET_SYSERR == GNUNET_NETWORK_socket_set_blocking (ap->sock, GNUNET_NO)) + { + /* we might want to treat this one as fatal... */ + GNUNET_break (0); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock)); + GNUNET_free (ap); + return; + } #if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connection to `%s' succeeded!\n", - GNUNET_a2s(sock->addr, sock->addrlen)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Trying to connect to `%s'\n"), + GNUNET_a2s(ap->addr, + ap->addrlen)); #endif - freeaddrinfo (sock->ai); - sock->ai_pos = NULL; - sock->ai = NULL; + 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); + 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)); + ap->task = GNUNET_SCHEDULER_add_write_net (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + delay, + ap->sock, + &connect_probe_continuation, + ap); +} + + +/** + * It is time to re-try connecting. + * + * @param cls the handle for the connection that should be re-tried + * @param tc unused scheduler taks context + */ +static void +retry_connect_continuation (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CONNECTION_Handle *sock = cls; + + sock->dns_active = GNUNET_YES; + GNUNET_RESOLVER_ip_get (sock->sched, + sock->cfg, + sock->hostname, + AF_UNSPEC, + GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT, + &try_connect_using_address, + sock); } @@ -584,6 +845,7 @@ connect_continuation (void *cls, * yet been established. This function only creates TCP connections. * * @param sched scheduler to use + * @param cfg configuration 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 @@ -591,37 +853,23 @@ connect_continuation (void *cls, * @return the socket handle */ struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_connect (struct GNUNET_SCHEDULER_Handle - *sched, const char *hostname, - uint16_t port, size_t maxbuf) +GNUNET_CONNECTION_create_from_connect (struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *hostname, + uint16_t port, size_t maxbuf) { struct GNUNET_CONNECTION_Handle *ret; GNUNET_assert (0 < strlen (hostname)); /* sanity check */ ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle) + maxbuf); - ret->sock = NULL; + ret->cfg = cfg; 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_net (sched, GNUNET_NO, /* abort on shutdown */ - GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_SCHEDULER_NO_TASK, - GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT, - ret->sock, - &connect_continuation, ret); + retry_connect_continuation (ret, NULL); return ret; - } @@ -699,68 +947,13 @@ GNUNET_CONNECTION_create_from_sockaddr (struct GNUNET_SCHEDULER_Handle int GNUNET_CONNECTION_check (struct GNUNET_CONNECTION_Handle *sock) { - if (sock->ai != NULL) + if ( (sock->ap_head != NULL) || + (sock->dns_active == GNUNET_YES) ) return GNUNET_YES; /* still trying to connect */ return (sock->sock == NULL) ? GNUNET_NO : GNUNET_YES; } -/** - * Scheduler let us know that the connect task is finished (or was - * cancelled due to shutdown). Now really clean up. - */ -static void -destroy_continuation (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct GNUNET_CONNECTION_Handle *sock = cls; - GNUNET_CONNECTION_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 != NULL) - { -#if DEBUG_CONNECTION - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down socket.\n"); -#endif - GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR); - } - if (sock->read_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_add_after (sock->sched, - GNUNET_YES, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sock->read_task, - &destroy_continuation, sock); - return; - } - if (NULL != (notify = sock->nth.notify_ready)) - { - 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; - } - } - if (sock->sock != NULL) - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_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); -} - - /** * Close the socket and free associated resources. Pending * transmissions are simply dropped. A pending receive call will be @@ -771,17 +964,22 @@ destroy_continuation (void *cls, void GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *sock) { - if (sock->write_buffer_off == 0) - sock->ai_pos = NULL; /* if we're still trying to connect and have - no message pending, stop trying! */ + if ( (sock->write_buffer_off == 0) && + (sock->dns_active == GNUNET_YES) ) + { + sock->dns_active = GNUNET_SYSERR; /* if we're still trying to connect and have + no message pending, stop trying! */ + return; + } GNUNET_assert (sock->sched != NULL); GNUNET_SCHEDULER_add_after (sock->sched, GNUNET_YES, GNUNET_SCHEDULER_PRIORITY_KEEP, - sock->connect_task, + GNUNET_SCHEDULER_NO_TASK, &destroy_continuation, sock); } + /** * Tell the receiver callback that a timeout was reached. */ @@ -891,6 +1089,9 @@ RETRY: * 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) @@ -899,8 +1100,7 @@ receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) struct GNUNET_TIME_Absolute now; sh->read_task = GNUNET_SCHEDULER_NO_TASK; - if ((sh->sock == NULL) && - (sh->connect_task == GNUNET_SCHEDULER_NO_TASK)) + if (sh->sock == NULL) { /* not connected and no longer trying */ #if DEBUG_CONNECTION @@ -921,25 +1121,17 @@ receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 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; - } + GNUNET_assert (sh->sock != NULL); /* connect succeeded, wait for data! */ sh->read_task = GNUNET_SCHEDULER_add_read_net (tc->sched, - GNUNET_YES, - GNUNET_SCHEDULER_PRIORITY_KEEP, - sh->connect_task, - GNUNET_TIME_absolute_get_remaining - (sh->receive_timeout), - sh->sock, &receive_ready, - sh); + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_absolute_get_remaining + (sh->receive_timeout), + sh->sock, + &receive_ready, + sh); } @@ -956,9 +1148,8 @@ receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * @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 */ -GNUNET_SCHEDULER_TaskIdentifier +void GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *sock, size_t max, struct GNUNET_TIME_Relative timeout, @@ -967,16 +1158,27 @@ GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *sock, struct GNUNET_SCHEDULER_TaskContext tc; GNUNET_assert ((sock->read_task == GNUNET_SCHEDULER_NO_TASK) && + (0 == (sock->ccs & CC_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; - memset (&tc, 0, sizeof (tc)); - tc.sched = sock->sched; - tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE; - receive_again (sock, &tc); - return sock->read_task; + if (sock->sock != NULL) + { + memset (&tc, 0, sizeof (tc)); + tc.sched = sock->sched; + tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE; + receive_again (sock, &tc); + return; + } + if ( (sock->dns_active != GNUNET_YES) && + (sock->ap_head == NULL) ) + { + receiver (receiver_cls, NULL, 0, NULL, 0, ETIMEDOUT); + return; + } + sock->ccs += CC_RECEIVE_AGAIN; } @@ -986,16 +1188,22 @@ GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *sock, * for the cancellation to be valid. * * @param sock socket handle - * @param task task identifier returned from the receive call - * @return closure of the original receiver callback + * @return closure of the original receiver callback closure */ void * -GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *sock, - GNUNET_SCHEDULER_TaskIdentifier task) +GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *sock) { - GNUNET_assert (sock->read_task == task); - GNUNET_assert (sock == GNUNET_SCHEDULER_cancel (sock->sched, task)); - sock->read_task = GNUNET_SCHEDULER_NO_TASK; + if (sock->read_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_assert (sock == GNUNET_SCHEDULER_cancel (sock->sched, + sock->read_task)); + sock->read_task = GNUNET_SCHEDULER_NO_TASK; + } + else + { + GNUNET_assert (0 != (sock->ccs & CC_RECEIVE_AGAIN)); + sock->ccs -= CC_RECEIVE_AGAIN; + } sock->receiver = NULL; return sock->receiver_cls; } @@ -1090,6 +1298,9 @@ transmit_error (struct GNUNET_CONNECTION_Handle *sock) * 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. + * + * @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) @@ -1100,19 +1311,6 @@ transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_assert (sock->write_task != GNUNET_SCHEDULER_NO_TASK); sock->write_task = GNUNET_SCHEDULER_NO_TASK; - if (sock->connect_task != GNUNET_SCHEDULER_NO_TASK) - { - /* 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; - } if ( (sock->sock == NULL) || ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) && (0 == (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) && @@ -1225,8 +1423,9 @@ GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle GNUNET_assert (notify != NULL); GNUNET_assert (sock->write_buffer_size >= size); - if ((sock->sock == NULL) && - (sock->connect_task == GNUNET_SCHEDULER_NO_TASK)) + if ( (sock->sock == NULL) && + (sock->ap_head == NULL) && + (sock->dns_active != GNUNET_YES) ) { #if DEBUG_CONNECTION GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -1253,21 +1452,16 @@ GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle sock); if (sock->write_task == GNUNET_SCHEDULER_NO_TASK) { - if (sock->connect_task == GNUNET_SCHEDULER_NO_TASK) + if (sock->sock != NULL) sock->write_task = GNUNET_SCHEDULER_add_write_net (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); + 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); + sock->ccs |= CC_TRANSMIT_READY; } return &sock->nth; } diff --git a/src/util/gnunet-service-resolver.c b/src/util/gnunet-service-resolver.c new file mode 100644 index 000000000..304f11b7b --- /dev/null +++ b/src/util/gnunet-service-resolver.c @@ -0,0 +1,492 @@ +/* + This file is part of GNUnet. + (C) 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + + 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 + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + 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. +*/ + +/** + * @file resolver/gnunet-service-resolver.c + * @brief code to do DNS resolution + * @author Christian Grothoff + */ + +#include +#include "platform.h" +#include "gnunet_disk_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_service_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_strings_lib.h" +#include "gnunet_time_lib.h" +#include "resolver.h" + + +struct IPCache +{ + struct IPCache *next; + char *addr; + struct sockaddr *sa; + struct GNUNET_TIME_Absolute last_refresh; + struct GNUNET_TIME_Absolute last_request; + unsigned int salen; +}; + + +static struct IPCache *head; + + + + +#if HAVE_GETNAMEINFO +static void +getnameinfo_resolve (struct IPCache *cache) +{ + char hostname[256]; + + if (0 == getnameinfo (cache->sa, + cache->salen, + hostname, + sizeof(hostname), + NULL, 0, 0)) + cache->addr = GNUNET_strdup (hostname); +} +#endif + + +#if HAVE_GETHOSTBYADDR +static void +gethostbyaddr_resolve (struct IPCache *cache) +{ + struct hostent *ent; + + switch (cache->sa->sa_family) + { + case AF_INET: + ent = gethostbyaddr (&((struct sockaddr_in *) cache->sa)->sin_addr, + sizeof (struct in_addr), AF_INET); + break; + case AF_INET6: + ent = gethostbyaddr (&((struct sockaddr_in6 *) cache->sa)->sin6_addr, + sizeof (struct in6_addr), AF_INET6); + break; + default: + ent = NULL; + } + if (ent != NULL) + cache->addr = GNUNET_strdup (ent->h_name); +} +#endif + + +static void +cache_resolve (struct IPCache *cache) +{ +#if HAVE_GETNAMEINFO + if (cache->addr == NULL) + getnameinfo_resolve (cache); +#endif +#if HAVE_GETHOSTBYADDR + if (cache->addr == NULL) + gethostbyaddr_resolve (cache); +#endif +} + + + +/** + * Get an IP address as a string (works for both IPv4 and IPv6). Note + * that the resolution happens asynchronously and that the first call + * may not immediately result in the FQN (but instead in a + * human-readable IP address). + * + * @param sa should be of type "struct sockaddr*" + */ +static void +get_ip_as_string (struct GNUNET_SERVER_Client *client, + const struct sockaddr *sav, socklen_t salen) +{ + struct IPCache *cache; + struct IPCache *prev; + struct GNUNET_TIME_Absolute now; + struct GNUNET_SERVER_TransmitContext *tc; + + if (salen < sizeof (struct sockaddr)) + { + GNUNET_break (0); + return; + } + now = GNUNET_TIME_absolute_get (); + cache = head; + prev = NULL; + while ((cache != NULL) && + ((cache->salen != salen) || (0 != memcmp (cache->sa, sav, salen)))) + { + if (GNUNET_TIME_absolute_get_duration (cache->last_request).value < + 60 * 60 * 1000) + { + if (prev != NULL) + { + prev->next = cache->next; + GNUNET_free_non_null (cache->addr); + GNUNET_free (cache->sa); + GNUNET_free (cache); + cache = prev->next; + } + else + { + head = cache->next; + GNUNET_free_non_null (cache->addr); + GNUNET_free (cache->sa); + GNUNET_free (cache); + cache = head; + } + continue; + } + prev = cache; + cache = cache->next; + } + if (cache != NULL) + { + cache->last_request = now; + if (GNUNET_TIME_absolute_get_duration (cache->last_request).value < + 60 * 60 * 1000) + { + GNUNET_free_non_null (cache->addr); + cache->addr = NULL; + cache->salen = 0; + cache_resolve (cache); + } + } + else + { + cache = GNUNET_malloc (sizeof (struct IPCache)); + cache->next = head; + cache->salen = salen; + cache->sa = GNUNET_malloc (salen); + memcpy (cache->sa, sav, salen); + cache->last_request = GNUNET_TIME_absolute_get (); + cache->last_refresh = GNUNET_TIME_absolute_get (); + cache->addr = NULL; + cache_resolve (cache); + head = cache; + } + tc = GNUNET_SERVER_transmit_context_create (client); + if (cache->addr != NULL) + GNUNET_SERVER_transmit_context_append (tc, + cache->addr, + strlen (cache->addr) + 1, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + GNUNET_SERVER_transmit_context_append (tc, NULL, 0, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +#if HAVE_GETADDRINFO +static int +getaddrinfo_resolve (struct GNUNET_SERVER_TransmitContext *tc, + const char *hostname, int domain) +{ + int s; + struct addrinfo hints; + struct addrinfo *result; + struct addrinfo *pos; + + memset (&hints, 0, sizeof (struct addrinfo)); +// FIXME in PlibC +#ifndef MINGW + hints.ai_family = domain; +#else + hints.ai_family = AF_INET; +#endif + hints.ai_socktype = SOCK_STREAM; /* go for TCP */ + + if (0 != (s = getaddrinfo (hostname, NULL, &hints, &result))) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Could not resolve `%s' (%s): %s\n"), hostname, + (domain == + AF_INET) ? "IPv4" : ((domain == + AF_INET6) ? "IPv6" : "any"), + gai_strerror (s)); + if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY) || +#ifndef MINGW + (s == EAI_SYSTEM) +#else + // FIXME NILS + 1 +#endif + ) + return GNUNET_NO; /* other function may still succeed */ + return GNUNET_SYSERR; + } + if (result == NULL) + return GNUNET_SYSERR; + pos = result; + while (pos != NULL) + { + GNUNET_SERVER_transmit_context_append (tc, + result->ai_addr, + result->ai_addrlen, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + pos = pos->ai_next; + } + freeaddrinfo (result); + return GNUNET_OK; +} +#endif + +#if HAVE_GETHOSTBYNAME2 +static int +gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc, + const char *hostname, int domain) +{ + struct hostent *hp; + struct sockaddr_in a4; + struct sockaddr_in6 a6; + int ret1; + int ret2; + + if (domain == AF_UNSPEC) + { + ret1 = gethostbyname2_resolve (tc, hostname, AF_INET); + ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6); + if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK)) + return GNUNET_OK; + if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR)) + return GNUNET_SYSERR; + return GNUNET_NO; + } + hp = gethostbyname2 (hostname, domain); + if (hp == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Could not find IP of host `%s': %s\n"), + hostname, hstrerror (h_errno)); + return GNUNET_SYSERR; + } + GNUNET_assert (hp->h_addrtype == domain); + if (domain == AF_INET) + { + GNUNET_assert (hp->h_length == sizeof (struct in_addr)); + memset (&a4, 0, sizeof (a4)); + a4.sin_family = AF_INET; + memcpy (&a4.sin_addr, hp->h_addr_list[0], hp->h_length); + GNUNET_SERVER_transmit_context_append (tc, + &a4, + sizeof (a4), + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + } + else + { + GNUNET_assert (hp->h_length == sizeof (struct in6_addr)); + memset (&a6, 0, sizeof (a6)); + a6.sin6_family = AF_INET6; + memcpy (&a6.sin6_addr, hp->h_addr_list[0], hp->h_length); + GNUNET_SERVER_transmit_context_append (tc, + &a6, + sizeof (a6), + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + } + return GNUNET_OK; +} +#endif + +#if HAVE_GETHOSTBYNAME +static int +gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc, + const char *hostname) +{ + struct hostent *hp; + struct sockaddr_in addr; + + hp = GETHOSTBYNAME (hostname); + if (hp == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Could not find IP of host `%s': %s\n"), + hostname, hstrerror (h_errno)); + return GNUNET_SYSERR; + } + if (hp->h_addrtype != AF_INET) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_assert (hp->h_length == sizeof (struct in_addr)); + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + memcpy (&addr.sin_addr, hp->h_addr_list[0], hp->h_length); + GNUNET_SERVER_transmit_context_append (tc, + &addr, + sizeof (addr), + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + return GNUNET_OK; +} +#endif + + +/** + * Convert a string to an IP address. + * + * @param client where to send the IP address + * @param hostname the hostname to resolve + * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" + */ +static void +get_ip_from_hostname (struct GNUNET_SERVER_Client *client, + const char *hostname, int domain) +{ + int ret; + struct GNUNET_SERVER_TransmitContext *tc; + + tc = GNUNET_SERVER_transmit_context_create (client); + ret = GNUNET_NO; +#if HAVE_GETADDRINFO + if (ret == GNUNET_NO) + ret = getaddrinfo_resolve (tc, hostname, domain); +#endif +#if HAVE_GETHOSTBYNAME2 + if (ret == GNUNET_NO) + ret = gethostbyname2_resolve (tc, hostname, domain); +#endif +#if HAVE_GETHOSTBYNAME + if ((ret == GNUNET_NO) && ((domain == AF_UNSPEC) || (domain == PF_INET))) + gethostbyname_resolve (tc, hostname); +#endif + GNUNET_SERVER_transmit_context_append (tc, NULL, 0, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Handle GET-message. + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + */ +static void +handle_get (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + uint16_t msize; + const struct GNUNET_RESOLVER_GetMessage *msg; + const char *hostname; + uint16_t size; + int direction; + int domain; + + msize = ntohs (message->size); + if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msg = (const struct GNUNET_RESOLVER_GetMessage *) message; + size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage); + direction = ntohl (msg->direction); + domain = ntohl (msg->domain); + if (direction == GNUNET_NO) + { + /* IP from hostname */ + hostname = (const char *) &msg[1]; + if (hostname[size - 1] != '\0') + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver asked to look up `%s'.\n"), hostname); +#endif + get_ip_from_hostname (client, hostname, domain); + } + else + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver asked to look up IP address.\n")); +#endif + get_ip_as_string (client, (const struct sockaddr *) &msg[1], size); + } +} + + +/** + * List of handlers for the messages understood by this + * service. + */ +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0}, + {NULL, NULL, 0, 0} +}; + + +/** + * Process resolver requests. + * + * @param cls closure + * @param sched scheduler to use + * @param server the initialized server + * @param cfg configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_SERVER_add_handlers (server, handlers); +} + + +/** + * The main function for the resolver service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + int ret; + struct IPCache *pos; + + ret = (GNUNET_OK == + GNUNET_SERVICE_run (argc, + argv, + "resolver", &run, NULL, NULL, NULL)) ? 0 : 1; + + while (head != NULL) + { + pos = head->next; + GNUNET_free_non_null (head->addr); + GNUNET_free (head->sa); + GNUNET_free (head); + head = pos; + } + return ret; +} + +/* end of gnunet-service-resolver.c */ diff --git a/src/util/resolver.h b/src/util/resolver.h new file mode 100644 index 000000000..31637f01d --- /dev/null +++ b/src/util/resolver.h @@ -0,0 +1,63 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + 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 + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + 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. +*/ + +/** + * @author Christian Grothoff + * @file resolver/resolver.h + */ +#ifndef RESOLVER_H +#define RESOLVER_H + +#include "gnunet_common.h" + +#define DEBUG_RESOLVER GNUNET_NO + +/** + * Request for the resolver. Followed by either + * the "struct sockaddr" or the 0-terminated hostname. + * + * The response will be one or more messages of type + * RESOLVER_RESPONSE, each with the message header + * immediately followed by the requested data + * (hostname or struct sockaddr, depending on direction). + * The last RESOLVER_RESPONSE will just be a header + * without any data (used to indicate the end of the list). + */ +struct GNUNET_RESOLVER_GetMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_STATISTICS_VALUE + */ + struct GNUNET_MessageHeader header; + + /** + * GNUNET_YES to get hostname from IP, + * GNUNET_NO to get IP from hostname. + */ + int32_t direction GNUNET_PACKED; + + /** + * Domain to use (AF_INET, AF_INET6 or AF_UNSPEC). + */ + int32_t domain GNUNET_PACKED; + +}; + +#endif diff --git a/src/util/resolver_api.c b/src/util/resolver_api.c new file mode 100644 index 000000000..dcda3141e --- /dev/null +++ b/src/util/resolver_api.c @@ -0,0 +1,589 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + 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 + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + 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. +*/ + +/** + * @file resolver/resolver_api.c + * @brief resolver for writing a tool + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_client_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_resolver_service.h" +#include "gnunet_server_lib.h" +#include "resolver.h" + + +/** + * FIXME. + */ +struct GetAddressContext +{ + + /** + * FIXME. + */ + GNUNET_RESOLVER_AddressCallback callback; + + /** + * Closure for "callback". + */ + void *cls; + + /** + * FIXME. + */ + struct GNUNET_RESOLVER_GetMessage *msg; + + /** + * FIXME. + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * FIXME. + */ + struct GNUNET_TIME_Absolute timeout; +}; + + +/** + * Possible hostnames for "loopback". + */ +static const char *loopback[] = { + "localhost", + "127.0.0.1", + "ip6-localnet", + "::1", + NULL +}; + + +/** + * Check that the resolver service runs on localhost + * (or equivalent). + */ +static void +check_config (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *hostname; + unsigned int i; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "resolver", + "HOSTNAME", + &hostname)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Must specify `%s' for `%s' in configuration!\n"), + "HOSTNAME", + "resolver"); + GNUNET_assert (0); + } + i = 0; + while (loopback[i] != NULL) + if (0 == strcmp (loopback[i++], hostname)) + { + GNUNET_free (hostname); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Must specify `%s' for `%s' in configuration!\n"), + "localhost", + "resolver"); + GNUNET_free (hostname); + GNUNET_assert (0); +} + + +/** + * Convert IP address to string without DNS resolution. + */ +static char * +no_resolve (const struct sockaddr *sa, socklen_t salen) +{ + char *ret; + char inet4[INET_ADDRSTRLEN]; + char inet6[INET6_ADDRSTRLEN]; + + if (salen < sizeof (struct sockaddr)) + return NULL; + switch (sa->sa_family) + { + case AF_INET: + if (salen != sizeof (struct sockaddr_in)) + return NULL; + inet_ntop (AF_INET, + &((struct sockaddr_in *) sa)->sin_addr, + inet4, INET_ADDRSTRLEN); + ret = GNUNET_strdup (inet4); + break; + case AF_INET6: + if (salen != sizeof (struct sockaddr_in6)) + return NULL; + inet_ntop (AF_INET6, + &((struct sockaddr_in6 *) sa)->sin6_addr, + inet6, INET6_ADDRSTRLEN); + ret = GNUNET_strdup (inet6); + break; + default: + ret = NULL; + break; + } + return ret; +} + + +static void +handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GetAddressContext *gac = cls; + uint16_t size; + const struct sockaddr *sa; + socklen_t salen; + + + if (msg == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Timeout trying to resolve hostname.\n")); + gac->callback (gac->cls, NULL, 0); + GNUNET_CLIENT_disconnect (gac->client); + GNUNET_free (gac); + return; + } + if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type)) + { + GNUNET_break (0); + gac->callback (gac->cls, NULL, 0); + GNUNET_CLIENT_disconnect (gac->client); + GNUNET_free (gac); + return; + } + + size = ntohs (msg->size); + if (size == sizeof (struct GNUNET_MessageHeader)) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Received end message resolving hostname.\n")); +#endif + gac->callback (gac->cls, NULL, 0); + GNUNET_CLIENT_disconnect (gac->client); + GNUNET_free (gac); + return; + } + sa = (const struct sockaddr *) &msg[1]; + salen = size - sizeof (struct GNUNET_MessageHeader); + if (salen < sizeof (struct sockaddr)) + { + GNUNET_break (0); + gac->callback (gac->cls, NULL, 0); + GNUNET_CLIENT_disconnect (gac->client); + GNUNET_free (gac); + return; + } +#if DEBUG_RESOLVER + { + char *ips = no_resolve (sa, salen); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), ips); + GNUNET_free (ips); + } +#endif + gac->callback (gac->cls, sa, salen); + GNUNET_CLIENT_receive (gac->client, + &handle_address_response, + gac, + GNUNET_TIME_absolute_get_remaining (gac->timeout)); +} + + +static size_t +transmit_get_ip (void *cls, size_t size, void *buf) +{ + struct GetAddressContext *actx = cls; + uint16_t ms; + + if (buf == NULL) + { + /* timeout / error */ + GNUNET_free (actx->msg); + actx->callback (actx->cls, NULL, 0); + GNUNET_CLIENT_disconnect (actx->client); + GNUNET_free (actx); + return 0; + } + ms = ntohs (actx->msg->header.size); + GNUNET_assert (size >= ms); + memcpy (buf, actx->msg, ms); + GNUNET_free (actx->msg); + actx->msg = NULL; + GNUNET_CLIENT_receive (actx->client, + &handle_address_response, + actx, + GNUNET_TIME_absolute_get_remaining (actx->timeout)); + return ms; +} + + + +/** + * Convert a string to one or more IP addresses. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param hostname the hostname to resolve + * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" + * @param callback function to call with addresses + * @param callback_cls closure for callback + * @param timeout how long to try resolving + */ +void +GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *hostname, + int domain, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_AddressCallback callback, + void *callback_cls) +{ + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_RESOLVER_GetMessage *msg; + struct GetAddressContext *actx; + size_t slen; + unsigned int i; + struct sockaddr_in v4; + struct sockaddr_in6 v6; + + check_config (cfg); + i = 0; + while (loopback[i] != NULL) + if (0 == strcmp (loopback[i++], hostname)) + { + memset (&v4, 0, sizeof(v4)); +#if HAVE_SOCKADDR_IN_SIN_LEN + v4.sin_len = sizeof (v4); +#endif + v4.sin_family = AF_INET; + v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + memset (&v6, 0, sizeof(v6)); +#if HAVE_SOCKADDR_IN_SIN_LEN + v6.sin6_len = sizeof (v6); +#endif + v6.sin6_family = AF_INET6; + v6.sin6_addr = in6addr_loopback; + + switch (domain) + { + case AF_INET: + callback (callback_cls, + (const struct sockaddr*) &v4, + sizeof(v4)); + break; + case AF_INET6: + callback (callback_cls, + (const struct sockaddr*) &v6, + sizeof(v6)); + break; + case AF_UNSPEC: + callback (callback_cls, + (const struct sockaddr*) &v4, + sizeof(v4)); + callback (callback_cls, + (const struct sockaddr*) &v6, + sizeof(v6)); + break; + } + callback (callback_cls, NULL, 0); + return; + } + slen = strlen (hostname) + 1; + if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) > + GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + callback (callback_cls, NULL, 0); + return; + } + client = GNUNET_CLIENT_connect (sched, "resolver", cfg); + if (client == NULL) + { + callback (callback_cls, NULL, 0); + return; + } + msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen); + msg->header.size = + htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); + msg->direction = htonl (GNUNET_NO); + msg->domain = htonl (domain); + memcpy (&msg[1], hostname, slen); + actx = GNUNET_malloc (sizeof (struct GetAddressContext)); + actx->callback = callback; + actx->cls = callback_cls; + actx->client = client; + actx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + actx->msg = msg; + +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver requests DNS resolution of hostname `%s'.\n"), + hostname); +#endif + if (NULL == + GNUNET_CLIENT_notify_transmit_ready (client, + slen + + sizeof (struct + GNUNET_RESOLVER_GetMessage), + timeout, &transmit_get_ip, actx)) + { + GNUNET_free (msg); + GNUNET_free (actx); + callback (callback_cls, NULL, 0); + GNUNET_CLIENT_disconnect (client); + return; + } +} + + +struct GetHostnameContext +{ + GNUNET_RESOLVER_HostnameCallback callback; + void *cls; + struct GNUNET_RESOLVER_GetMessage *msg; + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_TIME_Absolute timeout; +}; + + +static void +handle_hostname_response (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GetHostnameContext *ghc = cls; + uint16_t size; + const char *hostname; + + if (msg == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Timeout trying to resolve IP address.\n")); + ghc->callback (ghc->cls, NULL); + GNUNET_CLIENT_disconnect (ghc->client); + GNUNET_free (ghc); + return; + } + size = ntohs (msg->size); + if (size == sizeof (struct GNUNET_MessageHeader)) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Received end message resolving IP address.\n")); +#endif + ghc->callback (ghc->cls, NULL); + GNUNET_CLIENT_disconnect (ghc->client); + GNUNET_free (ghc); + return; + } + hostname = (const char *) &msg[1]; + if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0') + { + GNUNET_break (0); + ghc->callback (ghc->cls, NULL); + GNUNET_CLIENT_disconnect (ghc->client); + GNUNET_free (ghc); + return; + } +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver returns `%s'.\n"), hostname); +#endif + ghc->callback (ghc->cls, hostname); + GNUNET_CLIENT_receive (ghc->client, + &handle_hostname_response, + ghc, + GNUNET_TIME_absolute_get_remaining (ghc->timeout)); +} + + +static size_t +transmit_get_hostname (void *cls, size_t size, void *buf) +{ + struct GetHostnameContext *hctx = cls; + uint16_t msize; + + if (buf == NULL) + { + GNUNET_free (hctx->msg); + hctx->callback (hctx->cls, NULL); + GNUNET_CLIENT_disconnect (hctx->client); + GNUNET_free (hctx); + return 0; + } + msize = ntohs (hctx->msg->header.size); + GNUNET_assert (size >= msize); + memcpy (buf, hctx->msg, msize); + GNUNET_free (hctx->msg); + hctx->msg = NULL; + GNUNET_CLIENT_receive (hctx->client, + &handle_hostname_response, + hctx, + GNUNET_TIME_absolute_get_remaining (hctx->timeout)); + return msize; +} + + + + +/** + * Get an IP address as a string. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param sa host address + * @param salen length of host address + * @param do_resolve use GNUNET_NO to return numeric hostname + * @param timeout how long to try resolving + * @param callback function to call with hostnames + * @param cls closure for callback + */ +void +GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct sockaddr *sa, + socklen_t salen, + int do_resolve, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_HostnameCallback callback, + void *cls) +{ + char *result; + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_RESOLVER_GetMessage *msg; + struct GetHostnameContext *hctx; + + check_config (cfg); + if (GNUNET_NO == do_resolve) + { + result = no_resolve (sa, salen); +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver returns `%s'.\n"), result); +#endif + callback (cls, result); + if (result != NULL) + { + GNUNET_free (result); + callback (cls, NULL); + } + return; + } + if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) > + GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + callback (cls, NULL); + return; + } + client = GNUNET_CLIENT_connect (sched, "resolver", cfg); + if (client == NULL) + { + callback (cls, NULL); + return; + } + msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen); + msg->header.size = + htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); + msg->direction = htonl (GNUNET_YES); + msg->domain = htonl (sa->sa_family); + memcpy (&msg[1], sa, salen); +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver requests DNS resolution of IP address.\n")); +#endif + hctx = GNUNET_malloc (sizeof (struct GetHostnameContext)); + hctx->callback = callback; + hctx->cls = cls; + hctx->client = client; + hctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + hctx->msg = msg; + if (NULL == + GNUNET_CLIENT_notify_transmit_ready (client, + sizeof (struct + GNUNET_RESOLVER_GetMessage) + + salen, timeout, + &transmit_get_hostname, hctx)) + { + GNUNET_free (msg); + callback (cls, NULL); + GNUNET_CLIENT_disconnect (client); + GNUNET_free (hctx); + } +} + +/** + * Maximum supported length of hostname + */ +#define MAX_HOSTNAME 1024 + + +/** + * Resolve our hostname to an IP address. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" + * @param callback function to call with addresses + * @param cls closure for callback + * @param timeout how long to try resolving + */ +void +GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_CONFIGURATION_Handle *cfg, + int domain, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_AddressCallback callback, + void *cls) +{ + char hostname[MAX_HOSTNAME]; + + check_config (cfg); + if (0 != gethostname (hostname, sizeof (hostname) - 1)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | + GNUNET_ERROR_TYPE_BULK, "gethostname"); + callback (cls, NULL, 0); + return; + } +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolving our hostname `%s'\n"), hostname); +#endif + GNUNET_RESOLVER_ip_get (sched, + cfg, hostname, domain, timeout, callback, cls); +} + + + + +/* end of resolver_api.c */ diff --git a/src/util/server.c b/src/util/server.c index 086c94996..303c370db 100644 --- a/src/util/server.c +++ b/src/util/server.c @@ -222,12 +222,6 @@ struct GNUNET_SERVER_Client */ struct GNUNET_TIME_Absolute last_activity; - /** - * Current task identifier for the receive call - * (or GNUNET_SCHEDULER_NO_TASK for none). - */ - GNUNET_SCHEDULER_TaskIdentifier my_receive; - /** * How many bytes in the "incoming_buffer" are currently * valid? (starting at offset 0). @@ -626,7 +620,6 @@ shutdown_incoming_processing (struct GNUNET_SERVER_Client *client) struct NotifyList *n; unsigned int rc; - GNUNET_assert (client->my_receive == GNUNET_SCHEDULER_NO_TASK); rc = client->reference_count; if (client->server != NULL) { @@ -745,7 +738,6 @@ process_incoming (void *cls, const char *cbuf = buf; size_t maxcpy; - client->my_receive = GNUNET_SCHEDULER_NO_TASK; if ((buf == NULL) || (available == 0) || (errCode != 0) || @@ -809,10 +801,10 @@ process_incoming (void *cls, (GNUNET_YES != client->shutdown_now) && (client->server != NULL)) { /* Finally, keep receiving! */ - client->my_receive = client->receive (client->client_closure, - GNUNET_SERVER_MAX_MESSAGE_SIZE, - server->idle_timeout, - &process_incoming, client); + client->receive (client->client_closure, + GNUNET_SERVER_MAX_MESSAGE_SIZE, + server->idle_timeout, + &process_incoming, client); } if (GNUNET_YES == client->shutdown_now) shutdown_incoming_processing (client); @@ -830,10 +822,10 @@ restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) process_client_buffer (client); if (0 == client->suspended) - client->my_receive = client->receive (client->client_closure, - GNUNET_SERVER_MAX_MESSAGE_SIZE, - client->server->idle_timeout, - &process_incoming, client); + client->receive (client->client_closure, + GNUNET_SERVER_MAX_MESSAGE_SIZE, + client->server->idle_timeout, + &process_incoming, client); } @@ -849,10 +841,10 @@ add_client (struct GNUNET_SERVER_Handle *server, client->last_activity = GNUNET_TIME_absolute_get (); client->next = server->clients; server->clients = client; - client->my_receive = client->receive (client->client_closure, - GNUNET_SERVER_MAX_MESSAGE_SIZE, - server->idle_timeout, - &process_incoming, client); + client->receive (client->client_closure, + GNUNET_SERVER_MAX_MESSAGE_SIZE, + server->idle_timeout, + &process_incoming, client); } @@ -864,15 +856,14 @@ add_client (struct GNUNET_SERVER_Handle *server, * @param timeout when should this operation time out * @param receiver function to call for processing * @param receiver_cls closure for receiver - * @return task identifier that can be used to cancel the operation */ -static GNUNET_SCHEDULER_TaskIdentifier +static void sock_receive (void *cls, size_t max, struct GNUNET_TIME_Relative timeout, GNUNET_CONNECTION_Receiver receiver, void *receiver_cls) { - return GNUNET_CONNECTION_receive (cls, max, timeout, receiver, receiver_cls); + GNUNET_CONNECTION_receive (cls, max, timeout, receiver, receiver_cls); } @@ -880,12 +871,11 @@ sock_receive (void *cls, * Wrapper to cancel receiving from a socket. * * @param cls handle to the GNUNET_CONNECTION_Handle to cancel - * @param ti task ID that was returned by GNUNET_CONNECTION_receive */ static void -sock_receive_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti) +sock_receive_cancel (void *cls) { - GNUNET_CONNECTION_receive_cancel (cls, ti); + GNUNET_CONNECTION_receive_cancel (cls); } @@ -1112,9 +1102,7 @@ GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client) { if (client->server == NULL) return; /* already disconnected */ - GNUNET_assert (client->my_receive != GNUNET_SCHEDULER_NO_TASK); - client->receive_cancel (client->client_closure, client->my_receive); - client->my_receive = GNUNET_SCHEDULER_NO_TASK; + client->receive_cancel (client->client_closure); shutdown_incoming_processing (client); } diff --git a/src/util/test_client.c b/src/util/test_client.c index 008e686a3..cda8025ac 100644 --- a/src/util/test_client.c +++ b/src/util/test_client.c @@ -185,6 +185,8 @@ check () GNUNET_CONFIGURATION_set_value_number (cfg, MYNAME, "PORT", PORT); GNUNET_CONFIGURATION_set_value_string (cfg, MYNAME, "HOSTNAME", "localhost"); + GNUNET_CONFIGURATION_set_value_string (cfg, + "resolver", "HOSTNAME", "localhost"); ok = 1; GNUNET_SCHEDULER_run (&task, &ok); GNUNET_CONFIGURATION_destroy (cfg); diff --git a/src/util/test_connection.c b/src/util/test_connection.c index f3a907165..888b9d015 100644 --- a/src/util/test_connection.c +++ b/src/util/test_connection.c @@ -27,7 +27,7 @@ #include "gnunet_scheduler_lib.h" #include "gnunet_time_lib.h" -#define VERBOSE GNUNET_NO +#define VERBOSE GNUNET_YES #define PORT 12435 @@ -42,7 +42,7 @@ static size_t sofar; static struct GNUNET_NETWORK_Handle *ls; - +static struct GNUNET_CONFIGURATION_Handle *cfg; /** * Create and initialize a listen socket for the server. @@ -154,7 +154,8 @@ task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) lsock = GNUNET_CONNECTION_create_from_existing (tc->sched, ls, 0); GNUNET_assert (lsock != NULL); csock = GNUNET_CONNECTION_create_from_connect (tc->sched, - "localhost", PORT, 1024); + cfg, + "localhost", PORT, 1024); GNUNET_assert (csock != NULL); #if VERBOSE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test asks for write notification\n"); @@ -190,7 +191,11 @@ check () int ok; ok = 1; + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_string (cfg, + "resolver", "HOSTNAME", "localhost"); GNUNET_SCHEDULER_run (&task, &ok); + GNUNET_CONFIGURATION_destroy (cfg); return ok; } diff --git a/src/util/test_connection_receive_cancel.c b/src/util/test_connection_receive_cancel.c index 49df185b7..6434228f7 100644 --- a/src/util/test_connection_receive_cancel.c +++ b/src/util/test_connection_receive_cancel.c @@ -40,9 +40,7 @@ static struct GNUNET_CONNECTION_Handle *lsock; static struct GNUNET_NETWORK_Handle *ls; -static GNUNET_SCHEDULER_TaskIdentifier receive_task; - - +static struct GNUNET_CONFIGURATION_Handle *cfg; /** @@ -95,12 +93,11 @@ run_accept_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_assert (asock != NULL); GNUNET_assert (GNUNET_YES == GNUNET_CONNECTION_check (asock)); GNUNET_CONNECTION_destroy (lsock); - receive_task - = GNUNET_CONNECTION_receive (asock, - 1024, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 5), &dead_receive, - cls); + GNUNET_CONNECTION_receive (asock, + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 5), &dead_receive, + cls); } @@ -108,7 +105,7 @@ static void receive_cancel_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { int *ok = cls; - GNUNET_CONNECTION_receive_cancel (asock, receive_task); + GNUNET_CONNECTION_receive_cancel (asock); GNUNET_CONNECTION_destroy (csock); GNUNET_CONNECTION_destroy (asock); *ok = 0; @@ -122,8 +119,8 @@ task_receive_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) ls = open_listen_socket (); lsock = GNUNET_CONNECTION_create_from_existing (tc->sched, ls, 0); GNUNET_assert (lsock != NULL); - csock = GNUNET_CONNECTION_create_from_connect (tc->sched, - "localhost", PORT, 1024); + csock = GNUNET_CONNECTION_create_from_connect (tc->sched, cfg, + "localhost", PORT, 1024); GNUNET_assert (csock != NULL); GNUNET_SCHEDULER_add_read_net (tc->sched, GNUNET_NO, @@ -150,7 +147,11 @@ check_receive_cancel () int ok; ok = 1; + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_string (cfg, + "resolver", "HOSTNAME", "localhost"); GNUNET_SCHEDULER_run (&task_receive_cancel, &ok); + GNUNET_CONFIGURATION_destroy (cfg); return ok; } diff --git a/src/util/test_connection_timeout.c b/src/util/test_connection_timeout.c index 2a1ce58f3..73433e434 100644 --- a/src/util/test_connection_timeout.c +++ b/src/util/test_connection_timeout.c @@ -37,6 +37,8 @@ static struct GNUNET_CONNECTION_Handle *lsock; static struct GNUNET_NETWORK_Handle *ls; +static struct GNUNET_CONFIGURATION_Handle *cfg; + /** * Create and initialize a listen socket for the server. @@ -105,8 +107,8 @@ task_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) ls = open_listen_socket (); lsock = GNUNET_CONNECTION_create_from_existing (tc->sched, ls, 0); GNUNET_assert (lsock != NULL); - csock = GNUNET_CONNECTION_create_from_connect (tc->sched, - "localhost", PORT, 1024); + csock = GNUNET_CONNECTION_create_from_connect (tc->sched, cfg, + "localhost", PORT, 1024); GNUNET_assert (csock != NULL); GNUNET_assert (NULL != GNUNET_CONNECTION_notify_transmit_ready (csock, @@ -126,7 +128,11 @@ check_timeout () int ok; ok = 1; + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_string (cfg, + "resolver", "HOSTNAME", "localhost"); GNUNET_SCHEDULER_run (&task_timeout, &ok); + GNUNET_CONFIGURATION_destroy (cfg); return ok; } diff --git a/src/util/test_connection_timeout_no_connect.c b/src/util/test_connection_timeout_no_connect.c index 6a6d32f38..84d50b4fb 100644 --- a/src/util/test_connection_timeout_no_connect.c +++ b/src/util/test_connection_timeout_no_connect.c @@ -33,6 +33,8 @@ static struct GNUNET_CONNECTION_Handle *csock; +static struct GNUNET_CONFIGURATION_Handle *cfg; + static size_t handle_timeout (void *cls, size_t size, void *buf) { @@ -51,8 +53,8 @@ handle_timeout (void *cls, size_t size, void *buf) static void task_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - csock = GNUNET_CONNECTION_create_from_connect (tc->sched, - "localhost", PORT, 1024); + csock = GNUNET_CONNECTION_create_from_connect (tc->sched, cfg, + "localhost", PORT, 1024); GNUNET_assert (csock != NULL); GNUNET_assert (NULL != GNUNET_CONNECTION_notify_transmit_ready (csock, @@ -72,7 +74,11 @@ check_timeout () int ok; ok = 1; + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_string (cfg, + "resolver", "HOSTNAME", "localhost"); GNUNET_SCHEDULER_run (&task_timeout, &ok); + GNUNET_CONFIGURATION_destroy (cfg); return ok; } diff --git a/src/util/test_connection_transmit_cancel.c b/src/util/test_connection_transmit_cancel.c index 642ea0d4c..5ec19bdb1 100644 --- a/src/util/test_connection_transmit_cancel.c +++ b/src/util/test_connection_transmit_cancel.c @@ -31,6 +31,8 @@ #define PORT 12435 +static struct GNUNET_CONFIGURATION_Handle *cfg; + static size_t not_run (void *cls, size_t size, void *buf) @@ -48,8 +50,8 @@ task_transmit_cancel (void *cls, struct GNUNET_CONNECTION_TransmitHandle *th; struct GNUNET_CONNECTION_Handle *csock; - csock = GNUNET_CONNECTION_create_from_connect (tc->sched, - "localhost", PORT, 1024); + csock = GNUNET_CONNECTION_create_from_connect (tc->sched, cfg, + "localhost", PORT, 1024); GNUNET_assert (csock != NULL); th = GNUNET_CONNECTION_notify_transmit_ready (csock, 12, @@ -72,7 +74,11 @@ check_transmit_cancel () int ok; ok = 1; + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_string (cfg, + "resolver", "HOSTNAME", "localhost"); GNUNET_SCHEDULER_run (&task_transmit_cancel, &ok); + GNUNET_CONFIGURATION_destroy (cfg); return ok; } diff --git a/src/util/test_resolver_api.c b/src/util/test_resolver_api.c new file mode 100644 index 000000000..295217c30 --- /dev/null +++ b/src/util/test_resolver_api.c @@ -0,0 +1,212 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + 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 + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + 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. +*/ +/** + * @file resolver/test_resolver_api.c + * @brief testcase for resolver_api.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_resolver_service.h" +#include "resolver.h" + +#define VERBOSE GNUNET_NO + + +static void +check_hostname (void *cls, const struct sockaddr *sa, socklen_t salen) +{ + int *ok = cls; + + if (salen == 0) + { + (*ok) &= ~8; + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Got IP address `%s' for our host.\n"), + GNUNET_a2s (sa, salen)); +} + + +static void +check_localhost_num (void *cls, const char *hostname) +{ + int *ok = cls; + if (hostname == NULL) + return; + if (0 == strcmp (hostname, "127.0.0.1")) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received correct hostname `%s'.\n", hostname); +#endif + (*ok) &= ~4; + } + else + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received invalid hostname `%s'.\n", hostname); +#endif + GNUNET_break (0); + } +} + +static void +check_localhost (void *cls, const char *hostname) +{ + int *ok = cls; + if (hostname == NULL) + return; + if (0 == strcmp (hostname, "localhost")) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received correct hostname `%s'.\n", hostname); +#endif + (*ok) &= ~2; + } + else + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received invalid hostname `%s'.\n", hostname); +#endif + GNUNET_break (0); + } +} + +static void +check_127 (void *cls, const struct sockaddr *sa, socklen_t salen) +{ + int *ok = cls; + const struct sockaddr_in *sai = (const struct sockaddr_in *) sa; + + if (sa == NULL) + return; + GNUNET_assert (sizeof (struct sockaddr_in) == salen); + if (sai->sin_addr.s_addr == htonl (INADDR_LOOPBACK)) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received correct address.\n"); +#endif + (*ok) &= ~1; + } + else + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received incorrect address.\n"); +#endif + GNUNET_break (0); + } +} + +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct sockaddr_in sa; + struct GNUNET_TIME_Relative timeout = + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + 2500); + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + GNUNET_RESOLVER_ip_get (sched, + cfg, + "localhost", AF_INET, timeout, &check_127, cls); + GNUNET_RESOLVER_hostname_get (sched, + cfg, + (const struct sockaddr *) &sa, + sizeof (struct sockaddr), + GNUNET_YES, timeout, &check_localhost, cls); + GNUNET_RESOLVER_hostname_get (sched, + cfg, + (const struct sockaddr *) &sa, + sizeof (struct sockaddr), + GNUNET_NO, + timeout, &check_localhost_num, cls); + GNUNET_RESOLVER_hostname_resolve (sched, + cfg, + AF_UNSPEC, timeout, &check_hostname, cls); +} + +static int +check () +{ + int ok = 1 + 2 + 4 + 8; + pid_t pid; + char *const argv[] = { "test-resolver-api", + "-c", + "test_resolver_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + pid = GNUNET_OS_start_process ("gnunet-service-resolver", + "gnunet-service-resolver", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", "test_resolver_api_data.conf", NULL); + sleep (1); + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-resolver-api", "nohelp", + options, &run, &ok); + if (0 != PLIBC_KILL (pid, SIGTERM)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + GNUNET_OS_process_wait(pid); + if (ok != 0) + fprintf (stderr, "Missed some resolutions: %u\n", ok); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-resolver-api", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + return ret; +} + +/* end of test_resolver_api.c */ diff --git a/src/util/test_resolver_api_data.conf b/src/util/test_resolver_api_data.conf new file mode 100644 index 000000000..d7a9be758 --- /dev/null +++ b/src/util/test_resolver_api_data.conf @@ -0,0 +1,6 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-statistics/ + +[resolver] +PORT = 22354 +HOSTNAME = localhost diff --git a/src/util/test_server.c b/src/util/test_server.c index 96b27558e..eadc03568 100644 --- a/src/util/test_server.c +++ b/src/util/test_server.c @@ -66,7 +66,10 @@ signal_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } -static GNUNET_SCHEDULER_TaskIdentifier +static GNUNET_SCHEDULER_TaskIdentifier ti; + + +static void my_receive (void *cls, size_t max, struct GNUNET_TIME_Relative timeout, @@ -104,12 +107,12 @@ my_receive (void *cls, default: GNUNET_assert (0); } - return ret; + ti = ret; } static void -my_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti) +my_cancel (void *cls) { GNUNET_SCHEDULER_cancel (sched, ti); } diff --git a/src/util/test_server_disconnect.c b/src/util/test_server_disconnect.c index 6c83961e0..84e9c7ccd 100644 --- a/src/util/test_server_disconnect.c +++ b/src/util/test_server_disconnect.c @@ -202,6 +202,8 @@ task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT); GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME", "localhost"); + GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME", + "localhost"); client = GNUNET_CLIENT_connect (tc->sched, "test", cfg); GNUNET_assert (client != NULL); GNUNET_CLIENT_notify_transmit_ready (client, diff --git a/src/util/test_server_with_client.c b/src/util/test_server_with_client.c index 9348f0d80..4f3048f34 100644 --- a/src/util/test_server_with_client.c +++ b/src/util/test_server_with_client.c @@ -176,6 +176,8 @@ task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT); GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME", "localhost"); + GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME", + "localhost"); client = GNUNET_CLIENT_connect (tc->sched, "test", cfg); GNUNET_assert (client != NULL); GNUNET_CLIENT_notify_transmit_ready (client, diff --git a/src/util/test_service_data.conf b/src/util/test_service_data.conf index 92c723619..d3a3e8278 100644 --- a/src/util/test_service_data.conf +++ b/src/util/test_service_data.conf @@ -24,3 +24,6 @@ ACCEPT_FROM6=::1; REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40; HOSTNAME=::1 ALLOW_SHUTDOWN=YES + +[resolver] +HOSTNAME=localhost