summaryrefslogtreecommitdiff
path: root/gweb
diff options
context:
space:
mode:
Diffstat (limited to 'gweb')
-rw-r--r--gweb/gresolv.c194
1 files changed, 100 insertions, 94 deletions
diff --git a/gweb/gresolv.c b/gweb/gresolv.c
index 09509da1..2b686a02 100644
--- a/gweb/gresolv.c
+++ b/gweb/gresolv.c
@@ -36,11 +36,30 @@
#include "gresolv.h"
+struct sort_result {
+ int precedence;
+ int scope;
+ gboolean reachable;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } src;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } dst;
+};
+
+struct resolv_query;
+
struct resolv_lookup {
GResolv *resolv;
guint id;
- char **results;
+ int nr_results;
+ struct sort_result *results;
struct resolv_query *ipv4_query;
struct resolv_query *ipv6_query;
@@ -59,8 +78,7 @@ struct resolv_query {
uint16_t msgid;
- GResolvResultFunc result_func;
- gpointer result_data;
+ struct resolv_lookup *lookup;
};
struct resolv_nameserver {
@@ -90,6 +108,8 @@ struct _GResolv {
gpointer debug_data;
};
+static void sort_and_return_results(struct resolv_lookup *lookup);
+
static inline void debug(GResolv *resolv, const char *format, ...)
{
char str[256];
@@ -124,20 +144,27 @@ static void destroy_lookup(struct resolv_lookup *lookup)
destroy_query(lookup->ipv6_query);
g_queue_remove(lookup->resolv->query_queue, lookup->ipv4_query);
}
- g_strfreev(lookup->results);
+ g_free(lookup->results);
g_free(lookup);
}
static gboolean query_timeout(gpointer user_data)
{
struct resolv_query *query = user_data;
+ struct resolv_lookup *lookup = query->lookup;
GResolv *resolv = query->resolv;
query->timeout = 0;
- if (query->result_func != NULL)
- query->result_func(G_RESOLV_RESULT_STATUS_NO_RESPONSE,
- NULL, query->result_data);
+ if (query == lookup->ipv4_query) {
+ lookup->ipv4_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE;
+ lookup->ipv4_query = NULL;
+ } else if (query == lookup->ipv6_query) {
+ lookup->ipv6_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE;
+ lookup->ipv6_query = NULL;
+ }
+ if (!lookup->ipv4_query && !lookup->ipv4_query)
+ sort_and_return_results(lookup);
destroy_query(query);
g_queue_remove(resolv->query_queue, query);
@@ -223,13 +250,29 @@ static gint compare_query_msgid(gconstpointer a, gconstpointer b)
return 0;
}
+static void add_result(struct resolv_lookup *lookup, int family, const void *data)
+{
+ int n = lookup->nr_results++;
+ lookup->results = g_realloc(lookup->results,
+ sizeof(struct sort_result) * (n+1));
+
+ memset(&lookup->results[n], 0, sizeof(struct sort_result));
+
+ lookup->results[n].dst.sa.sa_family = family;
+ if (family == AF_INET)
+ memcpy(&lookup->results[n].dst.sin.sin_addr, data, NS_INADDRSZ);
+ else
+ memcpy(&lookup->results[n].dst.sin6.sin6_addr, data, NS_IN6ADDRSZ);
+}
+
static void parse_response(struct resolv_nameserver *nameserver,
const unsigned char *buf, int len)
{
GResolv *resolv = nameserver->resolv;
GResolvResultStatus status;
+ struct resolv_query *query;
+ struct resolv_lookup *lookup;
GList *list;
- char **results;
ns_msg msg;
ns_rr rr;
int i, n, rcode, count;
@@ -268,50 +311,45 @@ static void parse_response(struct resolv_nameserver *nameserver,
break;
}
- results = g_try_new(char *, count + 1);
- if (results == NULL)
+ list = g_queue_find_custom(resolv->query_queue,
+ GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid);
+ if (!list)
return;
- for (i = 0, n = 0; i < count; i++) {
- char result[100];
+ query = list->data;
+ lookup = query->lookup;
+
+ if (query == lookup->ipv6_query) {
+ lookup->ipv6_status = status;
+ lookup->ipv6_query = NULL;
+ } else if (query == lookup->ipv4_query) {
+ lookup->ipv4_status = status;
+ lookup->ipv4_query = NULL;
+ }
+ for (i = 0, n = 0; i < count; i++) {
ns_parserr(&msg, ns_s_an, i, &rr);
if (ns_rr_class(rr) != ns_c_in)
continue;
+ g_assert(offsetof(struct sockaddr_in, sin_addr) ==
+ offsetof(struct sockaddr_in6, sin6_flowinfo));
+
if (ns_rr_type(rr) == ns_t_a &&
ns_rr_rdlen(rr) == NS_INADDRSZ) {
- inet_ntop(AF_INET, ns_rr_rdata(rr), result, sizeof(result));
+ add_result(lookup, AF_INET, ns_rr_rdata(rr));
} else if (ns_rr_type(rr) == ns_t_aaaa &&
ns_rr_rdlen(rr) == NS_IN6ADDRSZ) {
- inet_ntop(AF_INET6, ns_rr_rdata(rr), result, sizeof(result));
- } else
- continue;
-
- results[n++] = g_strdup(result);
+ add_result(lookup, AF_INET6, ns_rr_rdata(rr));
+ }
}
- results[n] = NULL;
-
- list = g_queue_find_custom(resolv->query_queue,
- GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid);
-
- if (list != NULL) {
- struct resolv_query *query = list->data;
-
- /* FIXME: This set of results is *only* for a single A or AAAA
- query; we need to merge both results together and then sort
- them according to RFC3484. While honouring /etc/gai.conf */
- if (query->result_func != NULL)
- query->result_func(status, results,
- query->result_data);
-
- destroy_query(query);
- g_queue_remove(resolv->query_queue, query);
- }
+ if (!lookup->ipv4_query && !lookup->ipv6_query)
+ sort_and_return_results(lookup);
- g_strfreev(results);
+ destroy_query(query);
+ g_queue_remove(resolv->query_queue, query);
}
static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond,
@@ -506,73 +544,45 @@ void g_resolv_flush_nameservers(GResolv *resolv)
flush_nameservers(resolv);
}
-static int count_results(char **results)
+static void sort_and_return_results(struct resolv_lookup *lookup)
{
- int i;
+ char buf[100];
+ GResolvResultStatus status;
+ char **results = g_try_new0(char *, lookup->nr_results + 1);
+ int i, n = 0;
if (!results)
- return 0;
-
- for (i = 0; results[i]; i++)
- ;
-
- return i;
-}
+ return;
-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;
+ /* FIXME: Now sort them properly according to RFC3484 and /etc/gai.conf */
- if (nr_new_results) {
- lookup->results = g_realloc(lookup->results, sizeof(char *) *
- (nr_new_results + nr_old_results + 1));
+ for (i = 0; i < lookup->nr_results; i++) {
+ if (lookup->results[i].dst.sa.sa_family == AF_INET) {
+ if (!inet_ntop(AF_INET, &lookup->results[i].dst.sin.sin_addr,
+ buf, sizeof(buf)))
+ continue;
+ } else if (lookup->results[i].dst.sa.sa_family == AF_INET6) {
+ if (!inet_ntop(AF_INET6, &lookup->results[i].dst.sin6.sin6_addr,
+ buf, sizeof(buf)))
+ continue;
+ } else
+ continue;
- while (nr_new_results) {
- lookup->results[nr_old_results++] = results[0];
- results[0] = NULL;
- results++;
- nr_new_results--;
- }
- lookup->results[nr_old_results] = NULL;
+ results[n++] = strdup(buf);
}
- if (lookup->ipv4_query || lookup->ipv6_query)
- return;
-
- /* FIXME: Sort results according to RFC3484 and /etc/gai.conf */
+ results[n++] = NULL;
status = lookup->ipv4_status;
if (status == G_RESOLV_RESULT_STATUS_SUCCESS)
status = lookup->ipv6_status;
- lookup->result_func(status, lookup->results, lookup->result_data);
+ lookup->result_func(status, results, lookup->result_data);
+ g_strfreev(results);
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);
@@ -587,24 +597,20 @@ static gint add_query(struct resolv_lookup *lookup, const char *hostname, int ty
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;
+ query->lookup = lookup;
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;
+ if (type == ns_t_aaaa)
lookup->ipv6_query = query;
- } else {
- query->result_func = ipv4_result;
+ else
lookup->ipv4_query = query;
- }
return 0;
}