/*
This file is part of GNUnet.
- (C) 2011-2013 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2011-2013 GNUnet e.V.
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
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.
*/
/**
* @brief GNU Name System resolver logic
* @author Martin Schanzenbach
* @author Christian Grothoff
- *
- * TODO:
- * - GNS: handle special SRV names --- no delegation, direct lookup;
- * can likely be done in 'resolver_lookup_get_next_label'. (#3003)
- * - revocation checks (use REVOCATION service!), (#3004)
- * - DNAME support (#3005)
*/
#include "platform.h"
#include "gnunet_util_lib.h"
/**
* Default timeout for DNS lookups.
*/
-#define DNS_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
+#define DNS_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
/**
* Default timeout for VPN redirections.
/**
* Expiration time for the DNS record, 0 if we didn't
- * get anything useful (i.e. 'gethostbyname' was used).
+ * get anything useful (i.e. 'gethostbyname()' was used).
*/
uint64_t expiration_time;
/**
* ID of a task associated with the resolution process.
*/
- GNUNET_SCHEDULER_TaskIdentifier task_id;
+ struct GNUNET_SCHEDULER_Task * task_id;
/**
* The name to resolve
/**
* Use only cache
*/
- int only_cached;
+ enum GNUNET_GNS_LocalOptions options;
+
+ /**
+ * For SRV and TLSA records, the number of the
+ * protocol specified in the name. 0 if no protocol was given.
+ */
+ int protocol;
+
+ /**
+ * For SRV and TLSA records, the number of the
+ * service specified in the name. 0 if no service was given.
+ */
+ int service;
/**
* Desired type for the resolution.
*/
static const struct GNUNET_CONFIGURATION_Handle *cfg;
-#if 0
-/**
- * Check if name is in srv format (_x._y.xxx)
- *
- * @param name
- * @return #GNUNET_YES if true
- */
-static int
-is_srv (const char *name)
-{
- char *ndup;
- int ret;
-
- if (*name != '_')
- return GNUNET_NO;
- if (NULL == strstr (name, "._"))
- return GNUNET_NO;
- ret = GNUNET_YES;
- ndup = GNUNET_strdup (name);
- strtok (ndup, ".");
- if (NULL == strtok (NULL, "."))
- ret = GNUNET_NO;
- if (NULL == strtok (NULL, "."))
- ret = GNUNET_NO;
- if (NULL != strtok (NULL, "."))
- ret = GNUNET_NO;
- GNUNET_free (ndup);
- return ret;
-}
-#endif
-
/**
* Determine if this name is canonical (is a legal name in a zone, without delegation);
* Task scheduled to asynchronously fail a resolution.
*
* @param cls the 'struct GNS_ResolverHandle' of the resolution to fail
- * @param tc task context
*/
static void
-fail_resolution (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+fail_resolution (void *cls)
{
struct GNS_ResolverHandle *rh = cls;
- rh->task_id = GNUNET_SCHEDULER_NO_TASK;
+ rh->task_id = NULL;
rh->proc (rh->proc_cls, 0, NULL);
GNS_resolver_lookup_cancel (rh);
}
#if (defined WINDOWS) || (defined DARWIN)
/* Don't have this on W32, here's a naive implementation
* Was somehow removed on OS X ... */
-void *
+static void *
memrchr (const void *s,
int c,
size_t n)
/**
* Get the next, rightmost label from the name that we are trying to resolve,
- * and update the resolution position accordingly.
+ * and update the resolution position accordingly. Labels usually consist
+ * of up to 63 characters without a period ("."); however, we use a special
+ * convention to support SRV and TLSA records where the domain name
+ * includes an encoding for a service and protocol in the name. The
+ * syntax (see RFC 2782) here is "_Service._Proto.Name" and in this
+ * special case we include the "_Service._Proto" in the rightmost label.
+ * Thus, for "_443._tcp.foo.bar" we first return the label "bar" and then
+ * the label "_443._tcp.foo". The special case is detected by the
+ * presence of labels beginning with an underscore. Whenever a label
+ * begins with an underscore, it is combined with the label to its right
+ * (and the "." is preserved).
*
* @param rh handle to the resolution operation to get the next label from
* @return NULL if there are no more labels
const char *rp;
const char *dot;
size_t len;
+ char *ret;
+ char *srv_name;
+ char *proto_name;
+ struct protoent *pe;
+ struct servent *se;
if (0 == rh->name_resolution_pos)
return NULL;
- dot = memrchr (rh->name, (int) '.', rh->name_resolution_pos);
+ dot = memrchr (rh->name,
+ (int) '.',
+ rh->name_resolution_pos);
if (NULL == dot)
{
/* done, this was the last one */
rp = dot + 1;
rh->name_resolution_pos = dot - rh->name;
}
- return GNUNET_strndup (rp, len);
+ rh->protocol = 0;
+ rh->service = 0;
+ ret = GNUNET_strndup (rp, len);
+ /* If we have labels starting with underscore with label on
+ * the right (SRV/DANE/BOX case), determine port/protocol;
+ * The format of `rh->name` must be "_PORT._PROTOCOL".
+ */
+ if ( ('_' == rh->name[0]) &&
+ (NULL != (dot = memrchr (rh->name,
+ (int) '.',
+ rh->name_resolution_pos))) &&
+ ('_' == dot[1]) &&
+ (NULL == memrchr (rh->name,
+ (int) '.',
+ dot - rh->name)) )
+ {
+ srv_name = GNUNET_strndup (&rh->name[1],
+ (dot - rh->name) - 1);
+ proto_name = GNUNET_strndup (&dot[2],
+ rh->name_resolution_pos - (dot - rh->name) - 1);
+ rh->name_resolution_pos = 0;
+ pe = getprotobyname (proto_name);
+ if (NULL == pe)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Protocol `%s' unknown, skipping labels.\n"),
+ proto_name);
+ GNUNET_free (proto_name);
+ GNUNET_free (srv_name);
+ return ret;
+ }
+ se = getservbyname (srv_name,
+ proto_name);
+ if (NULL == se)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Service `%s' unknown for protocol `%s', skipping labels.\n"),
+ srv_name,
+ proto_name);
+ GNUNET_free (proto_name);
+ GNUNET_free (srv_name);
+ return ret;
+ }
+ rh->protocol = pe->p_proto;
+ rh->service = se->s_port;
+ }
+ return ret;
}
const struct sockaddr_in *sa4;
const struct sockaddr_in6 *sa6;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Received %u bytes of DNS IP data\n",
- addrlen);
if (NULL == addr)
{
rh->std_resolve = NULL;
transmit_lookup_dns_result (rh);
return;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received %u bytes of DNS IP data\n",
+ addrlen);
switch (addr->sa_family)
{
case AF_INET:
* @param tc task context
*/
static void
-recursive_resolution (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc);
+recursive_resolution (void *cls);
/**
rh->dns_request = NULL;
GNUNET_SCHEDULER_cancel (rh->task_id);
- rh->task_id = GNUNET_SCHEDULER_NO_TASK;
+ rh->task_id = NULL;
p = GNUNET_DNSPARSER_parse ((const char *) dns,
dns_len);
if (NULL == p)
{
GNUNET_free (rh->name);
rh->name = GNUNET_strdup (p->answers[0].data.hostname);
+ rh->name_resolution_pos = strlen (rh->name);
start_resolver_lookup (rh);
GNUNET_DNSPARSER_free_packet (p);
return;
}
- /* FIXME: add DNAME support */
/* convert from (parsed) DNS to (binary) GNS format! */
rd_count = p->num_answers + p->num_authority_records + p->num_additional_records;
else if (i < p->num_answers + p->num_authority_records)
rec = &p->authority_records[i - p->num_answers];
else
- rec = &p->authority_records[i - p->num_answers - p->num_authority_records];
+ rec = &p->additional_records[i - p->num_answers - p->num_authority_records];
/* As we copied the full DNS name to 'rh->ac_tail->label', this
should be the correct check to see if this record is actually
a record for our label... */
/* name is absolute, start from the beginning */
GNUNET_free (rh->name);
rh->name = GNUNET_strdup (cname);
+ rh->name_resolution_pos = strlen (rh->name);
start_resolver_lookup (rh);
}
shorten_ac = rh->ac_tail;
for (i=0;i<rd_count;i++)
{
+ if ( (0 != rh->protocol) &&
+ (0 != rh->service) &&
+ (GNUNET_GNSRECORD_TYPE_BOX != rd[i].record_type) )
+ continue; /* we _only_ care about boxed records */
+
rd_new[rd_off] = rd[i];
/* Check if the embedded name(s) end in "+", and if so,
replace the "+" with the zone at "ac_tail", changing the name
struct GNUNET_DNSPARSER_SrvRecord *srv;
off = 0;
- /* FIXME: passing rh->name here is is not necessarily what we want
- (SRV support not finished) */
- srv = GNUNET_DNSPARSER_parse_srv (rh->name,
- rd[i].data,
+ srv = GNUNET_DNSPARSER_parse_srv (rd[i].data,
rd[i].data_size,
&off);
if ( (NULL == srv) ||
}
else
{
- srv->domain_name = translate_dot_plus (rh, srv->domain_name);
srv->target = translate_dot_plus (rh, srv->target);
scratch_start = scratch_off;
if (GNUNET_OK !=
case GNUNET_GNSRECORD_TYPE_NICK:
{
const char *nick;
+
nick = rd[i].data;
if ((rd[i].data_size > 0) &&
(nick[rd[i].data_size -1] != '\0'))
case GNUNET_GNSRECORD_TYPE_GNS2DNS:
{
/* delegation to DNS */
+ if (GNUNET_GNSRECORD_TYPE_GNS2DNS == rh->record_type)
+ {
+ rd_off++;
+ break; /* do not follow to DNS, we wanted the GNS2DNS record! */
+ }
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Found GNS2DNS record, delegating to DNS!\n");
goto do_recurse;
}
+ case GNUNET_GNSRECORD_TYPE_BOX:
+ {
+ /* unbox SRV/TLSA records if a specific one was requested */
+ if ( (0 != rh->protocol) &&
+ (0 != rh->service) &&
+ (rd[i].data_size >= sizeof (struct GNUNET_GNSRECORD_BoxRecord)) )
+ {
+ const struct GNUNET_GNSRECORD_BoxRecord *box;
+
+ box = rd[i].data;
+ if ( (ntohs (box->protocol) == rh->protocol) &&
+ (ntohs (box->service) == rh->service) )
+ {
+ /* Box matches, unbox! */
+ rd_new[rd_off].record_type = ntohl (box->record_type);
+ rd_new[rd_off].data_size -= sizeof (struct GNUNET_GNSRECORD_BoxRecord);
+ rd_new[rd_off].data = &box[1];
+ rd_off++;
+ }
+ }
+ else
+ {
+ /* no specific protocol/service specified, preserve all BOX
+ records (for modern, GNS-enabled applications) */
+ rd_off++;
+ }
+ }
default:
rd_off++;
break;
resolver to use */
g2dc = GNUNET_new (struct Gns2DnsContext);
g2dc->ns = ns;
+
g2dc->rh = GNUNET_new (struct GNS_ResolverHandle);
g2dc->rh->authority_zone = rh->ac_tail->authority_info.gns_authority;
ip = translate_dot_plus (rh, ip);
g2dc->rh->proc = &handle_gns2dns_result;
g2dc->rh->proc_cls = rh;
g2dc->rh->record_type = GNUNET_GNSRECORD_TYPE_ANY;
- g2dc->rh->only_cached = GNUNET_NO;
+ g2dc->rh->options = GNUNET_GNS_LO_DEFAULT;
g2dc->rh->loop_limiter = rh->loop_limiter + 1;
rh->g2dc = g2dc;
start_resolver_lookup (g2dc->rh);
GNUNET_assert (NULL != rh->namecache_qe);
rh->namecache_qe = NULL;
- if ( (GNUNET_NO == rh->only_cached) &&
+ if ( ( (GNUNET_GNS_LO_DEFAULT == rh->options) ||
+ ( (GNUNET_GNS_LO_LOCAL_MASTER == rh->options) &&
+ (ac != rh->ac_head) ) ) &&
( (NULL == block) ||
(0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (block->expiration_time)).rel_value_us) ) )
{
* Task scheduled to continue with the resolution process.
*
* @param cls the `struct GNS_ResolverHandle` of the resolution
- * @param tc task context
*/
static void
-recursive_resolution (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+recursive_resolution (void *cls)
{
struct GNS_ResolverHandle *rh = cls;
- rh->task_id = GNUNET_SCHEDULER_NO_TASK;
+ rh->task_id = NULL;
if (MAX_RECURSION < rh->loop_limiter++)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
* @param record_type the record type to look up
* @param name the name to look up
* @param shorten_key a private key for use with PSEU import (can be NULL)
- * @param only_cached #GNUNET_NO to only check locally not DHT for performance
+ * @param options local options to control local lookup
* @param proc the processor to call on result
* @param proc_cls the closure to pass to @a proc
* @return handle to cancel operation
uint32_t record_type,
const char *name,
const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key,
- int only_cached,
+ enum GNUNET_GNS_LocalOptions options,
GNS_ResultProcessor proc, void *proc_cls)
{
struct GNS_ResolverHandle *rh;
rh->authority_zone = *zone;
rh->proc = proc;
rh->proc_cls = proc_cls;
- rh->only_cached = only_cached;
+ rh->options = options;
rh->record_type = record_type;
rh->name = GNUNET_strdup (name);
rh->name_resolution_pos = strlen (name);
GNUNET_free (rh->g2dc);
rh->g2dc = NULL;
}
- if (GNUNET_SCHEDULER_NO_TASK != rh->task_id)
+ if (NULL != rh->task_id)
{
GNUNET_SCHEDULER_cancel (rh->task_id);
- rh->task_id = GNUNET_SCHEDULER_NO_TASK;
+ rh->task_id = NULL;
}
if (NULL != rh->get_handle)
{
dht_lookup_heap =
GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
max_allowed_background_queries = max_bg_queries;
- if (GNUNET_SYSERR == (use_cache = GNUNET_CONFIGURATION_get_value_yesno (c,
- "gns",
- "USE_CACHE")))
+ if (GNUNET_SYSERR == (use_cache =
+ GNUNET_CONFIGURATION_get_value_yesno (c,
+ "gns",
+ "USE_CACHE")))
use_cache = GNUNET_YES;
if (GNUNET_NO == use_cache)
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Namecache disabled\n");
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Namecache disabled\n");
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (c,