#include "gskdnsresolver.h"
#include <string.h>
#include "../gsknameresolver.h"
#include "../gskghelpers.h"
#include "../gskerror.h"
#include "../gskmacros.h"
/**
* gsk_dns_resolver_resolve:
* @resolver: the DNS resolver which should begin processing the request.
* @recursive: whether to use recursive name resolution on the server.
* @dns_questions: list of GskDnsQuestion's to resolve.
* @func: function which will be called with answers
* to the given questions.
* @on_fail: function to call if the name cannot be resolved.
* @func_data: data to pass to @func and @on_fail.
* @destroy: function to call with the task is over.
* @hints: flags to pass to the name resolver.
*
* Begin a DNS lookup task.
*
* returns: a running DNS lookup task.
*/
GskDnsResolverTask *
gsk_dns_resolver_resolve (GskDnsResolver *resolver,
gboolean recursive,
GSList *dns_questions,
GskDnsResolverResponseFunc func,
GskDnsResolverFailFunc on_fail,
gpointer func_data,
GDestroyNotify destroy,
GskDnsResolverHints *hints)
{
GskDnsResolverIface *iface = GSK_DNS_RESOLVER_GET_IFACE (resolver);
g_return_val_if_fail (iface != NULL, NULL);
return (*iface->resolve)(resolver, recursive, dns_questions,
func, on_fail,
func_data, destroy, hints);
}
/**
* gsk_dns_resolver_cancel:
* @resolver: a resolver which is running a DNS lookup task.
* @task: a DNS lookup task to cancel.
*
* Cancel a running DNS lookup task.
*/
void
gsk_dns_resolver_cancel (GskDnsResolver *resolver,
GskDnsResolverTask *task)
{
GskDnsResolverIface *iface = GSK_DNS_RESOLVER_GET_IFACE (resolver);
(*iface->cancel) (resolver, task);
}
/* --- forward lookup --- */
typedef struct _LookupData LookupData;
struct _LookupData
{
char *host_name;
gboolean is_ipv6;
GskDnsResolverLookupFunc func;
GskDnsResolverFailFunc on_fail;
gpointer func_data;
GDestroyNotify destroy;
};
#define LOOKUP_DATA_GET_RRTYPE(lookup_data) \
((lookup_data)->is_ipv6 ? GSK_DNS_RR_HOST_ADDRESS_IPV6 \
: GSK_DNS_RR_HOST_ADDRESS)
static GskDnsResourceRecord *
list_search (GSList *rr_list,
const char *owner,
GskDnsResourceRecordType type)
{
while (rr_list != NULL)
{
GskDnsResourceRecord *record = rr_list->data;
rr_list = rr_list->next;
if (strcasecmp (record->owner, owner) == 0 && type == record->type)
return record;
}
return NULL;
}
static gboolean
list_search_questions (GSList *questions, const char *host,
GskDnsResourceRecordType type)
{
while (questions != NULL)
{
GskDnsQuestion *q = questions->data;
questions = questions->next;
if (strcmp (q->query_name, host) == 0
&& (q->query_type == GSK_DNS_RR_WILDCARD || q->query_type == type))
return TRUE;
}
return FALSE;
}
static void
lookup_data_handle_result (GSList *answers,
GSList *authority,
GSList *additional,
GSList *negatives,
gpointer handle_result_data)
{
LookupData *data = handle_result_data;
const char *host = data->host_name;
GskDnsResourceRecordType rrtype = LOOKUP_DATA_GET_RRTYPE (data);
for (;;)
{
GskDnsResourceRecord *answer;
if (list_search_questions (negatives, host, rrtype)
|| list_search_questions (negatives, host, GSK_DNS_RR_CANONICAL_NAME))
{
if (data->on_fail != NULL)
{
GError *error = g_error_new (GSK_G_ERROR_DOMAIN,
GSK_ERROR_RESOLVER_NOT_FOUND,
_("dns resolver: name not found: %s"), host);
(*data->on_fail) (error, data->func_data);
g_error_free (error);
}
return;
}
answer = list_search (answers, host, rrtype);
if (answer == NULL)
answer = list_search (authority, host, rrtype);
if (answer == NULL)
answer = list_search (additional, host, rrtype);
if (answer != NULL)
{
if (answer->type == GSK_DNS_RR_HOST_ADDRESS)
{
/* XXX: more error checking is really required!!! */
/* (basically there should be a trail of CNAME's) */
GskSocketAddress *addr;
addr = gsk_socket_address_ipv4_new (answer->rdata.a.ip_address, 0);
(*data->func) (addr, data->func_data);
g_object_unref (addr);
return;
}
}
answer = list_search (answers, host, GSK_DNS_RR_CANONICAL_NAME);
if (answer == NULL)
answer = list_search (authority, host, GSK_DNS_RR_CANONICAL_NAME);
if (answer == NULL)
answer = list_search (additional, host, GSK_DNS_RR_CANONICAL_NAME);
if (answer == NULL)
break;
/* ok, try again with the cname */
host = answer->rdata.domain_name;
}
if (data->on_fail)
{
GError *error = g_error_new (GSK_G_ERROR_DOMAIN,
GSK_ERROR_RESOLVER_NO_DATA,
_("dns resolver: got answers, but nothing good"));
(*data->on_fail) (error, data->func_data);
g_error_free (error);
}
}
static void
lookup_data_fail (GError *error,
gpointer handle_result_data)
{
LookupData *data = handle_result_data;
if (data->on_fail)
(*data->on_fail) (error, data->func_data);
}
static void
lookup_data_destroy (gpointer handle_result_data)
{
LookupData *data = handle_result_data;
if (data->destroy != NULL)
(*data->destroy) (data->func_data);
g_free (data);
}
/**
* gsk_dns_resolver_lookup:
* @resolver: DNS client to ask questions.
* @name: name of host to look up.
* @func: function to call on successful name lookup.
* @on_fail: function to call on name lookup failure.
* @func_data: data to pass to @func and @on_fail.
* @destroy: function to call when the task is destroyed.
*
* Begin a simple DNS lookup, using the underlying general resolver.
*
* TODO. IPv6 support.
*
* returns: a running DNS lookup task.
*/
GskDnsResolverTask *
gsk_dns_resolver_lookup (GskDnsResolver *resolver,
const char *name,
GskDnsResolverLookupFunc func,
GskDnsResolverFailFunc on_fail,
gpointer func_data,
GDestroyNotify destroy)
{
GskDnsQuestion question
= {
(char *) name,
GSK_DNS_RR_HOST_ADDRESS,
GSK_DNS_CLASS_INTERNET,
NULL
};
GSList question_list
= {
&question,
NULL
};
LookupData *lookup_data;
if (strspn(name, "0123456789. ") == strlen(name))
{
/* this isn't a name, it's a number.
probably an IP number. */
const char *tmp = name;
guint8 ip_address[4];
if (gsk_dns_parse_ip_address (&tmp, ip_address))
{
GskSocketAddress *socket_address;
socket_address = gsk_socket_address_ipv4_new(ip_address, 0);
(*func)(socket_address, func_data);
if (destroy != NULL)
(*destroy)(func_data);
g_object_unref (socket_address);
return NULL;
}
/* let it go on the fail through the normal code-path */
}
lookup_data = g_malloc (sizeof (LookupData) + strlen (name) + 1);
lookup_data->func = func;
lookup_data->is_ipv6 = FALSE;
lookup_data->on_fail = on_fail;
lookup_data->func_data = func_data;
lookup_data->destroy = destroy;
lookup_data->host_name = strcpy ((char*) (lookup_data + 1), name);
return gsk_dns_resolver_resolve (resolver,
TRUE,
&question_list,
lookup_data_handle_result,
lookup_data_fail,
lookup_data,
lookup_data_destroy,
NULL);
}
/* --- type implementation --- */
GType
gsk_dns_resolver_get_type (void)
{
static GType type = 0;
if (type == 0)
{
static const GTypeInfo resolver_info =
{
sizeof (GskDnsResolverIface),
NULL, /* base_init */
NULL, /* base_finalize */
NULL,
NULL, /* class_finalize */
NULL, /* class_data */
0,
0,
NULL,
NULL
};
type = g_type_register_static (G_TYPE_INTERFACE,
"GskDnsResolver",
&resolver_info,
G_TYPE_FLAG_ABSTRACT);
g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
}
return type;
}
/* --- name-resolver interface --- */
static gpointer
name_start_resolve (GskNameResolver *resolver,
GskNameResolverFamily family,
const char *name,
GskNameResolverSuccessFunc success,
GskNameResolverFailureFunc failure,
gpointer func_data,
GDestroyNotify destroy)
{
GskDnsResolver *dns_resolver = GSK_DNS_RESOLVER (resolver);
g_return_val_if_fail (family == GSK_NAME_RESOLVER_FAMILY_IPV4, NULL);
return gsk_dns_resolver_lookup (dns_resolver, name,
success, failure, func_data, destroy);
}
static gboolean
name_cancel_resolve (GskNameResolver *resolver,
gpointer handle)
{
gsk_dns_resolver_cancel (GSK_DNS_RESOLVER (resolver),
(GskDnsResolverTask *) handle);
return TRUE;
}
static void
init_name_resolver_iface (GskNameResolverIface *iface)
{
iface->start_resolve = name_start_resolve;
iface->cancel_resolve = name_cancel_resolve;
}
void
gsk_dns_resolver_add_name_resolver_iface (GType type)
{
static const GInterfaceInfo name_resolver_info =
{
(GInterfaceInitFunc) init_name_resolver_iface,
NULL,
NULL
};
g_type_add_interface_static (type,
GSK_TYPE_NAME_RESOLVER,
&name_resolver_info);
}
syntax highlighted by Code2HTML, v. 0.9.1