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