Added timer delay to get the response
[platform/upstream/connman.git] / src / resolver.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  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 <stdio.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <resolv.h>
33 #include <netdb.h>
34
35 #include "connman.h"
36
37 /*
38  * Just to avoid build failure due to missing STATEDIR
39  */
40 #if defined TIZEN_EXT
41 #ifdef STATEDIR
42 #undef STATEDIR
43 #endif
44 #define STATEDIR "/etc"
45 #endif
46
47 #define RESOLV_CONF_STATEDIR STATEDIR"/resolv.conf"
48 #define RESOLV_CONF_ETC "/etc/resolv.conf"
49
50 #define RESOLVER_FLAG_PUBLIC (1 << 0)
51
52 /*
53  * Threshold for RDNSS lifetime. Will be used to trigger RS
54  * before RDNSS entries actually expire
55  */
56 #define RESOLVER_LIFETIME_REFRESH_THRESHOLD 0.8
57
58 struct entry_data {
59         int index;
60         char *domain;
61         char *server;
62         int family;
63         unsigned int flags;
64         unsigned int lifetime;
65         guint timeout;
66 };
67
68 static GSList *entry_list = NULL;
69 static bool dnsproxy_enabled = false;
70
71 struct resolvfile_entry {
72         int index;
73         char *domain;
74         char *server;
75 };
76
77 static GList *resolvfile_list = NULL;
78
79 static void resolvfile_remove_entries(GList *entries)
80 {
81         GList *list;
82
83         for (list = entries; list; list = list->next) {
84                 struct resolvfile_entry *entry = list->data;
85
86                 resolvfile_list = g_list_remove(resolvfile_list, entry);
87
88                 g_free(entry->server);
89                 g_free(entry->domain);
90                 g_free(entry);
91         }
92
93         g_list_free(entries);
94 }
95
96 static int resolvfile_export(void)
97 {
98         GList *list;
99         GString *content;
100         int fd, err;
101         unsigned int count;
102         mode_t old_umask;
103
104         content = g_string_new("# Generated by Connection Manager\n");
105
106         /*
107          * Domains and nameservers are added in reverse so that the most
108          * recently appended entry is the primary one. No more than
109          * MAXDNSRCH/MAXNS entries are used.
110          */
111
112         for (count = 0, list = g_list_first(resolvfile_list);
113                                                 list && (count < MAXDNSRCH);
114                                                 list = g_list_next(list)) {
115                 struct resolvfile_entry *entry = list->data;
116
117                 if (!entry->domain)
118                         continue;
119
120                 if (count == 0)
121                         g_string_append_printf(content, "search ");
122
123                 g_string_append_printf(content, "%s ", entry->domain);
124                 count++;
125         }
126
127         if (count)
128                 g_string_append_printf(content, "\n");
129
130         for (count = 0, list = g_list_first(resolvfile_list);
131                                                 list && (count < MAXNS);
132                                                 list = g_list_next(list)) {
133                 struct resolvfile_entry *entry = list->data;
134
135                 if (!entry->server)
136                         continue;
137
138                 g_string_append_printf(content, "nameserver %s\n",
139                                                                 entry->server);
140                 count++;
141         }
142
143         old_umask = umask(022);
144
145         fd = open(RESOLV_CONF_STATEDIR, O_RDWR | O_CREAT | O_CLOEXEC,
146                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
147         if (fd < 0) {
148                 connman_warn_once("Cannot create "RESOLV_CONF_STATEDIR" "
149                         "falling back to "RESOLV_CONF_ETC);
150
151                 fd = open(RESOLV_CONF_ETC, O_RDWR | O_CREAT | O_CLOEXEC,
152                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
153
154                 if (fd < 0) {
155                         err = -errno;
156                         goto done;
157                 }
158         }
159
160         if (ftruncate(fd, 0) < 0) {
161                 err = -errno;
162                 goto failed;
163         }
164
165         err = 0;
166
167         if (write(fd, content->str, content->len) < 0)
168                 err = -errno;
169
170 failed:
171         close(fd);
172
173 done:
174         g_string_free(content, TRUE);
175         umask(old_umask);
176
177         return err;
178 }
179
180 int __connman_resolvfile_append(int index, const char *domain,
181                                                         const char *server)
182 {
183         struct resolvfile_entry *entry;
184
185         DBG("index %d server %s", index, server);
186
187         if (index < 0)
188                 return -ENOENT;
189
190         entry = g_try_new0(struct resolvfile_entry, 1);
191         if (!entry)
192                 return -ENOMEM;
193
194         entry->index = index;
195         entry->domain = g_strdup(domain);
196         entry->server = g_strdup(server);
197
198         resolvfile_list = g_list_append(resolvfile_list, entry);
199
200         return resolvfile_export();
201 }
202
203 int __connman_resolvfile_remove(int index, const char *domain,
204                                                         const char *server)
205 {
206         GList *list, *matches = NULL;
207
208         DBG("index %d server %s", index, server);
209
210         for (list = resolvfile_list; list; list = g_list_next(list)) {
211                 struct resolvfile_entry *entry = list->data;
212
213                 if (index >= 0 && entry->index != index)
214                         continue;
215
216                 if (domain && g_strcmp0(entry->domain, domain) != 0)
217                         continue;
218
219                 if (g_strcmp0(entry->server, server) != 0)
220                         continue;
221
222                 matches = g_list_append(matches, entry);
223         }
224
225         resolvfile_remove_entries(matches);
226
227         return resolvfile_export();
228 }
229
230 void __connman_resolver_append_fallback_nameservers(void)
231 {
232         GSList *list;
233
234         for (list = entry_list; list; list = list->next) {
235                 struct entry_data *entry = list->data;
236
237                 if (entry->index >= 0 && entry->server)
238                         return;
239         }
240
241         for (list = entry_list; list; list = list->next) {
242                 struct entry_data *entry = list->data;
243
244                 if (entry->index != -1 || !entry->server)
245                         continue;
246
247                 DBG("index %d server %s", entry->index, entry->server);
248
249                 if (dnsproxy_enabled) {
250                         __connman_dnsproxy_append(entry->index, entry->domain,
251                                         entry->server);
252                 } else {
253                         __connman_resolvfile_append(entry->index,
254                                         entry->domain, entry->server);
255                 }
256         }
257 }
258
259 static void remove_fallback_nameservers(void)
260 {
261         GSList *list;
262
263         for (list = entry_list; list; list = list->next) {
264                 struct entry_data *entry = list->data;
265
266                 if (entry->index >= 0 || !entry->server)
267                         continue;
268
269                 DBG("index %d server %s", entry->index, entry->server);
270
271                 if (dnsproxy_enabled) {
272                         __connman_dnsproxy_remove(entry->index, entry->domain,
273                                         entry->server);
274                 } else {
275                         __connman_resolvfile_remove(entry->index,
276                                         entry->domain, entry->server);
277                 }
278         }
279 }
280
281 static void remove_entries(GSList *entries)
282 {
283         GSList *list;
284
285         for (list = entries; list; list = list->next) {
286                 struct entry_data *entry = list->data;
287
288                 entry_list = g_slist_remove(entry_list, entry);
289
290                 if (dnsproxy_enabled) {
291                         __connman_dnsproxy_remove(entry->index, entry->domain,
292                                                         entry->server);
293                 } else {
294                         __connman_resolvfile_remove(entry->index, entry->domain,
295                                                         entry->server);
296                 }
297
298                 if (entry->timeout)
299                         g_source_remove(entry->timeout);
300                 g_free(entry->server);
301                 g_free(entry->domain);
302                 g_free(entry);
303         }
304
305         g_slist_free(entries);
306
307         __connman_resolver_append_fallback_nameservers();
308 }
309
310 static gboolean resolver_expire_cb(gpointer user_data)
311 {
312         struct entry_data *entry = user_data;
313         GSList *list;
314
315         DBG("index %d domain %s server %s",
316                         entry->index, entry->domain, entry->server);
317
318         list = g_slist_prepend(NULL, entry);
319
320         if (entry->index >= 0) {
321                 struct connman_service *service;
322                 service = __connman_service_lookup_from_index(entry->index);
323                 if (service)
324 #if defined TIZEN_EXT
325                         __connman_service_nameserver_remove(service,
326                                         entry->server, true,
327                                         CONNMAN_IPCONFIG_TYPE_ALL);
328 #else
329                         __connman_service_nameserver_remove(service,
330                                                         entry->server, true);
331 #endif
332         }
333
334         remove_entries(list);
335
336         return FALSE;
337 }
338
339 static gboolean resolver_refresh_cb(gpointer user_data)
340 {
341         struct entry_data *entry = user_data;
342         unsigned int interval;
343         struct connman_service *service = NULL;
344
345         /* Round up what we have left from lifetime */
346         interval = entry->lifetime *
347                 (1 - RESOLVER_LIFETIME_REFRESH_THRESHOLD) + 1.0;
348
349         DBG("RDNSS start index %d domain %s "
350                         "server %s remaining lifetime %d",
351                         entry->index, entry->domain,
352                         entry->server, interval);
353
354         entry->timeout = g_timeout_add_seconds(interval,
355                         resolver_expire_cb, entry);
356
357         if (entry->index >= 0) {
358                 service = __connman_service_lookup_from_index(entry->index);
359                 if (service) {
360                         /*
361                          * Send Router Solicitation to refresh RDNSS entries
362                          * before their lifetime expires
363                          */
364                         __connman_network_refresh_rs_ipv6(
365                                         __connman_service_get_network(service),
366                                         entry->index);
367                 }
368         }
369         return FALSE;
370 }
371
372 static int append_resolver(int index, const char *domain,
373                                 const char *server, unsigned int lifetime,
374                                                         unsigned int flags)
375 {
376         struct entry_data *entry;
377         unsigned int interval;
378
379         DBG("index %d domain %s server %s lifetime %d flags %d",
380                                 index, domain, server, lifetime, flags);
381
382         if (!server && !domain)
383                 return -EINVAL;
384
385 #ifdef TIZEN_EXT
386         if (g_strcmp0(server, "0.0.0.0") == 0)
387                 return -EINVAL;
388 #endif
389
390         entry = g_try_new0(struct entry_data, 1);
391         if (!entry)
392                 return -ENOMEM;
393
394         entry->index = index;
395         entry->domain = g_strdup(domain);
396         entry->server = g_strdup(server);
397         entry->flags = flags;
398         entry->lifetime = lifetime;
399
400         if (server)
401                 entry->family = connman_inet_check_ipaddress(server);
402
403         if (lifetime) {
404                 interval = lifetime * RESOLVER_LIFETIME_REFRESH_THRESHOLD;
405
406                 DBG("RDNSS start index %d domain %s "
407                                 "server %s lifetime threshold %d",
408                                 index, domain, server, interval);
409
410                 entry->timeout = g_timeout_add_seconds(interval,
411                                 resolver_refresh_cb, entry);
412         }
413
414         if (entry->index >= 0 && entry->server)
415                 remove_fallback_nameservers();
416
417         entry_list = g_slist_append(entry_list, entry);
418
419         if (dnsproxy_enabled)
420                 __connman_dnsproxy_append(entry->index, domain, server);
421         else
422                 __connman_resolvfile_append(entry->index, domain, server);
423
424         /*
425          * We update the service only for those nameservers
426          * that are automagically added via netlink (lifetime > 0)
427          */
428         if (server && entry->index >= 0 && lifetime) {
429                 struct connman_service *service;
430                 service = __connman_service_lookup_from_index(entry->index);
431                 if (service)
432 #if defined TIZEN_EXT
433                         __connman_service_nameserver_append(service,
434                                         server, true,
435                                         CONNMAN_IPCONFIG_TYPE_ALL);
436 #else
437                         __connman_service_nameserver_append(service,
438                                                         server, true);
439 #endif
440         }
441
442         return 0;
443 }
444
445 /**
446  * connman_resolver_append:
447  * @index: network interface index
448  * @domain: domain limitation
449  * @server: server address
450  *
451  * Append resolver server address to current list
452  */
453 int connman_resolver_append(int index, const char *domain,
454                                                 const char *server)
455 {
456         GSList *list;
457
458         DBG("index %d domain %s server %s", index, domain, server);
459
460         if (!server && !domain)
461                 return -EINVAL;
462
463         for (list = entry_list; list; list = list->next) {
464                 struct entry_data *entry = list->data;
465
466                 if (entry->timeout > 0)
467                         continue;
468
469                 if (entry->index == index &&
470                                 g_strcmp0(entry->domain, domain) == 0 &&
471                                 g_strcmp0(entry->server, server) == 0) {
472                         if (dnsproxy_enabled)
473                                 __connman_dnsproxy_append(entry->index, domain,
474                                                 server);
475
476                         return -EEXIST;
477                 }
478         }
479
480         return append_resolver(index, domain, server, 0, 0);
481 }
482
483 /**
484  * connman_resolver_append_lifetime:
485  * @index: network interface index
486  * @domain: domain limitation
487  * @server: server address
488  * @timeout: server lifetime in seconds
489  *
490  * Append resolver server address to current list
491  */
492 int connman_resolver_append_lifetime(int index, const char *domain,
493                                 const char *server, unsigned int lifetime)
494 {
495         GSList *list;
496         unsigned int interval;
497
498         DBG("index %d domain %s server %s lifetime %d",
499                                 index, domain, server, lifetime);
500
501         if (!server && !domain)
502                 return -EINVAL;
503
504         for (list = entry_list; list; list = list->next) {
505                 struct entry_data *entry = list->data;
506
507                 if (entry->timeout == 0 ||
508                                 entry->index != index ||
509                                 g_strcmp0(entry->domain, domain) != 0 ||
510                                 g_strcmp0(entry->server, server) != 0)
511                         continue;
512
513                 g_source_remove(entry->timeout);
514
515                 if (lifetime == 0) {
516                         resolver_expire_cb(entry);
517                         return 0;
518                 }
519
520                 interval = lifetime * RESOLVER_LIFETIME_REFRESH_THRESHOLD;
521
522                 DBG("RDNSS start index %d domain %s "
523                                 "server %s lifetime threshold %d",
524                                 index, domain, server, interval);
525
526                 entry->timeout = g_timeout_add_seconds(interval,
527                                 resolver_refresh_cb, entry);
528                 return 0;
529         }
530
531         return append_resolver(index, domain, server, lifetime, 0);
532 }
533
534 /**
535  * connman_resolver_remove:
536  * @index: network interface index
537  * @domain: domain limitation
538  * @server: server address
539  *
540  * Remover resolver server address from current list
541  */
542 int connman_resolver_remove(int index, const char *domain, const char *server)
543 {
544         GSList *list, *matches = NULL;
545
546         DBG("index %d domain %s server %s", index, domain, server);
547
548         for (list = entry_list; list; list = list->next) {
549                 struct entry_data *entry = list->data;
550
551                 if (entry->index != index)
552                         continue;
553
554                 if (g_strcmp0(entry->domain, domain) != 0)
555                         continue;
556
557                 if (g_strcmp0(entry->server, server) != 0)
558                         continue;
559
560                 matches = g_slist_prepend(matches, entry);
561                 break;
562         }
563
564         if (!matches)
565                 return -ENOENT;
566
567         remove_entries(matches);
568
569         return 0;
570 }
571
572 /**
573  * connman_resolver_remove_all:
574  * @index: network interface index
575  *
576  * Remove all resolver server address for the specified interface index
577  */
578 int connman_resolver_remove_all(int index)
579 {
580         GSList *list, *matches = NULL;
581
582         DBG("index %d", index);
583
584         if (index < 0)
585                 return -EINVAL;
586
587         for (list = entry_list; list; list = list->next) {
588                 struct entry_data *entry = list->data;
589
590                 if (entry->index != index)
591                         continue;
592
593                 matches = g_slist_prepend(matches, entry);
594         }
595
596         if (!matches)
597                 return -ENOENT;
598
599         remove_entries(matches);
600
601         return 0;
602 }
603
604 int __connman_resolver_redo_servers(int index)
605 {
606         GSList *list;
607
608         if (!dnsproxy_enabled)
609                 return 0;
610
611         DBG("index %d", index);
612
613         if (index < 0)
614                 return -EINVAL;
615
616         for (list = entry_list; list; list = list->next) {
617                 struct entry_data *entry = list->data;
618
619                 if (entry->timeout == 0 || entry->index != index)
620                         continue;
621
622                 /*
623                  * This function must only check IPv6 server addresses so
624                  * do not remove IPv4 name servers unnecessarily.
625                  */
626                 if (entry->family != AF_INET6)
627                         continue;
628
629                 /*
630                  * We remove the server, and then re-create so that it will
631                  * use proper source addresses when sending DNS queries.
632                  */
633                 __connman_dnsproxy_remove(entry->index, entry->domain,
634                                         entry->server);
635
636                 __connman_dnsproxy_append(entry->index, entry->domain,
637                                         entry->server);
638         }
639
640         /*
641          * We want to re-add all search domains back to search
642          * domain lists as they just got removed for RDNSS IPv6-servers
643          * (above).
644          * Removal of search domains is not necessary
645          * as there can be only one instance of each search domain
646          * in the each dns-servers search domain list.
647         */
648
649         for (list = entry_list; list; list = list->next) {
650                 struct entry_data *entry = list->data;
651
652                 if (entry->index != index)
653                         continue;
654
655                 if (entry->server)
656                         continue;
657
658                 __connman_dnsproxy_append(entry->index, entry->domain,
659                                         NULL);
660         }
661
662         return 0;
663 }
664
665 static void free_entry(gpointer data)
666 {
667         struct entry_data *entry = data;
668         g_free(entry->domain);
669         g_free(entry->server);
670         g_free(entry);
671 }
672
673 static void free_resolvfile(gpointer data)
674 {
675         struct resolvfile_entry *entry = data;
676         g_free(entry->domain);
677         g_free(entry->server);
678         g_free(entry);
679 }
680
681 int __connman_resolver_set_mdns(int index, bool enabled)
682 {
683         if (!dnsproxy_enabled)
684                 return -ENOTSUP;
685
686         return __connman_dnsproxy_set_mdns(index, enabled);
687 }
688
689 int __connman_resolver_init(gboolean dnsproxy)
690 {
691         int i;
692         char **ns;
693
694         DBG("dnsproxy %d", dnsproxy);
695
696         /* get autoip nameservers */
697         ns = __connman_inet_get_pnp_nameservers(NULL);
698         for (i = 0; ns && ns[i]; i += 1) {
699                 DBG("pnp server %s", ns[i]);
700                 append_resolver(i, NULL, ns[i], 86400, 0);
701         }
702         g_strfreev(ns);
703
704         if (!dnsproxy)
705                 return 0;
706
707         if (__connman_dnsproxy_init() < 0) {
708                 /* Fall back to resolv.conf */
709                 return 0;
710         }
711
712         dnsproxy_enabled = true;
713
714         ns = connman_setting_get_string_list("FallbackNameservers");
715         for (i = 0; ns && ns[i]; i += 1) {
716                 DBG("server %s", ns[i]);
717                 append_resolver(-1, NULL, ns[i], 0, RESOLVER_FLAG_PUBLIC);
718         }
719
720         return 0;
721 }
722
723 void __connman_resolver_cleanup(void)
724 {
725         DBG("");
726
727         if (dnsproxy_enabled)
728                 __connman_dnsproxy_cleanup();
729         else {
730                 GList *list;
731                 GSList *slist;
732
733                 for (list = resolvfile_list; list; list = g_list_next(list))
734                         free_resolvfile(list->data);
735                 g_list_free(resolvfile_list);
736                 resolvfile_list = NULL;
737
738                 for (slist = entry_list; slist; slist = g_slist_next(slist))
739                         free_entry(slist->data);
740                 g_slist_free(entry_list);
741                 entry_list = NULL;
742         }
743 }