gresolv: First partial implementation for RFC3484 sorting
[framework/connectivity/connman.git] / gweb / gresolv.c
1 /*
2  *
3  *  Resolver library with GLib integration
4  *
5  *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <unistd.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <resolv.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netdb.h>
35 #include <arpa/inet.h>
36 #include <arpa/nameser.h>
37
38 #include "gresolv.h"
39
40 struct sort_result {
41         int precedence;
42         int scope;
43         int src_label;
44         int dst_label;
45         gboolean reachable;
46         union {
47                 struct sockaddr sa;
48                 struct sockaddr_in sin;
49                 struct sockaddr_in6 sin6;
50         } src;
51         union {
52                 struct sockaddr sa;
53                 struct sockaddr_in sin;
54                 struct sockaddr_in6 sin6;
55         } dst;
56 };
57
58 struct resolv_query;
59
60 struct resolv_lookup {
61         GResolv *resolv;
62         guint id;
63
64         int nr_results;
65         struct sort_result *results;
66
67         struct resolv_query *ipv4_query;
68         struct resolv_query *ipv6_query;
69
70         guint ipv4_status;
71         guint ipv6_status;
72
73         GResolvResultFunc result_func;
74         gpointer result_data;
75 };
76
77 struct resolv_query {
78         GResolv *resolv;
79
80         guint timeout;
81
82         uint16_t msgid;
83
84         struct resolv_lookup *lookup;
85 };
86
87 struct resolv_nameserver {
88         GResolv *resolv;
89
90         char *address;
91         uint16_t port;
92         unsigned long flags;
93
94         GIOChannel *udp_channel;
95         guint udp_watch;
96 };
97
98 struct _GResolv {
99         gint ref_count;
100
101         guint next_lookup_id;
102         GQueue *lookup_queue;
103         GQueue *query_queue;
104
105         int index;
106         GList *nameserver_list;
107
108         struct __res_state res;
109
110         GResolvDebugFunc debug_func;
111         gpointer debug_data;
112 };
113
114 static void sort_and_return_results(struct resolv_lookup *lookup);
115 static void rfc3484_sort_results(struct resolv_lookup *lookup);
116
117 static inline void debug(GResolv *resolv, const char *format, ...)
118 {
119         char str[256];
120         va_list ap;
121
122         if (resolv->debug_func == NULL)
123                 return;
124
125         va_start(ap, format);
126
127         if (vsnprintf(str, sizeof(str), format, ap) > 0)
128                 resolv->debug_func(str, resolv->debug_data);
129
130         va_end(ap);
131 }
132
133 static void destroy_query(struct resolv_query *query)
134 {
135         if (query->timeout > 0)
136                 g_source_remove(query->timeout);
137
138         g_free(query);
139 }
140
141 static void destroy_lookup(struct resolv_lookup *lookup)
142 {
143         if (lookup->ipv4_query) {
144                 destroy_query(lookup->ipv4_query);
145                 g_queue_remove(lookup->resolv->query_queue, lookup->ipv4_query);
146         }
147         if (lookup->ipv6_query) {
148                 destroy_query(lookup->ipv6_query);
149                 g_queue_remove(lookup->resolv->query_queue, lookup->ipv4_query);
150         }
151         g_free(lookup->results);
152         g_free(lookup);
153 }
154
155 static gboolean query_timeout(gpointer user_data)
156 {
157         struct resolv_query *query = user_data;
158         struct resolv_lookup *lookup = query->lookup;
159         GResolv *resolv = query->resolv;
160
161         query->timeout = 0;
162
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;
169         }
170         if (!lookup->ipv4_query && !lookup->ipv4_query)
171                 sort_and_return_results(lookup);
172
173         destroy_query(query);
174         g_queue_remove(resolv->query_queue, query);
175
176         return FALSE;
177 }
178
179 static void free_nameserver(struct resolv_nameserver *nameserver)
180 {
181         if (nameserver == NULL)
182                 return;
183
184         if (nameserver->udp_watch > 0)
185                 g_source_remove(nameserver->udp_watch);
186
187         if (nameserver->udp_channel != NULL)
188                 g_io_channel_unref(nameserver->udp_channel);
189
190         g_free(nameserver->address);
191         g_free(nameserver);
192 }
193
194 static void flush_nameservers(GResolv *resolv)
195 {
196         GList *list;
197
198         for (list = g_list_first(resolv->nameserver_list);
199                                         list; list = g_list_next(list))
200                 free_nameserver(list->data);
201
202         g_list_free(resolv->nameserver_list);
203         resolv->nameserver_list = NULL;
204 }
205
206 static int send_query(GResolv *resolv, const unsigned char *buf, int len)
207 {
208         GList *list;
209
210         if (resolv->nameserver_list == NULL)
211                 return -ENOENT;
212
213         for (list = g_list_first(resolv->nameserver_list);
214                                         list; list = g_list_next(list)) {
215                 struct resolv_nameserver *nameserver = list->data;
216                 int sk, sent;
217
218                 if (nameserver->udp_channel == NULL)
219                         continue;
220
221                 sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
222
223                 sent = send(sk, buf, len, 0);
224         }
225
226         return 0;
227 }
228
229 static gint compare_lookup_id(gconstpointer a, gconstpointer b)
230 {
231         const struct resolv_lookup *lookup = a;
232         guint id = GPOINTER_TO_UINT(b);
233
234         if (lookup->id < id)
235                 return -1;
236
237         if (lookup->id > id)
238                 return 1;
239
240         return 0;
241 }
242
243 static gint compare_query_msgid(gconstpointer a, gconstpointer b)
244 {
245         const struct resolv_query *query = a;
246         uint16_t msgid = GPOINTER_TO_UINT(b);
247
248         if (query->msgid < msgid)
249                 return -1;
250
251         if (query->msgid > msgid)
252                 return 1;
253
254         return 0;
255 }
256
257 static void add_result(struct resolv_lookup *lookup, int family, const void *data)
258 {
259         int n = lookup->nr_results++;
260         lookup->results = g_realloc(lookup->results,
261                                     sizeof(struct sort_result) * (n+1));
262
263         memset(&lookup->results[n], 0, sizeof(struct sort_result));
264
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);
268         else
269                 memcpy(&lookup->results[n].dst.sin6.sin6_addr, data, NS_IN6ADDRSZ);
270 }
271
272 static void parse_response(struct resolv_nameserver *nameserver,
273                                         const unsigned char *buf, int len)
274 {
275         GResolv *resolv = nameserver->resolv;
276         GResolvResultStatus status;
277         struct resolv_query *query;
278         struct resolv_lookup *lookup;
279         GList *list;
280         ns_msg msg;
281         ns_rr rr;
282         int i, n, rcode, count;
283
284         debug(resolv, "response from %s", nameserver->address);
285
286         ns_initparse(buf, len, &msg);
287
288         rcode = ns_msg_getflag(msg, ns_f_rcode);
289         count = ns_msg_count(msg, ns_s_an);
290
291         debug(resolv, "msg id: 0x%04x rcode: %d count: %d",
292                                         ns_msg_id(msg), rcode, count);
293
294         switch (rcode) {
295         case 0:
296                 status = G_RESOLV_RESULT_STATUS_SUCCESS;
297                 break;
298         case 1:
299                 status = G_RESOLV_RESULT_STATUS_FORMAT_ERROR;
300                 break;
301         case 2:
302                 status = G_RESOLV_RESULT_STATUS_SERVER_FAILURE;
303                 break;
304         case 3:
305                 status = G_RESOLV_RESULT_STATUS_NAME_ERROR;
306                 break;
307         case 4:
308                 status = G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED;
309                 break;
310         case 5:
311                 status = G_RESOLV_RESULT_STATUS_REFUSED;
312                 break;
313         default:
314                 status = G_RESOLV_RESULT_STATUS_ERROR;
315                 break;
316         }
317
318         list = g_queue_find_custom(resolv->query_queue,
319                         GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid);
320         if (!list)
321                 return;
322
323         query = list->data;
324         lookup = query->lookup;
325
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;
332         }
333
334         for (i = 0, n = 0; i < count; i++) {
335                 ns_parserr(&msg, ns_s_an, i, &rr);
336
337                 if (ns_rr_class(rr) != ns_c_in)
338                         continue;
339
340                 g_assert(offsetof(struct sockaddr_in, sin_addr) ==
341                          offsetof(struct sockaddr_in6, sin6_flowinfo));
342
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));
349                 }
350         }
351
352         if (!lookup->ipv4_query && !lookup->ipv6_query)
353                 sort_and_return_results(lookup);
354
355         destroy_query(query);
356         g_queue_remove(resolv->query_queue, query);
357 }
358
359 static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond,
360                                                         gpointer user_data)
361 {
362         struct resolv_nameserver *nameserver = user_data;
363         unsigned char buf[4096];
364         int sk, len;
365
366         if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
367                 nameserver->udp_watch = 0;
368                 return FALSE;
369         }
370
371         sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
372
373         len = recv(sk, buf, sizeof(buf), 0);
374         if (len < 12)
375                 return TRUE;
376
377         parse_response(nameserver, buf, len);
378
379         return TRUE;
380 }
381
382 static int connect_udp_channel(struct resolv_nameserver *nameserver)
383 {
384         struct addrinfo hints, *rp;
385         char portnr[6];
386         int err, sk;
387
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;
392
393         sprintf(portnr, "%d", nameserver->port);
394         err = getaddrinfo(nameserver->address, portnr, &hints, &rp);
395         if (err)
396                 return -EINVAL;
397
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. */
401         if (!rp)
402                 return -EINVAL;
403
404         sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
405         if (sk < 0) {
406                 freeaddrinfo(rp);
407                 return -EIO;
408         }
409
410         if (connect(sk, rp->ai_addr, rp->ai_addrlen) < 0) {
411                 close(sk);
412                 freeaddrinfo(rp);
413                 return -EIO;
414         }
415
416         freeaddrinfo(rp);
417
418         nameserver->udp_channel = g_io_channel_unix_new(sk);
419         if (nameserver->udp_channel == NULL) {
420                 close(sk);
421                 return -ENOMEM;
422         }
423
424         g_io_channel_set_close_on_unref(nameserver->udp_channel, TRUE);
425
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);
429
430         return 0;
431 }
432
433 GResolv *g_resolv_new(int index)
434 {
435         GResolv *resolv;
436
437         if (index < 0)
438                 return NULL;
439
440         resolv = g_try_new0(GResolv, 1);
441         if (resolv == NULL)
442                 return NULL;
443
444         resolv->ref_count = 1;
445
446         resolv->next_lookup_id = 1;
447
448         resolv->query_queue = g_queue_new();
449         if (resolv->query_queue == NULL) {
450                 g_free(resolv);
451                 return NULL;
452         }
453
454         resolv->lookup_queue = g_queue_new();
455         if (resolv->lookup_queue == NULL) {
456                 g_queue_free(resolv->query_queue);
457                 g_free(resolv);
458                 return NULL;
459         }
460
461         resolv->index = index;
462         resolv->nameserver_list = NULL;
463
464         res_ninit(&resolv->res);
465
466         return resolv;
467 }
468
469 GResolv *g_resolv_ref(GResolv *resolv)
470 {
471         if (resolv == NULL)
472                 return NULL;
473
474         g_atomic_int_inc(&resolv->ref_count);
475
476         return resolv;
477 }
478
479 void g_resolv_unref(GResolv *resolv)
480 {
481         struct resolv_query *query;
482
483         if (resolv == NULL)
484                 return;
485
486         if (g_atomic_int_dec_and_test(&resolv->ref_count) == FALSE)
487                 return;
488
489         while ((query = g_queue_pop_head(resolv->query_queue)))
490                 destroy_query(query);
491
492         g_queue_free(resolv->query_queue);
493         g_queue_free(resolv->lookup_queue);
494
495         flush_nameservers(resolv);
496
497         res_nclose(&resolv->res);
498
499         g_free(resolv);
500 }
501
502 void g_resolv_set_debug(GResolv *resolv,
503                                 GResolvDebugFunc func, gpointer user_data)
504 {
505         if (resolv == NULL)
506                 return;
507
508         resolv->debug_func = func;
509         resolv->debug_data = user_data;
510 }
511
512 gboolean g_resolv_add_nameserver(GResolv *resolv, const char *address,
513                                         uint16_t port, unsigned long flags)
514 {
515         struct resolv_nameserver *nameserver;
516
517         if (resolv == NULL)
518                 return FALSE;
519
520         nameserver = g_try_new0(struct resolv_nameserver, 1);
521         if (nameserver == NULL)
522                 return FALSE;
523
524         nameserver->address = g_strdup(address);
525         nameserver->port = port;
526         nameserver->flags = flags;
527
528         if (connect_udp_channel(nameserver) < 0) {
529                 free_nameserver(nameserver);
530                 return FALSE;
531         }
532
533         nameserver->resolv = resolv;
534
535         resolv->nameserver_list = g_list_append(resolv->nameserver_list,
536                                                                 nameserver);
537
538         debug(resolv, "setting nameserver %s", address);
539
540         return TRUE;
541 }
542
543 void g_resolv_flush_nameservers(GResolv *resolv)
544 {
545         if (resolv == NULL)
546                 return;
547
548         flush_nameservers(resolv);
549 }
550
551 static void sort_and_return_results(struct resolv_lookup *lookup)
552 {
553         char buf[100];
554         GResolvResultStatus status;
555         char **results = g_try_new0(char *, lookup->nr_results + 1);
556         int i, n = 0;
557
558         if (!results)
559                 return;
560
561         rfc3484_sort_results(lookup);
562
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,
566                                        buf, sizeof(buf)))
567                                 continue;
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,
570                                        buf, sizeof(buf)))
571                                 continue;
572                 } else
573                         continue;
574
575                 results[n++] = strdup(buf);
576         }
577         results[n++] = NULL;
578
579         status = lookup->ipv4_status;
580         if (status == G_RESOLV_RESULT_STATUS_SUCCESS)
581                 status = lookup->ipv6_status;
582
583         lookup->result_func(status, results, lookup->result_data);
584
585         g_strfreev(results);
586         g_queue_remove(lookup->resolv->lookup_queue, lookup);
587         destroy_lookup(lookup);
588 }
589
590 static gint add_query(struct resolv_lookup *lookup, const char *hostname, int type)
591 {
592         struct resolv_query *query = g_try_new0(struct resolv_query, 1);
593         unsigned char buf[4096];
594         int len;
595
596         if (query == NULL)
597                 return -ENOMEM;
598
599         len = res_mkquery(ns_o_query, hostname, ns_c_in, type,
600                                         NULL, 0, NULL, buf, sizeof(buf));
601
602         query->msgid = buf[0] << 8 | buf[1];
603
604         if (send_query(lookup->resolv, buf, len) < 0)
605                 return -EIO;
606
607         query->resolv = lookup->resolv;
608         query->lookup = lookup;
609
610         g_queue_push_tail(lookup->resolv->query_queue, query);
611
612         query->timeout = g_timeout_add_seconds(5, query_timeout, query);
613
614         if (type == ns_t_aaaa)
615                 lookup->ipv6_query = query;
616         else
617                 lookup->ipv4_query = query;
618
619         return 0;
620 }
621
622 guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
623                                 GResolvResultFunc func, gpointer user_data)
624 {
625         struct resolv_lookup *lookup;
626
627         debug(resolv, "lookup hostname %s", hostname);
628
629         if (resolv == NULL)
630                 return 0;
631
632         if (resolv->nameserver_list == NULL) {
633                 int i;
634
635                 for (i = 0; i < resolv->res.nscount; i++) {
636                         char buf[100];
637                         int family = resolv->res.nsaddr_list[i].sin_family;
638                         void *sa_addr = &resolv->res.nsaddr_list[i].sin_addr;
639
640                         if (family != AF_INET && resolv->res._u._ext.nsaddrs[i]) {
641                                 family = AF_INET6;
642                                 sa_addr = &resolv->res._u._ext.nsaddrs[i]->sin6_addr;
643                         }
644                         if (family != AF_INET && family != AF_INET6)
645                                 continue;
646
647                         if (inet_ntop(family, sa_addr, buf, sizeof(buf)))
648                                 g_resolv_add_nameserver(resolv, buf, 53, 0);
649                 }
650
651                 if (resolv->nameserver_list == NULL)
652                         g_resolv_add_nameserver(resolv, "127.0.0.1", 53, 0);
653         }
654
655         lookup = g_try_new0(struct resolv_lookup, 1);
656         if (!lookup)
657                 return 0;
658
659         lookup->resolv = resolv;
660         lookup->result_func = func;
661         lookup->result_data = user_data;
662         lookup->id = resolv->next_lookup_id++;
663
664         if (add_query(lookup, hostname, ns_t_a)) {
665                 g_free(lookup);
666                 return -EIO;
667         }
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);
671                 g_free(lookup);
672                 return -EIO;
673         }
674
675         g_queue_push_tail(resolv->lookup_queue, lookup);
676         return lookup->id;
677 }
678
679 gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id)
680 {
681         GList *list;
682
683         list = g_queue_find_custom(resolv->lookup_queue,
684                                 GUINT_TO_POINTER(id), compare_lookup_id);
685
686         if (list == NULL)
687                 return FALSE;
688
689         destroy_lookup(list->data);
690         g_queue_remove(resolv->query_queue, list->data);
691
692         return TRUE;
693 }
694
695 static void find_srcaddr(struct sort_result *res)
696 {
697         int fd;
698         socklen_t sl = sizeof(res->src);
699
700         fd = socket(res->dst.sa.sa_family, SOCK_DGRAM, IPPROTO_IP);
701         if (fd < 0)
702                 return;
703
704         if (connect(fd, &res->dst.sa, sizeof(res->dst))) {
705                 close(fd);
706                 return;
707         }
708         if (getsockname(fd, &res->src.sa, &sl)) {
709                 close(fd);
710                 return;
711         }
712         res->reachable = TRUE;
713         close(fd);
714 }
715
716 struct gai_table
717 {
718         unsigned char addr[NS_IN6ADDRSZ];
719         int mask;
720         int value;
721 };
722
723 static const struct gai_table gai_labels[] = {
724         {
725                 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
726                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
727                 .mask = 128,
728                 .value = 0,
729         }, {
730                 .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
731                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
732                 .mask = 16,
733                 .value = 2,
734         }, {
735                 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
736                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
737                 .mask = 96,
738                 .value = 3,
739         }, {
740                 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
741                           0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 },
742                 .mask = 96,
743                 .value = 4,
744         }, {
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 },
748                 .mask = 10,
749                 .value = 5,
750         }, {
751                 .addr = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
752                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
753                 .mask = 7,
754                 .value = 6,
755         }, {
756                 .addr = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
757                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
758                 .mask = 32,
759                 .value = 7,
760         }, {
761                 /* catch-all */
762                 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
763                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
764                 .mask = 0,
765                 .value = 1,
766         }
767 };
768
769 static const struct gai_table gai_precedences[] = {
770         {
771                 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
772                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
773                 .mask = 128,
774                 .value = 50,
775         }, {
776                 .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
777                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
778                 .mask = 16,
779                 .value = 30,
780         }, {
781                 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
782                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
783                 .mask = 96,
784                 .value = 20,
785         }, {
786                 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
787                           0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 },
788                 .mask = 96,
789                 .value = 10,
790         }, {
791                 .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
792                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
793                 .mask = 0,
794                 .value = 40,
795         }
796 };
797
798 static unsigned char v4mapped[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
799                                     0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
800
801 static gboolean mask_compare(const unsigned char *one, const unsigned char *two, int mask)
802 {
803         if (mask > 8) {
804                 if (memcmp(one, two, mask / 8))
805                         return FALSE;
806                 one += mask / 8;
807                 two += mask / 8;
808                 mask %= 8;
809         }
810         if (mask && ((*one ^ *two) >> (8-mask)))
811             return FALSE;
812
813         return TRUE;
814 }
815
816 static int match_gai_table(struct sockaddr *sa, const struct gai_table *tbl)
817 {
818         struct sockaddr_in *sin = (void *)sa;
819         struct sockaddr_in6 *sin6 = (void *)sa;
820         void *addr;
821
822         if (sa->sa_family == AF_INET) {
823                 addr = v4mapped;
824                 memcpy(v4mapped+12, &sin->sin_addr, NS_INADDRSZ);
825         } else
826                 addr = &sin6->sin6_addr;
827
828         while (1) {
829                 if (mask_compare(addr, tbl->addr, tbl->mask))
830                         return tbl->value;
831                 tbl++;
832         }
833 }
834
835 static int rfc3484_compare(const void *__one, const void *__two)
836 {
837         const struct sort_result *one = __one;
838         const struct sort_result *two = __two;
839
840         /* Rule 1: Avoid unusable destinations */
841         if (one->reachable && !two->reachable)
842                 return -1;
843         else if (two->reachable && !one->reachable)
844                 return 1;
845
846         /* Rule 2: Prefer matching scope */
847
848         /* Rule 3: Avoid deprecated addresses */
849
850         /* Rule 4: Prefer home addresses */
851
852         /* Rule 5: Prefer matching label */
853         if (one->dst_label == one->src_label &&
854             two->dst_label != two->src_label)
855                 return -1;
856         else if (two->dst_label == two->src_label &&
857                  one->dst_label != one->src_label)
858                 return 1;
859
860         /* Rule 6: Prefer higher precedence */
861         if (one->precedence > two->precedence)
862                 return -1;
863         else if (two->precedence > one->precedence)
864                 return 1;
865
866         /* Rule 7: Prefer native transport */
867
868         /* Rule 8: Prefer smaller scope */
869
870         /* Rule 9: Use longest matching prefix */
871
872         /* Rule 10: Otherwise, leave the order unchanged */
873         if (one < two)
874                 return -1;
875         else
876                 return 1;
877 }
878
879 static void rfc3484_sort_results(struct resolv_lookup *lookup)
880 {
881         int i;
882
883         for (i = 0; i < lookup->nr_results; i++) {
884                 struct sort_result *res = &lookup->results[i];
885                 find_srcaddr(res);
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);
889         }
890
891         qsort(lookup->results, lookup->nr_results, sizeof(struct sort_result),
892               rfc3484_compare);
893 }