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