#include "gnunet_network_lib.h"
#include "gnunet_scheduler_lib.h"
-#define DEBUG_NETWORK GNUNET_NO
+#define DEBUG_NETWORK GNUNET_YES
+
+/**
+ * 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
{
*/
struct sockaddr *addr;
+ /**
+ * Pointer to the hostname if socket was
+ * created using DNS lookup, otherwise NULL.
+ */
+ char *hostname;
+
/**
* Pointer to our write buffer.
*/
*/
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.
*/
}
+/**
+ * Perform a DNS lookup for the hostname associated
+ * with the current socket, iterating over the address
+ * families as specified in the "address_families" array.
+ */
+static void
+try_lookup (struct GNUNET_NETWORK_SocketHandle *sock)
+{
+ struct addrinfo hints;
+ int ec;
+
+ 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;
+ }
+}
+
+
/**
* Initiate asynchronous TCP connect request.
*
}
while (1)
{
+ if (sock->ai_pos == NULL)
+ try_lookup (sock);
if (sock->ai_pos == NULL)
{
/* no more addresses to try, fatal! */
uint16_t port, size_t maxbuf)
{
struct GNUNET_NETWORK_SocketHandle *ret;
- struct addrinfo hints;
- int ec;
ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf);
ret->sock = -1;
ret->write_buffer = (char *) &ret[1];
ret->write_buffer_size = maxbuf;
ret->port = port;
- memset (&hints, 0, sizeof (hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- if (0 != (ec = getaddrinfo (hostname, NULL, &hints, &ret->ai)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
- "`%s' failed for hostname `%s': %s\n",
- "getaddrinfo", hostname, gai_strerror (ec));
- GNUNET_free (ret);
- return NULL;
- }
- ret->ai_pos = ret->ai;
+ ret->af_fam_offset = sizeof (address_families) / sizeof(address_families[0]);
+ ret->hostname = GNUNET_strdup (hostname);
if (GNUNET_SYSERR == try_connect (ret))
{
- freeaddrinfo (ret->ai);
+ if (NULL != ret->ai)
+ freeaddrinfo (ret->ai);
+ GNUNET_free (ret->hostname);
GNUNET_free (ret);
return NULL;
}
GNUNET_break (0 == CLOSE (sock->sock));
GNUNET_free_non_null (sock->addr);
if (sock->ai != NULL)
- freeaddrinfo (sock->ai);
+ freeaddrinfo (sock->ai);
+ GNUNET_free_non_null (sock->hostname);
GNUNET_free (sock);
}
sock);
return;
}
- if (sock->sock == -1)
+ 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))) )
{
#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 ((tc->write_ready == NULL) || (!FD_ISSET (sock->sock, tc->write_ready)))
+ if ((tc->write_ready == NULL) || (!FD_ISSET (sock->sock, tc->write_ready)))
{
- /* special circumstances: not yet ready to write */
+ /* special circumstances (in particular,
+ PREREQ_DONE after connect): not yet ready to write,
+ but no "fatal" error either. Hence retry. */
goto SCHEDULE_WRITE;
}
GNUNET_assert (sock->write_buffer_off >= sock->write_buffer_pos);
GNUNET_NO,
GNUNET_SCHEDULER_PRIORITY_KEEP,
GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
- GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_TIME_absolute_get_remaining (sock->nth.transmit_timeout),
sock->sock, &transmit_ready, sock);
}
&transmit_timeout,
sock);
if (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
- 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 (sock->connect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
+ sock->write_task = GNUNET_SCHEDULER_add_write (sock->sched,
+ GNUNET_NO,
+ GNUNET_SCHEDULER_PRIORITY_KEEP,
+ GNUNET_SCHEDULER_NO_PREREQUISITE_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);
+ }
return &sock->nth;
}