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