From f24b3cd2ac63beedcfe40993abab262924ceb586 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 26 Jul 2010 15:20:20 -0700 Subject: Add initial support for hostname lookup via resolver --- gresolv/gresolv.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ gresolv/gresolv.h | 2 + 2 files changed, 132 insertions(+) (limited to 'gresolv') diff --git a/gresolv/gresolv.c b/gresolv/gresolv.c index d1aa8d27..8256b0b7 100644 --- a/gresolv/gresolv.c +++ b/gresolv/gresolv.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -34,11 +35,14 @@ #include "gresolv.h" struct resolv_nameserver { + GResolv *resolv; + char *address; uint16_t port; unsigned long flags; GIOChannel *udp_channel; + guint udp_watch; }; struct _GResolv { @@ -51,11 +55,30 @@ struct _GResolv { gpointer debug_data; }; +static inline void debug(GResolv *resolv, const char *format, ...) +{ + char str[256]; + va_list ap; + + if (resolv->debug_func == NULL) + return; + + va_start(ap, format); + + if (vsnprintf(str, sizeof(str), format, ap) > 0) + resolv->debug_func(str, resolv->debug_data); + + va_end(ap); +} + static void free_nameserver(struct resolv_nameserver *nameserver) { if (nameserver == NULL) return; + if (nameserver->udp_watch > 0) + g_source_remove(nameserver->udp_watch); + if (nameserver->udp_channel != NULL) g_io_channel_unref(nameserver->udp_channel); @@ -75,6 +98,89 @@ static void flush_nameservers(GResolv *resolv) resolv->nameserver_list = NULL; } +static int send_query(GResolv *resolv, const unsigned char *buf, int len) +{ + GList *list; + + if (resolv->nameserver_list == NULL) + return -ENOENT; + + for (list = g_list_first(resolv->nameserver_list); + list; list = g_list_next(list)) { + struct resolv_nameserver *nameserver = list->data; + int sk, sent; + + if (nameserver->udp_channel == NULL) + continue; + + sk = g_io_channel_unix_get_fd(nameserver->udp_channel); + + sent = send(sk, buf, len, 0); + } + + return 0; +} + +static void parse_response(struct resolv_nameserver *nameserver, + const unsigned char *buf, int len) +{ + GResolv *resolv = nameserver->resolv; + ns_msg msg; + ns_rr rr; + int i, rcode; + + debug(resolv, "response from %s", nameserver->address); + + ns_initparse(buf, len, &msg); + + rcode = ns_msg_getflag(msg, ns_f_rcode); + + debug(resolv, "msg id: 0x%04x rcode: %d count: %d", + ns_msg_id(msg), rcode, ns_msg_count(msg, ns_s_an)); + + for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) { + char result[100]; + + ns_parserr(&msg, ns_s_an, i, &rr); + + if (ns_rr_class(rr) != ns_c_in) + continue; + + if (ns_rr_type(rr) != ns_t_a) + continue; + + if (ns_rr_rdlen(rr) != NS_INADDRSZ) + continue; + + inet_ntop(AF_INET, ns_rr_rdata(rr), result, sizeof(result)); + + debug(resolv, "result: %s", result); + } +} + +static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct resolv_nameserver *nameserver = user_data; + unsigned char buf[4096]; + int sk, len; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + nameserver->udp_watch = 0; + return FALSE; + } + + sk = g_io_channel_unix_get_fd(nameserver->udp_channel); + + len = recv(sk, buf, sizeof(buf), 0); + if (len < 12) + return TRUE; + + parse_response(nameserver, buf, len); + + return TRUE; +} + static int connect_udp_channel(struct resolv_nameserver *nameserver) { struct sockaddr_in sin; @@ -102,6 +208,10 @@ static int connect_udp_channel(struct resolv_nameserver *nameserver) g_io_channel_set_close_on_unref(nameserver->udp_channel, TRUE); + nameserver->udp_watch = g_io_add_watch(nameserver->udp_channel, + G_IO_IN, received_udp_data, + nameserver); + return 0; } @@ -178,9 +288,13 @@ int g_resolv_add_nameserver(GResolv *resolv, const char *address, return -EIO; } + nameserver->resolv = resolv; + resolv->nameserver_list = g_list_append(resolv->nameserver_list, nameserver); + debug(resolv, "setting nameserver %s", address); + return 0; } @@ -191,3 +305,19 @@ void g_resolv_flush_nameservers(GResolv *resolv) flush_nameservers(resolv); } + +int g_resolv_lookup_hostname(GResolv *resolv, const char *hostname) +{ + unsigned char buf[4096]; + int len; + + debug(resolv, "lookup hostname %s", hostname); + + len = res_mkquery(ns_o_query, hostname, ns_c_in, ns_t_a, + NULL, 0, NULL, buf, sizeof(buf)); + + if (send_query(resolv, buf, len) < 0) + return -EIO; + + return 0; +} diff --git a/gresolv/gresolv.h b/gresolv/gresolv.h index 40b49728..baafd355 100644 --- a/gresolv/gresolv.h +++ b/gresolv/gresolv.h @@ -48,6 +48,8 @@ int g_resolv_add_nameserver(GResolv *resolv, const char *address, uint16_t port, unsigned long flags); void g_resolv_flush_nameservers(GResolv *resolv); +int g_resolv_lookup_hostname(GResolv *resolv, const char *hostname); + #ifdef __cplusplus } #endif -- cgit v1.2.3