3 * Resolver library with GLib integration
5 * Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 #include <sys/types.h>
33 #include <sys/socket.h>
35 #include <arpa/inet.h>
36 #include <arpa/nameser.h>
49 struct sockaddr_in sin;
50 struct sockaddr_in6 sin6;
54 struct sockaddr_in sin;
55 struct sockaddr_in6 sin6;
61 struct resolv_lookup {
66 struct sort_result *results;
68 struct resolv_query *ipv4_query;
69 struct resolv_query *ipv6_query;
74 GResolvResultFunc result_func;
85 struct resolv_lookup *lookup;
88 struct resolv_nameserver {
95 GIOChannel *udp_channel;
102 guint next_lookup_id;
103 GQueue *lookup_queue;
107 GList *nameserver_list;
109 struct __res_state res;
111 GResolvDebugFunc debug_func;
115 static void sort_and_return_results(struct resolv_lookup *lookup);
116 static void rfc3484_sort_results(struct resolv_lookup *lookup);
118 static inline void debug(GResolv *resolv, const char *format, ...)
123 if (resolv->debug_func == NULL)
126 va_start(ap, format);
128 if (vsnprintf(str, sizeof(str), format, ap) > 0)
129 resolv->debug_func(str, resolv->debug_data);
134 static void destroy_query(struct resolv_query *query)
136 if (query->timeout > 0)
137 g_source_remove(query->timeout);
142 static void destroy_lookup(struct resolv_lookup *lookup)
144 if (lookup->ipv4_query) {
145 destroy_query(lookup->ipv4_query);
146 g_queue_remove(lookup->resolv->query_queue, lookup->ipv4_query);
148 if (lookup->ipv6_query) {
149 destroy_query(lookup->ipv6_query);
150 g_queue_remove(lookup->resolv->query_queue, lookup->ipv6_query);
152 g_free(lookup->results);
156 static gboolean query_timeout(gpointer user_data)
158 struct resolv_query *query = user_data;
159 struct resolv_lookup *lookup = query->lookup;
160 GResolv *resolv = query->resolv;
164 if (query == lookup->ipv4_query) {
165 lookup->ipv4_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE;
166 lookup->ipv4_query = NULL;
167 } else if (query == lookup->ipv6_query) {
168 lookup->ipv6_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE;
169 lookup->ipv6_query = NULL;
171 if (!lookup->ipv4_query && !lookup->ipv4_query)
172 sort_and_return_results(lookup);
174 destroy_query(query);
175 g_queue_remove(resolv->query_queue, query);
180 static void free_nameserver(struct resolv_nameserver *nameserver)
182 if (nameserver == NULL)
185 if (nameserver->udp_watch > 0)
186 g_source_remove(nameserver->udp_watch);
188 if (nameserver->udp_channel != NULL)
189 g_io_channel_unref(nameserver->udp_channel);
191 g_free(nameserver->address);
195 static void flush_nameservers(GResolv *resolv)
199 for (list = g_list_first(resolv->nameserver_list);
200 list; list = g_list_next(list))
201 free_nameserver(list->data);
203 g_list_free(resolv->nameserver_list);
204 resolv->nameserver_list = NULL;
207 static int send_query(GResolv *resolv, const unsigned char *buf, int len)
211 if (resolv->nameserver_list == NULL)
214 for (list = g_list_first(resolv->nameserver_list);
215 list; list = g_list_next(list)) {
216 struct resolv_nameserver *nameserver = list->data;
219 if (nameserver->udp_channel == NULL)
222 sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
224 sent = send(sk, buf, len, 0);
230 static gint compare_lookup_id(gconstpointer a, gconstpointer b)
232 const struct resolv_lookup *lookup = a;
233 guint id = GPOINTER_TO_UINT(b);
244 static gint compare_query_msgid(gconstpointer a, gconstpointer b)
246 const struct resolv_query *query = a;
247 uint16_t msgid = GPOINTER_TO_UINT(b);
249 if (query->msgid < msgid)
252 if (query->msgid > msgid)
258 static void add_result(struct resolv_lookup *lookup, int family, const void *data)
260 int n = lookup->nr_results++;
261 lookup->results = g_realloc(lookup->results,
262 sizeof(struct sort_result) * (n+1));
264 memset(&lookup->results[n], 0, sizeof(struct sort_result));
266 lookup->results[n].dst.sa.sa_family = family;
267 if (family == AF_INET)
268 memcpy(&lookup->results[n].dst.sin.sin_addr, data, NS_INADDRSZ);
270 memcpy(&lookup->results[n].dst.sin6.sin6_addr, data, NS_IN6ADDRSZ);
273 static void parse_response(struct resolv_nameserver *nameserver,
274 const unsigned char *buf, int len)
276 GResolv *resolv = nameserver->resolv;
277 GResolvResultStatus status;
278 struct resolv_query *query;
279 struct resolv_lookup *lookup;
283 int i, n, rcode, count;
285 debug(resolv, "response from %s", nameserver->address);
287 ns_initparse(buf, len, &msg);
289 rcode = ns_msg_getflag(msg, ns_f_rcode);
290 count = ns_msg_count(msg, ns_s_an);
292 debug(resolv, "msg id: 0x%04x rcode: %d count: %d",
293 ns_msg_id(msg), rcode, count);
297 status = G_RESOLV_RESULT_STATUS_SUCCESS;
300 status = G_RESOLV_RESULT_STATUS_FORMAT_ERROR;
303 status = G_RESOLV_RESULT_STATUS_SERVER_FAILURE;
306 status = G_RESOLV_RESULT_STATUS_NAME_ERROR;
309 status = G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED;
312 status = G_RESOLV_RESULT_STATUS_REFUSED;
315 status = G_RESOLV_RESULT_STATUS_ERROR;
319 list = g_queue_find_custom(resolv->query_queue,
320 GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid);
325 lookup = query->lookup;
327 if (query == lookup->ipv6_query) {
328 lookup->ipv6_status = status;
329 lookup->ipv6_query = NULL;
330 } else if (query == lookup->ipv4_query) {
331 lookup->ipv4_status = status;
332 lookup->ipv4_query = NULL;
335 for (i = 0, n = 0; i < count; i++) {
336 ns_parserr(&msg, ns_s_an, i, &rr);
338 if (ns_rr_class(rr) != ns_c_in)
341 g_assert(offsetof(struct sockaddr_in, sin_addr) ==
342 offsetof(struct sockaddr_in6, sin6_flowinfo));
344 if (ns_rr_type(rr) == ns_t_a &&
345 ns_rr_rdlen(rr) == NS_INADDRSZ) {
346 add_result(lookup, AF_INET, ns_rr_rdata(rr));
347 } else if (ns_rr_type(rr) == ns_t_aaaa &&
348 ns_rr_rdlen(rr) == NS_IN6ADDRSZ) {
349 add_result(lookup, AF_INET6, ns_rr_rdata(rr));
353 if (!lookup->ipv4_query && !lookup->ipv6_query)
354 sort_and_return_results(lookup);
356 destroy_query(query);
357 g_queue_remove(resolv->query_queue, query);
360 static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond,
363 struct resolv_nameserver *nameserver = user_data;
364 unsigned char buf[4096];
367 if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
368 nameserver->udp_watch = 0;
372 sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
374 len = recv(sk, buf, sizeof(buf), 0);
378 parse_response(nameserver, buf, len);
383 static int connect_udp_channel(struct resolv_nameserver *nameserver)
385 struct addrinfo hints, *rp;
389 memset(&hints, 0, sizeof(hints));
390 hints.ai_family = AF_UNSPEC;
391 hints.ai_socktype = SOCK_DGRAM;
392 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_NUMERICHOST;
394 sprintf(portnr, "%d", nameserver->port);
395 err = getaddrinfo(nameserver->address, portnr, &hints, &rp);
399 /* Do not blindly copy this code elsewhere; it doesn't loop over the
400 results using ->ai_next as it should. That's OK in *this* case
401 because it was a numeric lookup; we *know* there's only one. */
405 sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
411 if (connect(sk, rp->ai_addr, rp->ai_addrlen) < 0) {
419 nameserver->udp_channel = g_io_channel_unix_new(sk);
420 if (nameserver->udp_channel == NULL) {
425 g_io_channel_set_close_on_unref(nameserver->udp_channel, TRUE);
427 nameserver->udp_watch = g_io_add_watch(nameserver->udp_channel,
428 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
429 received_udp_data, nameserver);
434 GResolv *g_resolv_new(int index)
441 resolv = g_try_new0(GResolv, 1);
445 resolv->ref_count = 1;
447 resolv->next_lookup_id = 1;
449 resolv->query_queue = g_queue_new();
450 if (resolv->query_queue == NULL) {
455 resolv->lookup_queue = g_queue_new();
456 if (resolv->lookup_queue == NULL) {
457 g_queue_free(resolv->query_queue);
462 resolv->index = index;
463 resolv->nameserver_list = NULL;
465 res_ninit(&resolv->res);
470 GResolv *g_resolv_ref(GResolv *resolv)
475 g_atomic_int_inc(&resolv->ref_count);
480 void g_resolv_unref(GResolv *resolv)
482 struct resolv_query *query;
487 if (g_atomic_int_dec_and_test(&resolv->ref_count) == FALSE)
490 while ((query = g_queue_pop_head(resolv->query_queue)))
491 destroy_query(query);
493 g_queue_free(resolv->query_queue);
494 g_queue_free(resolv->lookup_queue);
496 flush_nameservers(resolv);
498 res_nclose(&resolv->res);
503 void g_resolv_set_debug(GResolv *resolv,
504 GResolvDebugFunc func, gpointer user_data)
509 resolv->debug_func = func;
510 resolv->debug_data = user_data;
513 gboolean g_resolv_add_nameserver(GResolv *resolv, const char *address,
514 uint16_t port, unsigned long flags)
516 struct resolv_nameserver *nameserver;
521 nameserver = g_try_new0(struct resolv_nameserver, 1);
522 if (nameserver == NULL)
525 nameserver->address = g_strdup(address);
526 nameserver->port = port;
527 nameserver->flags = flags;
529 if (connect_udp_channel(nameserver) < 0) {
530 free_nameserver(nameserver);
534 nameserver->resolv = resolv;
536 resolv->nameserver_list = g_list_append(resolv->nameserver_list,
539 debug(resolv, "setting nameserver %s", address);
544 void g_resolv_flush_nameservers(GResolv *resolv)
549 flush_nameservers(resolv);
552 static void sort_and_return_results(struct resolv_lookup *lookup)
555 GResolvResultStatus status;
556 char **results = g_try_new0(char *, lookup->nr_results + 1);
562 rfc3484_sort_results(lookup);
564 for (i = 0; i < lookup->nr_results; i++) {
565 if (lookup->results[i].dst.sa.sa_family == AF_INET) {
566 if (!inet_ntop(AF_INET, &lookup->results[i].dst.sin.sin_addr,
569 } else if (lookup->results[i].dst.sa.sa_family == AF_INET6) {
570 if (!inet_ntop(AF_INET6, &lookup->results[i].dst.sin6.sin6_addr,
576 results[n++] = strdup(buf);
580 status = lookup->ipv4_status;
581 if (status == G_RESOLV_RESULT_STATUS_SUCCESS)
582 status = lookup->ipv6_status;
584 lookup->result_func(status, results, lookup->result_data);
587 g_queue_remove(lookup->resolv->lookup_queue, lookup);
588 destroy_lookup(lookup);
591 static gint add_query(struct resolv_lookup *lookup, const char *hostname, int type)
593 struct resolv_query *query = g_try_new0(struct resolv_query, 1);
594 unsigned char buf[4096];
600 len = res_mkquery(ns_o_query, hostname, ns_c_in, type,
601 NULL, 0, NULL, buf, sizeof(buf));
603 query->msgid = buf[0] << 8 | buf[1];
605 if (send_query(lookup->resolv, buf, len) < 0)
608 query->resolv = lookup->resolv;
609 query->lookup = lookup;
611 g_queue_push_tail(lookup->resolv->query_queue, query);
613 query->timeout = g_timeout_add_seconds(5, query_timeout, query);
615 if (type == ns_t_aaaa)
616 lookup->ipv6_query = query;
618 lookup->ipv4_query = query;
623 guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
624 GResolvResultFunc func, gpointer user_data)
626 struct resolv_lookup *lookup;
628 debug(resolv, "lookup hostname %s", hostname);
633 if (resolv->nameserver_list == NULL) {
636 for (i = 0; i < resolv->res.nscount; i++) {
638 int family = resolv->res.nsaddr_list[i].sin_family;
639 void *sa_addr = &resolv->res.nsaddr_list[i].sin_addr;
641 if (family != AF_INET && resolv->res._u._ext.nsaddrs[i]) {
643 sa_addr = &resolv->res._u._ext.nsaddrs[i]->sin6_addr;
645 if (family != AF_INET && family != AF_INET6)
648 if (inet_ntop(family, sa_addr, buf, sizeof(buf)))
649 g_resolv_add_nameserver(resolv, buf, 53, 0);
652 if (resolv->nameserver_list == NULL)
653 g_resolv_add_nameserver(resolv, "127.0.0.1", 53, 0);
656 lookup = g_try_new0(struct resolv_lookup, 1);
660 lookup->resolv = resolv;
661 lookup->result_func = func;
662 lookup->result_data = user_data;
663 lookup->id = resolv->next_lookup_id++;
665 if (add_query(lookup, hostname, ns_t_a)) {
669 if (add_query(lookup, hostname, ns_t_aaaa)) {
670 destroy_query(lookup->ipv4_query);
671 g_queue_remove(resolv->query_queue, lookup->ipv4_query);
676 g_queue_push_tail(resolv->lookup_queue, lookup);
680 gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id)
684 list = g_queue_find_custom(resolv->lookup_queue,
685 GUINT_TO_POINTER(id), compare_lookup_id);
690 destroy_lookup(list->data);
691 g_queue_remove(resolv->query_queue, list->data);
696 static void find_srcaddr(struct sort_result *res)
699 socklen_t sl = sizeof(res->src);
701 fd = socket(res->dst.sa.sa_family, SOCK_DGRAM, IPPROTO_IP);
705 if (connect(fd, &res->dst.sa, sizeof(res->dst))) {
709 if (getsockname(fd, &res->src.sa, &sl)) {
713 res->reachable = TRUE;
719 unsigned char addr[NS_IN6ADDRSZ];
724 static const struct gai_table gai_labels[] = {
726 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
727 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
731 .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
732 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
736 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
737 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
741 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
742 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 },
746 /* Variations from RFC 3484, matching glibc behaviour */
747 .addr = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
748 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
752 .addr = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
753 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
757 .addr = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
758 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
763 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
764 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
770 static const struct gai_table gai_precedences[] = {
772 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
773 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
777 .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
778 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
782 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
783 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
787 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
788 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 },
792 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
793 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
799 static unsigned char v4mapped[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
800 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
802 static gboolean mask_compare(const unsigned char *one, const unsigned char *two, int mask)
805 if (memcmp(one, two, mask / 8))
811 if (mask && ((*one ^ *two) >> (8-mask)))
817 static int match_gai_table(struct sockaddr *sa, const struct gai_table *tbl)
819 struct sockaddr_in *sin = (void *)sa;
820 struct sockaddr_in6 *sin6 = (void *)sa;
823 if (sa->sa_family == AF_INET) {
825 memcpy(v4mapped+12, &sin->sin_addr, NS_INADDRSZ);
827 addr = &sin6->sin6_addr;
830 if (mask_compare(addr, tbl->addr, tbl->mask))
836 #define DQUAD(_a,_b,_c,_d) ( ((_a)<<24) | ((_b)<<16) | ((_c)<<8) | (_d) )
837 #define V4MATCH(addr, a,b,c,d, m) ( ((addr) ^ DQUAD(a,b,c,d)) >> (32 - (m)) )
839 #define RFC3484_SCOPE_LINK 2
840 #define RFC3484_SCOPE_SITE 5
841 #define RFC3484_SCOPE_GLOBAL 14
843 static int addr_scope(struct sockaddr *sa)
845 if (sa->sa_family == AF_INET) {
846 struct sockaddr_in *sin = (void *)sa;
847 guint32 addr = ntohl(sin->sin_addr.s_addr);
849 if (V4MATCH(addr, 169,254,0,0, 16) ||
850 V4MATCH(addr, 127,0,0,0, 8))
851 return RFC3484_SCOPE_LINK;
854 if (V4MATCH(addr, 10,0,0,0, 8) ||
855 V4MATCH(addr, 172,16,0,0, 12) ||
856 V4MATCH(addr, 192,168,0,0, 16))
857 return RFC3484_SCOPE_SITE;
860 return RFC3484_SCOPE_GLOBAL;
862 struct sockaddr_in6 *sin6 = (void *)sa;
864 /* Multicast addresses have a 4-bit scope field */
865 if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
866 return sin6->sin6_addr.s6_addr[1] & 0xf;
868 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
869 IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
870 return RFC3484_SCOPE_LINK;
872 if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
873 return RFC3484_SCOPE_SITE;
875 return RFC3484_SCOPE_GLOBAL;
879 static int rfc3484_compare(const void *__one, const void *__two)
881 const struct sort_result *one = __one;
882 const struct sort_result *two = __two;
884 /* Rule 1: Avoid unusable destinations */
885 if (one->reachable && !two->reachable)
887 else if (two->reachable && !one->reachable)
890 /* Rule 2: Prefer matching scope */
891 if (one->dst_scope == one->src_scope &&
892 two->dst_scope != two->src_scope)
894 else if (two->dst_scope == two->src_scope &&
895 one->dst_scope != one->src_scope)
898 /* Rule 3: Avoid deprecated addresses */
900 /* Rule 4: Prefer home addresses */
902 /* Rule 5: Prefer matching label */
903 if (one->dst_label == one->src_label &&
904 two->dst_label != two->src_label)
906 else if (two->dst_label == two->src_label &&
907 one->dst_label != one->src_label)
910 /* Rule 6: Prefer higher precedence */
911 if (one->precedence > two->precedence)
913 else if (two->precedence > one->precedence)
916 /* Rule 7: Prefer native transport */
918 /* Rule 8: Prefer smaller scope */
919 if (one->dst_scope != two->dst_scope)
920 return one->dst_scope - two->dst_scope;
922 /* Rule 9: Use longest matching prefix */
923 if (one->dst.sa.sa_family == AF_INET) {
924 /* Rule 9 is meaningless and counterproductive for Legacy IP
925 unless perhaps we can tell that it's actually on the local
926 subnet. But we don't (yet) have local interface config
927 information, so do nothing here for Legacy IP for now. */
931 for (i = 0; i < 4; i++) {
932 guint32 cmp_one, cmp_two;
934 cmp_one = one->src.sin6.sin6_addr.s6_addr32[i] ^
935 one->dst.sin6.sin6_addr.s6_addr32[i];
936 cmp_two = two->src.sin6.sin6_addr.s6_addr32[i] ^
937 two->dst.sin6.sin6_addr.s6_addr32[i];
939 if (!cmp_two && !cmp_one)
942 if (cmp_one && !cmp_two)
944 if (cmp_two && !cmp_one)
947 /* g_bit_storage() is effectively fls() */
948 cmp_one = g_bit_storage(ntohl(cmp_one));
949 cmp_two = g_bit_storage(ntohl(cmp_two));
951 if (cmp_one == cmp_two)
954 return cmp_one - cmp_two;
959 /* Rule 10: Otherwise, leave the order unchanged */
966 static void rfc3484_sort_results(struct resolv_lookup *lookup)
970 for (i = 0; i < lookup->nr_results; i++) {
971 struct sort_result *res = &lookup->results[i];
973 res->precedence = match_gai_table(&res->dst.sa, gai_precedences);
974 res->dst_label = match_gai_table(&res->dst.sa, gai_labels);
975 res->src_label = match_gai_table(&res->src.sa, gai_labels);
976 res->dst_scope = addr_scope(&res->dst.sa);
977 res->src_scope = addr_scope(&res->src.sa);
980 qsort(lookup->results, lookup->nr_results, sizeof(struct sort_result),