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>
48 struct sockaddr_in sin;
49 struct sockaddr_in6 sin6;
53 struct sockaddr_in sin;
54 struct sockaddr_in6 sin6;
60 struct resolv_lookup {
65 struct sort_result *results;
67 struct resolv_query *ipv4_query;
68 struct resolv_query *ipv6_query;
73 GResolvResultFunc result_func;
84 struct resolv_lookup *lookup;
87 struct resolv_nameserver {
94 GIOChannel *udp_channel;
101 guint next_lookup_id;
102 GQueue *lookup_queue;
106 GList *nameserver_list;
108 struct __res_state res;
110 GResolvDebugFunc debug_func;
114 static void sort_and_return_results(struct resolv_lookup *lookup);
115 static void rfc3484_sort_results(struct resolv_lookup *lookup);
117 static inline void debug(GResolv *resolv, const char *format, ...)
122 if (resolv->debug_func == NULL)
125 va_start(ap, format);
127 if (vsnprintf(str, sizeof(str), format, ap) > 0)
128 resolv->debug_func(str, resolv->debug_data);
133 static void destroy_query(struct resolv_query *query)
135 if (query->timeout > 0)
136 g_source_remove(query->timeout);
141 static void destroy_lookup(struct resolv_lookup *lookup)
143 if (lookup->ipv4_query) {
144 destroy_query(lookup->ipv4_query);
145 g_queue_remove(lookup->resolv->query_queue, lookup->ipv4_query);
147 if (lookup->ipv6_query) {
148 destroy_query(lookup->ipv6_query);
149 g_queue_remove(lookup->resolv->query_queue, lookup->ipv4_query);
151 g_free(lookup->results);
155 static gboolean query_timeout(gpointer user_data)
157 struct resolv_query *query = user_data;
158 struct resolv_lookup *lookup = query->lookup;
159 GResolv *resolv = query->resolv;
163 if (query == lookup->ipv4_query) {
164 lookup->ipv4_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE;
165 lookup->ipv4_query = NULL;
166 } else if (query == lookup->ipv6_query) {
167 lookup->ipv6_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE;
168 lookup->ipv6_query = NULL;
170 if (!lookup->ipv4_query && !lookup->ipv4_query)
171 sort_and_return_results(lookup);
173 destroy_query(query);
174 g_queue_remove(resolv->query_queue, query);
179 static void free_nameserver(struct resolv_nameserver *nameserver)
181 if (nameserver == NULL)
184 if (nameserver->udp_watch > 0)
185 g_source_remove(nameserver->udp_watch);
187 if (nameserver->udp_channel != NULL)
188 g_io_channel_unref(nameserver->udp_channel);
190 g_free(nameserver->address);
194 static void flush_nameservers(GResolv *resolv)
198 for (list = g_list_first(resolv->nameserver_list);
199 list; list = g_list_next(list))
200 free_nameserver(list->data);
202 g_list_free(resolv->nameserver_list);
203 resolv->nameserver_list = NULL;
206 static int send_query(GResolv *resolv, const unsigned char *buf, int len)
210 if (resolv->nameserver_list == NULL)
213 for (list = g_list_first(resolv->nameserver_list);
214 list; list = g_list_next(list)) {
215 struct resolv_nameserver *nameserver = list->data;
218 if (nameserver->udp_channel == NULL)
221 sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
223 sent = send(sk, buf, len, 0);
229 static gint compare_lookup_id(gconstpointer a, gconstpointer b)
231 const struct resolv_lookup *lookup = a;
232 guint id = GPOINTER_TO_UINT(b);
243 static gint compare_query_msgid(gconstpointer a, gconstpointer b)
245 const struct resolv_query *query = a;
246 uint16_t msgid = GPOINTER_TO_UINT(b);
248 if (query->msgid < msgid)
251 if (query->msgid > msgid)
257 static void add_result(struct resolv_lookup *lookup, int family, const void *data)
259 int n = lookup->nr_results++;
260 lookup->results = g_realloc(lookup->results,
261 sizeof(struct sort_result) * (n+1));
263 memset(&lookup->results[n], 0, sizeof(struct sort_result));
265 lookup->results[n].dst.sa.sa_family = family;
266 if (family == AF_INET)
267 memcpy(&lookup->results[n].dst.sin.sin_addr, data, NS_INADDRSZ);
269 memcpy(&lookup->results[n].dst.sin6.sin6_addr, data, NS_IN6ADDRSZ);
272 static void parse_response(struct resolv_nameserver *nameserver,
273 const unsigned char *buf, int len)
275 GResolv *resolv = nameserver->resolv;
276 GResolvResultStatus status;
277 struct resolv_query *query;
278 struct resolv_lookup *lookup;
282 int i, n, rcode, count;
284 debug(resolv, "response from %s", nameserver->address);
286 ns_initparse(buf, len, &msg);
288 rcode = ns_msg_getflag(msg, ns_f_rcode);
289 count = ns_msg_count(msg, ns_s_an);
291 debug(resolv, "msg id: 0x%04x rcode: %d count: %d",
292 ns_msg_id(msg), rcode, count);
296 status = G_RESOLV_RESULT_STATUS_SUCCESS;
299 status = G_RESOLV_RESULT_STATUS_FORMAT_ERROR;
302 status = G_RESOLV_RESULT_STATUS_SERVER_FAILURE;
305 status = G_RESOLV_RESULT_STATUS_NAME_ERROR;
308 status = G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED;
311 status = G_RESOLV_RESULT_STATUS_REFUSED;
314 status = G_RESOLV_RESULT_STATUS_ERROR;
318 list = g_queue_find_custom(resolv->query_queue,
319 GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid);
324 lookup = query->lookup;
326 if (query == lookup->ipv6_query) {
327 lookup->ipv6_status = status;
328 lookup->ipv6_query = NULL;
329 } else if (query == lookup->ipv4_query) {
330 lookup->ipv4_status = status;
331 lookup->ipv4_query = NULL;
334 for (i = 0, n = 0; i < count; i++) {
335 ns_parserr(&msg, ns_s_an, i, &rr);
337 if (ns_rr_class(rr) != ns_c_in)
340 g_assert(offsetof(struct sockaddr_in, sin_addr) ==
341 offsetof(struct sockaddr_in6, sin6_flowinfo));
343 if (ns_rr_type(rr) == ns_t_a &&
344 ns_rr_rdlen(rr) == NS_INADDRSZ) {
345 add_result(lookup, AF_INET, ns_rr_rdata(rr));
346 } else if (ns_rr_type(rr) == ns_t_aaaa &&
347 ns_rr_rdlen(rr) == NS_IN6ADDRSZ) {
348 add_result(lookup, AF_INET6, ns_rr_rdata(rr));
352 if (!lookup->ipv4_query && !lookup->ipv6_query)
353 sort_and_return_results(lookup);
355 destroy_query(query);
356 g_queue_remove(resolv->query_queue, query);
359 static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond,
362 struct resolv_nameserver *nameserver = user_data;
363 unsigned char buf[4096];
366 if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
367 nameserver->udp_watch = 0;
371 sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
373 len = recv(sk, buf, sizeof(buf), 0);
377 parse_response(nameserver, buf, len);
382 static int connect_udp_channel(struct resolv_nameserver *nameserver)
384 struct addrinfo hints, *rp;
388 memset(&hints, 0, sizeof(hints));
389 hints.ai_family = AF_UNSPEC;
390 hints.ai_socktype = SOCK_DGRAM;
391 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_NUMERICHOST;
393 sprintf(portnr, "%d", nameserver->port);
394 err = getaddrinfo(nameserver->address, portnr, &hints, &rp);
398 /* Do not blindly copy this code elsewhere; it doesn't loop over the
399 results using ->ai_next as it should. That's OK in *this* case
400 because it was a numeric lookup; we *know* there's only one. */
404 sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
410 if (connect(sk, rp->ai_addr, rp->ai_addrlen) < 0) {
418 nameserver->udp_channel = g_io_channel_unix_new(sk);
419 if (nameserver->udp_channel == NULL) {
424 g_io_channel_set_close_on_unref(nameserver->udp_channel, TRUE);
426 nameserver->udp_watch = g_io_add_watch(nameserver->udp_channel,
427 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
428 received_udp_data, nameserver);
433 GResolv *g_resolv_new(int index)
440 resolv = g_try_new0(GResolv, 1);
444 resolv->ref_count = 1;
446 resolv->next_lookup_id = 1;
448 resolv->query_queue = g_queue_new();
449 if (resolv->query_queue == NULL) {
454 resolv->lookup_queue = g_queue_new();
455 if (resolv->lookup_queue == NULL) {
456 g_queue_free(resolv->query_queue);
461 resolv->index = index;
462 resolv->nameserver_list = NULL;
464 res_ninit(&resolv->res);
469 GResolv *g_resolv_ref(GResolv *resolv)
474 g_atomic_int_inc(&resolv->ref_count);
479 void g_resolv_unref(GResolv *resolv)
481 struct resolv_query *query;
486 if (g_atomic_int_dec_and_test(&resolv->ref_count) == FALSE)
489 while ((query = g_queue_pop_head(resolv->query_queue)))
490 destroy_query(query);
492 g_queue_free(resolv->query_queue);
493 g_queue_free(resolv->lookup_queue);
495 flush_nameservers(resolv);
497 res_nclose(&resolv->res);
502 void g_resolv_set_debug(GResolv *resolv,
503 GResolvDebugFunc func, gpointer user_data)
508 resolv->debug_func = func;
509 resolv->debug_data = user_data;
512 gboolean g_resolv_add_nameserver(GResolv *resolv, const char *address,
513 uint16_t port, unsigned long flags)
515 struct resolv_nameserver *nameserver;
520 nameserver = g_try_new0(struct resolv_nameserver, 1);
521 if (nameserver == NULL)
524 nameserver->address = g_strdup(address);
525 nameserver->port = port;
526 nameserver->flags = flags;
528 if (connect_udp_channel(nameserver) < 0) {
529 free_nameserver(nameserver);
533 nameserver->resolv = resolv;
535 resolv->nameserver_list = g_list_append(resolv->nameserver_list,
538 debug(resolv, "setting nameserver %s", address);
543 void g_resolv_flush_nameservers(GResolv *resolv)
548 flush_nameservers(resolv);
551 static void sort_and_return_results(struct resolv_lookup *lookup)
554 GResolvResultStatus status;
555 char **results = g_try_new0(char *, lookup->nr_results + 1);
561 rfc3484_sort_results(lookup);
563 for (i = 0; i < lookup->nr_results; i++) {
564 if (lookup->results[i].dst.sa.sa_family == AF_INET) {
565 if (!inet_ntop(AF_INET, &lookup->results[i].dst.sin.sin_addr,
568 } else if (lookup->results[i].dst.sa.sa_family == AF_INET6) {
569 if (!inet_ntop(AF_INET6, &lookup->results[i].dst.sin6.sin6_addr,
575 results[n++] = strdup(buf);
579 status = lookup->ipv4_status;
580 if (status == G_RESOLV_RESULT_STATUS_SUCCESS)
581 status = lookup->ipv6_status;
583 lookup->result_func(status, results, lookup->result_data);
586 g_queue_remove(lookup->resolv->lookup_queue, lookup);
587 destroy_lookup(lookup);
590 static gint add_query(struct resolv_lookup *lookup, const char *hostname, int type)
592 struct resolv_query *query = g_try_new0(struct resolv_query, 1);
593 unsigned char buf[4096];
599 len = res_mkquery(ns_o_query, hostname, ns_c_in, type,
600 NULL, 0, NULL, buf, sizeof(buf));
602 query->msgid = buf[0] << 8 | buf[1];
604 if (send_query(lookup->resolv, buf, len) < 0)
607 query->resolv = lookup->resolv;
608 query->lookup = lookup;
610 g_queue_push_tail(lookup->resolv->query_queue, query);
612 query->timeout = g_timeout_add_seconds(5, query_timeout, query);
614 if (type == ns_t_aaaa)
615 lookup->ipv6_query = query;
617 lookup->ipv4_query = query;
622 guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
623 GResolvResultFunc func, gpointer user_data)
625 struct resolv_lookup *lookup;
627 debug(resolv, "lookup hostname %s", hostname);
632 if (resolv->nameserver_list == NULL) {
635 for (i = 0; i < resolv->res.nscount; i++) {
637 int family = resolv->res.nsaddr_list[i].sin_family;
638 void *sa_addr = &resolv->res.nsaddr_list[i].sin_addr;
640 if (family != AF_INET && resolv->res._u._ext.nsaddrs[i]) {
642 sa_addr = &resolv->res._u._ext.nsaddrs[i]->sin6_addr;
644 if (family != AF_INET && family != AF_INET6)
647 if (inet_ntop(family, sa_addr, buf, sizeof(buf)))
648 g_resolv_add_nameserver(resolv, buf, 53, 0);
651 if (resolv->nameserver_list == NULL)
652 g_resolv_add_nameserver(resolv, "127.0.0.1", 53, 0);
655 lookup = g_try_new0(struct resolv_lookup, 1);
659 lookup->resolv = resolv;
660 lookup->result_func = func;
661 lookup->result_data = user_data;
662 lookup->id = resolv->next_lookup_id++;
664 if (add_query(lookup, hostname, ns_t_a)) {
668 if (add_query(lookup, hostname, ns_t_aaaa)) {
669 destroy_query(lookup->ipv4_query);
670 g_queue_remove(resolv->query_queue, lookup->ipv4_query);
675 g_queue_push_tail(resolv->lookup_queue, lookup);
679 gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id)
683 list = g_queue_find_custom(resolv->lookup_queue,
684 GUINT_TO_POINTER(id), compare_lookup_id);
689 destroy_lookup(list->data);
690 g_queue_remove(resolv->query_queue, list->data);
695 static void find_srcaddr(struct sort_result *res)
698 socklen_t sl = sizeof(res->src);
700 fd = socket(res->dst.sa.sa_family, SOCK_DGRAM, IPPROTO_IP);
704 if (connect(fd, &res->dst.sa, sizeof(res->dst))) {
708 if (getsockname(fd, &res->src.sa, &sl)) {
712 res->reachable = TRUE;
718 unsigned char addr[NS_IN6ADDRSZ];
723 static const struct gai_table gai_labels[] = {
725 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
726 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
730 .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
731 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
735 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
736 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
740 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
741 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 },
745 /* Variations from RFC 3484, matching glibc behaviour */
746 .addr = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
747 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
751 .addr = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
752 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
756 .addr = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
757 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
762 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
763 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
769 static const struct gai_table gai_precedences[] = {
771 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
772 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
776 .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
777 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
781 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
782 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
786 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
787 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 },
791 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
792 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
798 static unsigned char v4mapped[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
799 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
801 static gboolean mask_compare(const unsigned char *one, const unsigned char *two, int mask)
804 if (memcmp(one, two, mask / 8))
810 if (mask && ((*one ^ *two) >> (8-mask)))
816 static int match_gai_table(struct sockaddr *sa, const struct gai_table *tbl)
818 struct sockaddr_in *sin = (void *)sa;
819 struct sockaddr_in6 *sin6 = (void *)sa;
822 if (sa->sa_family == AF_INET) {
824 memcpy(v4mapped+12, &sin->sin_addr, NS_INADDRSZ);
826 addr = &sin6->sin6_addr;
829 if (mask_compare(addr, tbl->addr, tbl->mask))
835 static int rfc3484_compare(const void *__one, const void *__two)
837 const struct sort_result *one = __one;
838 const struct sort_result *two = __two;
840 /* Rule 1: Avoid unusable destinations */
841 if (one->reachable && !two->reachable)
843 else if (two->reachable && !one->reachable)
846 /* Rule 2: Prefer matching scope */
848 /* Rule 3: Avoid deprecated addresses */
850 /* Rule 4: Prefer home addresses */
852 /* Rule 5: Prefer matching label */
853 if (one->dst_label == one->src_label &&
854 two->dst_label != two->src_label)
856 else if (two->dst_label == two->src_label &&
857 one->dst_label != one->src_label)
860 /* Rule 6: Prefer higher precedence */
861 if (one->precedence > two->precedence)
863 else if (two->precedence > one->precedence)
866 /* Rule 7: Prefer native transport */
868 /* Rule 8: Prefer smaller scope */
870 /* Rule 9: Use longest matching prefix */
872 /* Rule 10: Otherwise, leave the order unchanged */
879 static void rfc3484_sort_results(struct resolv_lookup *lookup)
883 for (i = 0; i < lookup->nr_results; i++) {
884 struct sort_result *res = &lookup->results[i];
886 res->precedence = match_gai_table(&res->dst.sa, gai_precedences);
887 res->dst_label = match_gai_table(&res->dst.sa, gai_labels);
888 res->src_label = match_gai_table(&res->src.sa, gai_labels);
891 qsort(lookup->results, lookup->nr_results, sizeof(struct sort_result),