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