From 658282bdf62e165c44a5e97e22ba5cce13839044 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 1 Dec 2010 21:49:16 +0000 Subject: [PATCH] gresolv: Send A and AAAA queries, merge results for lookup callback --- gweb/gresolv.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 169 insertions(+), 36 deletions(-) diff --git a/gweb/gresolv.c b/gweb/gresolv.c index 59a445d..09509da 100644 --- a/gweb/gresolv.c +++ b/gweb/gresolv.c @@ -36,10 +36,25 @@ #include "gresolv.h" +struct resolv_lookup { + GResolv *resolv; + guint id; + + char **results; + + struct resolv_query *ipv4_query; + struct resolv_query *ipv6_query; + + guint ipv4_status; + guint ipv6_status; + + GResolvResultFunc result_func; + gpointer result_data; +}; + struct resolv_query { GResolv *resolv; - guint id; guint timeout; uint16_t msgid; @@ -62,7 +77,8 @@ struct resolv_nameserver { struct _GResolv { gint ref_count; - guint next_query_id; + guint next_lookup_id; + GQueue *lookup_queue; GQueue *query_queue; int index; @@ -98,6 +114,20 @@ static void destroy_query(struct resolv_query *query) g_free(query); } +static void destroy_lookup(struct resolv_lookup *lookup) +{ + if (lookup->ipv4_query) { + destroy_query(lookup->ipv4_query); + g_queue_remove(lookup->resolv->query_queue, lookup->ipv4_query); + } + if (lookup->ipv6_query) { + destroy_query(lookup->ipv6_query); + g_queue_remove(lookup->resolv->query_queue, lookup->ipv4_query); + } + g_strfreev(lookup->results); + g_free(lookup); +} + static gboolean query_timeout(gpointer user_data) { struct resolv_query *query = user_data; @@ -165,15 +195,15 @@ static int send_query(GResolv *resolv, const unsigned char *buf, int len) return 0; } -static gint compare_query_id(gconstpointer a, gconstpointer b) +static gint compare_lookup_id(gconstpointer a, gconstpointer b) { - const struct resolv_query *query = a; + const struct resolv_lookup *lookup = a; guint id = GPOINTER_TO_UINT(b); - if (query->id < id) + if (lookup->id < id) return -1; - if (query->id > id) + if (lookup->id > id) return 1; return 0; @@ -371,14 +401,21 @@ GResolv *g_resolv_new(int index) resolv->ref_count = 1; - resolv->next_query_id = 1; - resolv->query_queue = g_queue_new(); + resolv->next_lookup_id = 1; + resolv->query_queue = g_queue_new(); if (resolv->query_queue == NULL) { g_free(resolv); return NULL; } + resolv->lookup_queue = g_queue_new(); + if (resolv->lookup_queue == NULL) { + g_queue_free(resolv->query_queue); + g_free(resolv); + return NULL; + } + resolv->index = index; resolv->nameserver_list = NULL; @@ -411,6 +448,7 @@ void g_resolv_unref(GResolv *resolv) destroy_query(query); g_queue_free(resolv->query_queue); + g_queue_free(resolv->lookup_queue); flush_nameservers(resolv); @@ -468,13 +506,114 @@ void g_resolv_flush_nameservers(GResolv *resolv) flush_nameservers(resolv); } -guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname, - GResolvResultFunc func, gpointer user_data) +static int count_results(char **results) { - struct resolv_query *query; + int i; + + if (!results) + return 0; + + for (i = 0; results[i]; i++) + ; + + return i; +} + +static void add_result(struct resolv_lookup *lookup, char **results) +{ + int nr_new_results = count_results(results); + int nr_old_results = count_results(lookup->results); + GResolvResultStatus status; + + if (nr_new_results) { + lookup->results = g_realloc(lookup->results, sizeof(char *) * + (nr_new_results + nr_old_results + 1)); + + while (nr_new_results) { + lookup->results[nr_old_results++] = results[0]; + results[0] = NULL; + results++; + nr_new_results--; + } + lookup->results[nr_old_results] = NULL; + } + if (lookup->ipv4_query || lookup->ipv6_query) + return; + + /* FIXME: Sort results according to RFC3484 and /etc/gai.conf */ + + status = lookup->ipv4_status; + if (status == G_RESOLV_RESULT_STATUS_SUCCESS) + status = lookup->ipv6_status; + + lookup->result_func(status, lookup->results, lookup->result_data); + + g_queue_remove(lookup->resolv->lookup_queue, lookup); + destroy_lookup(lookup); +} + +static void ipv4_result(GResolvResultStatus status, char **results, gpointer data) +{ + struct resolv_lookup *lookup = data; + + lookup->ipv4_status = status; + lookup->ipv4_query = NULL; + + add_result(lookup, results); +} + +static void ipv6_result(GResolvResultStatus status, char **results, gpointer data) +{ + struct resolv_lookup *lookup = data; + + lookup->ipv6_status = status; + lookup->ipv6_query = NULL; + + add_result(lookup, results); +} + + +static gint add_query(struct resolv_lookup *lookup, const char *hostname, int type) +{ + struct resolv_query *query = g_try_new0(struct resolv_query, 1); unsigned char buf[4096]; int len; + if (query == NULL) + return -ENOMEM; + + len = res_mkquery(ns_o_query, hostname, ns_c_in, type, + NULL, 0, NULL, buf, sizeof(buf)); + + query->msgid = buf[0] << 8 | buf[1]; + + query->result_data = lookup; + + if (send_query(lookup->resolv, buf, len) < 0) + return -EIO; + + query->resolv = lookup->resolv; + + g_queue_push_tail(lookup->resolv->query_queue, query); + + query->timeout = g_timeout_add_seconds(5, query_timeout, query); + + if (type == ns_t_aaaa) { + query->result_func = ipv6_result; + lookup->ipv6_query = query; + } else { + query->result_func = ipv4_result; + lookup->ipv4_query = query; + } + + return 0; +} + +guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname, + GResolvResultFunc func, gpointer user_data) +{ + struct resolv_lookup *lookup; + debug(resolv, "lookup hostname %s", hostname); if (resolv == NULL) @@ -503,47 +642,41 @@ guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname, g_resolv_add_nameserver(resolv, "127.0.0.1", 53, 0); } - query = g_try_new0(struct resolv_query, 1); - if (query == NULL) + lookup = g_try_new0(struct resolv_lookup, 1); + if (!lookup) return 0; - query->id = resolv->next_query_id++; + lookup->resolv = resolv; + lookup->result_func = func; + lookup->result_data = user_data; + lookup->id = resolv->next_lookup_id++; - /* FIXME: Send ns_t_aaaa query too, and see the FIXME in - parse_response() re merging and sorting the results */ - len = res_mkquery(ns_o_query, hostname, ns_c_in, ns_t_a, - NULL, 0, NULL, buf, sizeof(buf)); - - query->msgid = buf[0] << 8 | buf[1]; - - query->result_func = func; - query->result_data = user_data; - - if (send_query(resolv, buf, len) < 0) { - g_free(query); + if (add_query(lookup, hostname, ns_t_a)) { + g_free(lookup); + return -EIO; + } + if (add_query(lookup, hostname, ns_t_aaaa)) { + destroy_query(lookup->ipv4_query); + g_queue_remove(resolv->query_queue, lookup->ipv4_query); + g_free(lookup); return -EIO; } - query->resolv = resolv; - - g_queue_push_tail(resolv->query_queue, query); - - query->timeout = g_timeout_add_seconds(5, query_timeout, query); - - return query->id; + g_queue_push_tail(resolv->lookup_queue, lookup); + return lookup->id; } gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id) { GList *list; - list = g_queue_find_custom(resolv->query_queue, - GUINT_TO_POINTER(id), compare_query_id); + list = g_queue_find_custom(resolv->lookup_queue, + GUINT_TO_POINTER(id), compare_lookup_id); if (list == NULL) return FALSE; - destroy_query(list->data); + destroy_lookup(list->data); g_queue_remove(resolv->query_queue, list->data); return TRUE; -- 2.7.4