Do not automatically remove watches for service names
[framework/connectivity/connman.git] / gresolv / 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/socket.h>
32 #include <arpa/inet.h>
33 #include <arpa/nameser.h>
34
35 #include "gresolv.h"
36
37 struct resolv_query {
38         GResolv *resolv;
39
40         guint id;
41         guint timeout;
42
43         uint16_t msgid;
44
45         GResolvResultFunc result_func;
46         gpointer result_data;
47 };
48
49 struct resolv_nameserver {
50         GResolv *resolv;
51
52         char *address;
53         uint16_t port;
54         unsigned long flags;
55
56         GIOChannel *udp_channel;
57         guint udp_watch;
58 };
59
60 struct _GResolv {
61         gint ref_count;
62
63         guint next_query_id;
64         GQueue *query_queue;
65
66         int index;
67         GList *nameserver_list;
68
69         GResolvDebugFunc debug_func;
70         gpointer debug_data;
71 };
72
73 static inline void debug(GResolv *resolv, const char *format, ...)
74 {
75         char str[256];
76         va_list ap;
77
78         if (resolv->debug_func == NULL)
79                 return;
80
81         va_start(ap, format);
82
83         if (vsnprintf(str, sizeof(str), format, ap) > 0)
84                 resolv->debug_func(str, resolv->debug_data);
85
86         va_end(ap);
87 }
88
89 static void destroy_query(struct resolv_query *query)
90 {
91         if (query->timeout > 0)
92                 g_source_remove(query->timeout);
93
94         g_free(query);
95 }
96
97 static gboolean query_timeout(gpointer user_data)
98 {
99         struct resolv_query *query = user_data;
100         GResolv *resolv = query->resolv;
101
102         query->timeout = 0;
103
104         if (query->result_func != NULL)
105                 query->result_func(G_RESOLV_RESULT_STATUS_NO_RESPONSE,
106                                                 NULL, query->result_data);
107
108         destroy_query(query);
109         g_queue_remove(resolv->query_queue, query);
110
111         return FALSE;
112 }
113
114 static void free_nameserver(struct resolv_nameserver *nameserver)
115 {
116         if (nameserver == NULL)
117                 return;
118
119         if (nameserver->udp_watch > 0)
120                 g_source_remove(nameserver->udp_watch);
121
122         if (nameserver->udp_channel != NULL)
123                 g_io_channel_unref(nameserver->udp_channel);
124
125         g_free(nameserver->address);
126         g_free(nameserver);
127 }
128
129 static void flush_nameservers(GResolv *resolv)
130 {
131         GList *list;
132
133         for (list = g_list_first(resolv->nameserver_list);
134                                         list; list = g_list_next(list))
135                 free_nameserver(list->data);
136
137         g_list_free(resolv->nameserver_list);
138         resolv->nameserver_list = NULL;
139 }
140
141 static int send_query(GResolv *resolv, const unsigned char *buf, int len)
142 {
143         GList *list;
144
145         if (resolv->nameserver_list == NULL)
146                 return -ENOENT;
147
148         for (list = g_list_first(resolv->nameserver_list);
149                                         list; list = g_list_next(list)) {
150                 struct resolv_nameserver *nameserver = list->data;
151                 int sk, sent;
152
153                 if (nameserver->udp_channel == NULL)
154                         continue;
155
156                 sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
157
158                 sent = send(sk, buf, len, 0);
159         }
160
161         return 0;
162 }
163
164 static gint compare_query_id(gconstpointer a, gconstpointer b)
165 {
166         const struct resolv_query *query = a;
167         guint id = GPOINTER_TO_UINT(b);
168
169         if (query->id < id)
170                 return -1;
171
172         if (query->id > id)
173                 return 1;
174
175         return 0;
176 }
177
178 static gint compare_query_msgid(gconstpointer a, gconstpointer b)
179 {
180         const struct resolv_query *query = a;
181         uint16_t msgid = GPOINTER_TO_UINT(b);
182
183         if (query->msgid < msgid)
184                 return -1;
185
186         if (query->msgid > msgid)
187                 return 1;
188
189         return 0;
190 }
191
192 static void parse_response(struct resolv_nameserver *nameserver,
193                                         const unsigned char *buf, int len)
194 {
195         GResolv *resolv = nameserver->resolv;
196         GResolvResultStatus status;
197         GList *list;
198         char **results;
199         ns_msg msg;
200         ns_rr rr;
201         int i, n, rcode, count;
202
203         debug(resolv, "response from %s", nameserver->address);
204
205         ns_initparse(buf, len, &msg);
206
207         rcode = ns_msg_getflag(msg, ns_f_rcode);
208         count = ns_msg_count(msg, ns_s_an);
209
210         debug(resolv, "msg id: 0x%04x rcode: %d count: %d",
211                                         ns_msg_id(msg), rcode, count);
212
213         switch (rcode) {
214         case 0:
215                 status = G_RESOLV_RESULT_STATUS_SUCCESS;
216                 break;
217         case 1:
218                 status = G_RESOLV_RESULT_STATUS_FORMAT_ERROR;
219                 break;
220         case 2:
221                 status = G_RESOLV_RESULT_STATUS_SERVER_FAILURE;
222                 break;
223         case 3:
224                 status = G_RESOLV_RESULT_STATUS_NAME_ERROR;
225                 break;
226         case 4:
227                 status = G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED;
228                 break;
229         case 5:
230                 status = G_RESOLV_RESULT_STATUS_REFUSED;
231                 break;
232         default:
233                 status = G_RESOLV_RESULT_STATUS_ERROR;
234                 break;
235         }
236
237         results = g_try_new(char *, count + 1);
238         if (results == NULL)
239                 return;
240
241         for (i = 0, n = 0; i < count; i++) {
242                 char result[100];
243
244                 ns_parserr(&msg, ns_s_an, i, &rr);
245
246                 if (ns_rr_class(rr) != ns_c_in)
247                         continue;
248
249                 if (ns_rr_type(rr) != ns_t_a)
250                         continue;
251
252                 if (ns_rr_rdlen(rr) != NS_INADDRSZ)
253                         continue;
254
255                 inet_ntop(AF_INET, ns_rr_rdata(rr), result, sizeof(result));
256
257                 results[n++] = g_strdup(result);
258         }
259
260         results[n] = NULL;
261
262         list = g_queue_find_custom(resolv->query_queue,
263                         GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid);
264
265         if (list != NULL) {
266                 struct resolv_query *query = list->data;
267
268                 if (query->result_func != NULL)
269                         query->result_func(status, results,
270                                                 query->result_data);
271
272                 destroy_query(query);
273                 g_queue_remove(resolv->query_queue, query);
274         }
275
276         g_strfreev(results);
277 }
278
279 static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond,
280                                                         gpointer user_data)
281 {
282         struct resolv_nameserver *nameserver = user_data;
283         unsigned char buf[4096];
284         int sk, len;
285
286         if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
287                 nameserver->udp_watch = 0;
288                 return FALSE;
289         }
290
291         sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
292
293         len = recv(sk, buf, sizeof(buf), 0);
294         if (len < 12)
295                 return TRUE;
296
297         parse_response(nameserver, buf, len);
298
299         return TRUE;
300 }
301
302 static int connect_udp_channel(struct resolv_nameserver *nameserver)
303 {
304         struct sockaddr_in sin;
305         int sk;
306
307         sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
308         if (sk < 0)
309                 return -EIO;
310
311         memset(&sin, 0, sizeof(sin));
312         sin.sin_family = AF_INET;
313         sin.sin_port = htons(nameserver->port);
314         sin.sin_addr.s_addr = inet_addr(nameserver->address);
315
316         if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
317                 close(sk);
318                 return -EIO;
319         }
320
321         nameserver->udp_channel = g_io_channel_unix_new(sk);
322         if (nameserver->udp_channel == NULL) {
323                 close(sk);
324                 return -ENOMEM;
325         }
326
327         g_io_channel_set_close_on_unref(nameserver->udp_channel, TRUE);
328
329         nameserver->udp_watch = g_io_add_watch(nameserver->udp_channel,
330                                                 G_IO_IN, received_udp_data,
331                                                                 nameserver);
332
333         return 0;
334 }
335
336 GResolv *g_resolv_new(int index)
337 {
338         GResolv *resolv;
339
340         if (index < 0)
341                 return NULL;
342
343         resolv = g_try_new0(GResolv, 1);
344         if (resolv == NULL)
345                 return NULL;
346
347         resolv->ref_count = 1;
348
349         resolv->next_query_id = 1;
350         resolv->query_queue = g_queue_new();
351
352         if (resolv->query_queue == NULL) {
353                 g_free(resolv);
354                 return NULL;
355         }
356
357         resolv->index = index;
358         resolv->nameserver_list = NULL;
359
360         return resolv;
361 }
362
363 GResolv *g_resolv_ref(GResolv *resolv)
364 {
365         if (resolv == NULL)
366                 return NULL;
367
368         g_atomic_int_inc(&resolv->ref_count);
369
370         return resolv;
371 }
372
373 void g_resolv_unref(GResolv *resolv)
374 {
375         struct resolv_query *query;
376
377         if (resolv == NULL)
378                 return;
379
380         if (g_atomic_int_dec_and_test(&resolv->ref_count) == FALSE)
381                 return;
382
383         while ((query = g_queue_pop_head(resolv->query_queue)))
384                 destroy_query(query);
385
386         g_queue_free(resolv->query_queue);
387
388         flush_nameservers(resolv);
389
390         g_free(resolv);
391 }
392
393 void g_resolv_set_debug(GResolv *resolv,
394                                 GResolvDebugFunc func, gpointer user_data)
395 {
396         if (resolv == NULL)
397                 return;
398
399         resolv->debug_func = func;
400         resolv->debug_data = user_data;
401 }
402
403 gboolean g_resolv_add_nameserver(GResolv *resolv, const char *address,
404                                         uint16_t port, unsigned long flags)
405 {
406         struct resolv_nameserver *nameserver;
407
408         if (resolv == NULL)
409                 return FALSE;
410
411         nameserver = g_try_new0(struct resolv_nameserver, 1);
412         if (nameserver == NULL)
413                 return FALSE;
414
415         nameserver->address = g_strdup(address);
416         nameserver->port = port;
417         nameserver->flags = flags;
418
419         if (connect_udp_channel(nameserver) < 0) {
420                 free_nameserver(nameserver);
421                 return FALSE;
422         }
423
424         nameserver->resolv = resolv;
425
426         resolv->nameserver_list = g_list_append(resolv->nameserver_list,
427                                                                 nameserver);
428
429         debug(resolv, "setting nameserver %s", address);
430
431         return TRUE;
432 }
433
434 void g_resolv_flush_nameservers(GResolv *resolv)
435 {
436         if (resolv == NULL)
437                 return;
438
439         flush_nameservers(resolv);
440 }
441
442 guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
443                                 GResolvResultFunc func, gpointer user_data)
444 {
445         struct resolv_query *query;
446         unsigned char buf[4096];
447         int len;
448
449         debug(resolv, "lookup hostname %s", hostname);
450
451         if (resolv == NULL)
452                 return 0;
453
454         query = g_try_new0(struct resolv_query, 1);
455         if (query == NULL)
456                 return 0;
457
458         query->id = resolv->next_query_id++;
459
460         len = res_mkquery(ns_o_query, hostname, ns_c_in, ns_t_a,
461                                         NULL, 0, NULL, buf, sizeof(buf));
462
463         query->msgid = buf[0] << 8 | buf[1];
464
465         query->result_func = func;
466         query->result_data = user_data;
467
468         if (send_query(resolv, buf, len) < 0) {
469                 g_free(query);
470                 return -EIO;
471         }
472
473         query->resolv = resolv;
474
475         g_queue_push_tail(resolv->query_queue, query);
476
477         query->timeout = g_timeout_add_seconds(5, query_timeout, query);
478
479         return query->id;
480 }
481
482 gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id)
483 {
484         GList *list;
485
486         list = g_queue_find_custom(resolv->query_queue,
487                                 GUINT_TO_POINTER(id), compare_query_id);
488
489         if (list == NULL)
490                 return FALSE;
491
492         destroy_query(list->data);
493         g_queue_remove(resolv->query_queue, list->data);
494
495         return TRUE;
496 }