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