/*
This file is part of GNUnet.
- (C) 2012 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2012 GNUnet e.V.
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
You should have received a copy of the GNU General Public License
along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
*/
/**
* @file dns/dnsstub.c
*/
#include "platform.h"
#include "gnunet_util_lib.h"
+#include "gnunet_tun_lib.h"
#include "gnunet_dnsstub_lib.h"
/**
*/
#define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
+/**
+ * Timeout for retrying DNS queries.
+ */
+#define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
+
/**
* How many DNS sockets do we open at most at the same time?
* (technical socket maximum is this number x2 for IPv4+IPv6)
/**
* Task for reading from dnsout4 and dnsout6.
*/
- struct GNUNET_SCHEDULER_Task * read_task;
+ struct GNUNET_SCHEDULER_Task *read_task;
+
+ /**
+ * Task for retrying transmission of the query.
+ */
+ struct GNUNET_SCHEDULER_Task *retry_task;
/**
* When should this request time out?
*/
socklen_t addrlen;
+ /**
+ * Query we sent to @e addr.
+ */
+ void *request;
+
+ /**
+ * Number of bytes in @a request.
+ */
+ size_t request_len;
+
};
};
-
/**
* We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
*
GNUNET_SCHEDULER_cancel (rs->read_task);
rs->read_task = NULL;
}
+ if (NULL != rs->retry_task)
+ {
+ GNUNET_SCHEDULER_cancel (rs->retry_task);
+ rs->retry_task = NULL;
+ }
+ if (NULL != rs->request)
+ {
+ GNUNET_free (rs->request);
+ rs->request = NULL;
+ }
}
* Read a DNS response from the (unhindered) UDP-Socket
*
* @param cls socket to read from
- * @param tc scheduler context (must be shutdown or read ready)
*/
static void
-read_response (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc);
+read_response (void *cls);
/**
struct GNUNET_DNSSTUB_RequestSocket *rs;
struct GNUNET_NETWORK_FDSet *rset;
- rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
- DNS_SOCKET_MAX)];
+ for (unsigned int i=0;i<256;i++)
+ {
+ rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
+ DNS_SOCKET_MAX)];
+ if (NULL == rs->rc)
+ break;
+ }
+ if (NULL != rs->rc)
+ {
+ /* signal "failure" */
+ rs->rc (rs->rc_cls,
+ rs,
+ NULL,
+ 0);
+ rs->rc = NULL;
+ }
rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
switch (af)
{
GNUNET_SCHEDULER_cancel (rs->read_task);
rs->read_task = NULL;
}
+ if (NULL != rs->retry_task)
+ {
+ GNUNET_SCHEDULER_cancel (rs->retry_task);
+ rs->retry_task = NULL;
+ }
+ if (NULL != rs->request)
+ {
+ GNUNET_free (rs->request);
+ rs->request = NULL;
+ }
if ( (NULL == rs->dnsout4) &&
(NULL == rs->dnsout6) )
return NULL;
rset = GNUNET_NETWORK_fdset_create ();
if (NULL != rs->dnsout4)
- GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
+ GNUNET_NETWORK_fdset_set (rset,
+ rs->dnsout4);
if (NULL != rs->dnsout6)
- GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
+ GNUNET_NETWORK_fdset_set (rset,
+ rs->dnsout6);
rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
REQUEST_TIMEOUT,
rset,
NULL,
- &read_response, rs);
+ &read_response,
+ rs);
GNUNET_NETWORK_fdset_destroy (rset);
return rs;
}
+/**
+ * Task to (re)transmit the DNS query, possibly repeatedly until
+ * we succeed.
+ *
+ * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
+ */
+static void
+transmit_query (void *cls)
+{
+ struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
+ struct GNUNET_NETWORK_Handle *ret;
+
+ rs->retry_task = NULL;
+ ret = (NULL != rs->dnsout4) ? rs->dnsout4 : rs->dnsout6;
+ GNUNET_assert (NULL != ret);
+ if (GNUNET_SYSERR ==
+ GNUNET_NETWORK_socket_sendto (ret,
+ rs->request,
+ rs->request_len,
+ (struct sockaddr *) &rs->addr,
+ rs->addrlen))
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to send DNS request to %s\n"),
+ GNUNET_a2s ((struct sockaddr *) &rs->addr,
+ rs->addrlen));
+ else
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ _("Sent DNS request to %s\n"),
+ GNUNET_a2s ((struct sockaddr *) &rs->addr,
+ rs->addrlen));
+ rs->retry_task = GNUNET_SCHEDULER_add_delayed (DNS_RETRANSMIT_DELAY,
+ &transmit_query,
+ rs);
+}
+
+
/**
* Perform DNS resolution.
*
* @param ctx stub resolver to use
* @param sa the socket address
- * @param sa_len the socket length
+ * @param sa_len the length of @a sa
* @param request DNS request to transmit
- * @param request_len number of bytes in msg
+ * @param request_len number of bytes in @a request
* @param rc function to call with result
- * @param rc_cls closure for 'rc'
+ * @param rc_cls closure for @a rc
* @return socket used for the request, NULL on error
*/
struct GNUNET_DNSSTUB_RequestSocket *
void *rc_cls)
{
struct GNUNET_DNSSTUB_RequestSocket *rs;
- struct GNUNET_NETWORK_Handle *ret;
- int af;
- af = sa->sa_family;
- if (NULL == (rs = get_request_socket (ctx, af)))
+ if (NULL == (rs = get_request_socket (ctx,
+ sa->sa_family)))
return NULL;
- if (NULL != rs->dnsout4)
- ret = rs->dnsout4;
- else
- ret = rs->dnsout6;
- GNUNET_assert (NULL != ret);
- memcpy (&rs->addr,
- sa,
- sa_len);
+ GNUNET_assert (NULL == rs->rc);
+ GNUNET_memcpy (&rs->addr,
+ sa,
+ sa_len);
rs->addrlen = sa_len;
rs->rc = rc;
rs->rc_cls = rc_cls;
- if (GNUNET_SYSERR ==
- GNUNET_NETWORK_socket_sendto (ret,
- request,
- request_len,
- sa,
- sa_len))
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _("Failed to send DNS request to %s\n"),
- GNUNET_a2s (sa, sa_len));
- else
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- _("Sent DNS request to %s\n"),
- GNUNET_a2s (sa, sa_len));
+ rs->request = GNUNET_memdup (request,
+ request_len);
+ rs->request_len = request_len;
+ rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
+ rs);
return rs;
}
memset (&v4, 0, sizeof (v4));
memset (&v6, 0, sizeof (v6));
- if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr))
+ if (1 == inet_pton (AF_INET,
+ ctx->dns_exit,
+ &v4.sin_addr))
{
salen = sizeof (v4);
v4.sin_family = AF_INET;
sa = (struct sockaddr *) &v4;
af = AF_INET;
}
- else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr))
+ else if (1 == inet_pton (AF_INET6,
+ ctx->dns_exit,
+ &v6.sin6_addr))
{
salen = sizeof (v6);
v6.sin6_family = AF_INET6;
GNUNET_break (0);
return NULL;
}
- if (NULL == (rs = get_request_socket (ctx, af)))
+ if (NULL == (rs = get_request_socket (ctx,
+ af)))
return NULL;
+ GNUNET_assert (NULL == rs->rc);
if (NULL != rs->dnsout4)
dnsout = rs->dnsout4;
else
ctx->dns_exit);
return NULL;
}
- memcpy (&rs->addr,
- sa,
- salen);
+ GNUNET_memcpy (&rs->addr,
+ sa,
+ salen);
rs->addrlen = salen;
rs->rc = rc;
rs->rc_cls = rc_cls;
if (GNUNET_SYSERR ==
GNUNET_NETWORK_socket_sendto (dnsout,
request,
- request_len, sa, salen))
+ request_len,
+ sa,
+ salen))
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_("Failed to send DNS request to %s\n"),
GNUNET_a2s (sa, salen));
rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
-
return rs;
-
}
int len;
#ifndef MINGW
- if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
+ if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout),
+ FIONREAD,
+ &len))
{
/* conservative choice: */
len = UINT16_MAX;
addrlen = sizeof (addr);
memset (&addr, 0, sizeof (addr));
r = GNUNET_NETWORK_socket_recvfrom (dnsout,
- buf, sizeof (buf),
- (struct sockaddr*) &addr, &addrlen);
+ buf,
+ sizeof (buf),
+ (struct sockaddr*) &addr,
+ &addrlen);
if (-1 == r)
{
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "recvfrom");
GNUNET_NETWORK_socket_close (dnsout);
return GNUNET_SYSERR;
}
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Received DNS response that is too small (%u bytes)"),
- r);
+ (unsigned int) r);
return GNUNET_NO;
}
dns = (struct GNUNET_TUN_DnsHeader *) buf;
if ( (addrlen != rs->addrlen) ||
- (0 != memcmp (&rs->addr,
- &addr,
- addrlen)) ||
+ (GNUNET_YES !=
+ GNUNET_TUN_sockaddr_cmp ((struct sockaddr *) &rs->addr,
+ (struct sockaddr *) &addr,
+ GNUNET_YES)) ||
(0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value_us) )
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
* Read a DNS response from the (unhindered) UDP-Socket
*
* @param cls socket to read from
- * @param tc scheduler context (must be shutdown or read ready)
*/
static void
-read_response (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+read_response (void *cls)
{
struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
struct GNUNET_NETWORK_FDSet *rset;
+ const struct GNUNET_SCHEDULER_TaskContext *tc;
rs->read_task = NULL;
+ tc = GNUNET_SCHEDULER_get_task_context ();
if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
{
- /* timeout or shutdown */
+ /* signal "failure" (from timeout) */
+ if (NULL != rs->rc)
+ {
+ rs->rc (rs->rc_cls,
+ rs,
+ NULL,
+ 0);
+ rs->rc = NULL;
+ }
+ /* timeout */
cleanup_rs (rs);
return;
}
/* read and process ready sockets */
if ((NULL != rs->dnsout4) &&
- (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
- (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
+ (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+ rs->dnsout4)) &&
+ (GNUNET_SYSERR == do_dns_read (rs,
+ rs->dnsout4)))
rs->dnsout4 = NULL;
if ((NULL != rs->dnsout6) &&
- (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
- (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
+ (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+ rs->dnsout6)) &&
+ (GNUNET_SYSERR == do_dns_read (rs,
+ rs->dnsout6)))
rs->dnsout6 = NULL;
/* re-schedule read task */
rset = GNUNET_NETWORK_fdset_create ();
if (NULL != rs->dnsout4)
- GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
+ GNUNET_NETWORK_fdset_set (rset,
+ rs->dnsout4);
if (NULL != rs->dnsout6)
- GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
+ GNUNET_NETWORK_fdset_set (rset,
+ rs->dnsout6);
rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
GNUNET_TIME_absolute_get_remaining (rs->timeout),
rset,
NULL,
- &read_response, rs);
+ &read_response,
+ rs);
GNUNET_NETWORK_fdset_destroy (rset);
}