dnsproxy: Fallback to resolv.conf if dnsproxy fails
[framework/connectivity/connman.git] / src / resolver.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <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
34 #include "connman.h"
35
36 #define RESOLVER_FLAG_PUBLIC (1 << 0)
37
38 struct entry_data {
39         char *interface;
40         char *domain;
41         char *server;
42         unsigned int flags;
43         guint timeout;
44 };
45
46 static GSList *entry_list = NULL;
47 static connman_bool_t dnsproxy_enabled = FALSE;
48
49 struct resolvfile_entry {
50         char *interface;
51         char *domain;
52         char *server;
53 };
54
55 static GList *resolvfile_list = NULL;
56
57 static void resolvfile_remove_entries(GList *entries)
58 {
59         GList *list;
60
61         for (list = entries; list; list = list->next) {
62                 struct resolvfile_entry *entry = list->data;
63
64                 resolvfile_list = g_list_remove(resolvfile_list, entry);
65
66                 g_free(entry->server);
67                 g_free(entry->domain);
68                 g_free(entry->interface);
69                 g_free(entry);
70         }
71
72         g_list_free(entries);
73 }
74
75 static int resolvfile_export(void)
76 {
77         GList *list;
78         GString *content;
79         int fd, err;
80         unsigned int count;
81         mode_t old_umask;
82
83         content = g_string_new("# Generated by Connection Manager\n");
84
85         /*
86          * Domains and nameservers are added in reverse so that the most
87          * recently appended entry is the primary one. No more than
88          * MAXDNSRCH/MAXNS entries are used.
89          */
90
91         for (count = 0, list = g_list_last(resolvfile_list);
92                                                 list && (count < MAXDNSRCH);
93                                                 list = g_list_previous(list)) {
94                 struct resolvfile_entry *entry = list->data;
95
96                 if (!entry->domain)
97                         continue;
98
99                 if (count == 0)
100                         g_string_append_printf(content, "search ");
101
102                 g_string_append_printf(content, "%s ", entry->domain);
103                 count++;
104         }
105
106         if (count)
107                 g_string_append_printf(content, "\n");
108
109         for (count = 0, list = g_list_last(resolvfile_list);
110                                                 list && (count < MAXNS);
111                                                 list = g_list_previous(list)) {
112                 struct resolvfile_entry *entry = list->data;
113
114                 if (!entry->server)
115                         continue;
116
117                 g_string_append_printf(content, "nameserver %s\n",
118                                                                 entry->server);
119                 count++;
120         }
121
122         old_umask = umask(022);
123
124         fd = open("/etc/resolv.conf", O_RDWR | O_CREAT,
125                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
126         if (fd < 0) {
127                 err = -errno;
128                 goto done;
129         }
130
131         if (ftruncate(fd, 0) < 0) {
132                 err = -errno;
133                 goto failed;
134         }
135
136         err = 0;
137
138         if (write(fd, content->str, content->len) < 0)
139                 err = -errno;
140
141 failed:
142         close(fd);
143
144 done:
145         g_string_free(content, TRUE);
146         umask(old_umask);
147
148         return err;
149 }
150
151 int __connman_resolvfile_append(const char *interface, const char *domain,
152                                                         const char *server)
153 {
154         struct resolvfile_entry *entry;
155
156         DBG("interface %s server %s", interface, server);
157
158         if (interface == NULL)
159                 return -ENOENT;
160
161         entry = g_try_new0(struct resolvfile_entry, 1);
162         if (entry == NULL)
163                 return -ENOMEM;
164
165         entry->interface = g_strdup(interface);
166         entry->domain = g_strdup(domain);
167         entry->server = g_strdup(server);
168
169         resolvfile_list = g_list_append(resolvfile_list, entry);
170
171         return resolvfile_export();
172 }
173
174 int __connman_resolvfile_remove(const char *interface, const char *domain,
175                                                         const char *server)
176 {
177         GList *list, *matches = NULL;
178
179         DBG("interface %s server %s", interface, server);
180
181         for (list = resolvfile_list; list; list = g_list_next(list)) {
182                 struct resolvfile_entry *entry = list->data;
183
184                 if (interface != NULL &&
185                                 g_strcmp0(entry->interface, interface) != 0)
186                         continue;
187
188                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
189                         continue;
190
191                 if (g_strcmp0(entry->server, server) != 0)
192                         continue;
193
194                 matches = g_list_append(matches, entry);
195         }
196
197         resolvfile_remove_entries(matches);
198
199         return resolvfile_export();
200 }
201
202 static void remove_entries(GSList *entries)
203 {
204         GSList *list;
205
206         for (list = entries; list; list = list->next) {
207                 struct entry_data *entry = list->data;
208
209                 entry_list = g_slist_remove(entry_list, entry);
210
211                 if (dnsproxy_enabled == TRUE) {
212                         __connman_dnsproxy_remove(entry->interface, entry->domain,
213                                                         entry->server);
214                 } else {
215                         __connman_resolvfile_remove(entry->interface, entry->domain,
216                                                         entry->server);
217                 }
218
219                 if (entry->timeout)
220                         g_source_remove(entry->timeout);
221                 g_free(entry->server);
222                 g_free(entry->domain);
223                 g_free(entry->interface);
224                 g_free(entry);
225         }
226
227         g_slist_free(entries);
228 }
229
230 static gboolean resolver_expire_cb(gpointer user_data)
231 {
232         struct entry_data *entry = user_data;
233         GSList *list;
234
235         DBG("interface %s domain %s server %s",
236             entry->interface, entry->domain, entry->server);
237
238         list = g_slist_append(NULL, entry);
239         remove_entries(list);
240
241         return FALSE;
242 }
243
244 static int append_resolver(const char *interface, const char *domain,
245                            const char *server, unsigned int lifetime,
246                            unsigned int flags)
247 {
248         struct entry_data *entry;
249
250         DBG("interface %s domain %s server %s lifetime %d flags %d",
251             interface, domain, server, lifetime, flags);
252
253         if (server == NULL && domain == NULL)
254                 return -EINVAL;
255
256         entry = g_try_new0(struct entry_data, 1);
257         if (entry == NULL)
258                 return -ENOMEM;
259
260         entry->interface = g_strdup(interface);
261         entry->domain = g_strdup(domain);
262         entry->server = g_strdup(server);
263         entry->flags = flags;
264         if (lifetime)
265                 entry->timeout = g_timeout_add_seconds(lifetime,
266                                                        resolver_expire_cb,
267                                                        entry);
268
269         entry_list = g_slist_append(entry_list, entry);
270
271         if (dnsproxy_enabled == TRUE)
272                 __connman_dnsproxy_append(interface, domain, server);
273         else
274                 __connman_resolvfile_append(interface, domain, server);
275
276         return 0;
277 }
278
279 /**
280  * connman_resolver_append:
281  * @interface: network interface
282  * @domain: domain limitation
283  * @server: server address
284  *
285  * Append resolver server address to current list
286  */
287 int connman_resolver_append(const char *interface, const char *domain,
288                                                 const char *server)
289 {
290         DBG("interface %s domain %s server %s", interface, domain, server);
291
292         return append_resolver(interface, domain, server, 0, 0);
293 }
294
295 /**
296  * connman_resolver_append_lifetime:
297  * @interface: network interface
298  * @domain: domain limitation
299  * @server: server address
300  * @timeout: server lifetime in seconds
301  *
302  * Append resolver server address to current list
303  */
304 int connman_resolver_append_lifetime(const char *interface, const char *domain,
305                                      const char *server, unsigned int lifetime)
306 {
307         GSList *list;
308
309         DBG("interface %s domain %s server %s lifetime %d",
310             interface, domain, server, lifetime);
311
312         if (server == NULL)
313                 return -EINVAL;
314
315         for (list = entry_list; list; list = list->next) {
316                 struct entry_data *entry = list->data;
317
318                 if (!entry->timeout ||
319                     g_strcmp0(entry->interface, interface) ||
320                     g_strcmp0(entry->domain, domain) ||
321                     g_strcmp0(entry->server, server))
322                         continue;
323
324                 g_source_remove(entry->timeout);
325                 entry->timeout = g_timeout_add_seconds(lifetime,
326                                                        resolver_expire_cb,
327                                                        entry);
328                 return 0;
329         }
330         return append_resolver(interface, domain, server, lifetime, 0);
331 }
332
333 /**
334  * connman_resolver_remove:
335  * @interface: network interface
336  * @domain: domain limitation
337  * @server: server address
338  *
339  * Remover resolver server address from current list
340  */
341 int connman_resolver_remove(const char *interface, const char *domain,
342                                                         const char *server)
343 {
344         GSList *list, *matches = NULL;
345
346         DBG("interface %s domain %s server %s", interface, domain, server);
347
348         if (server == NULL)
349                 return -EINVAL;
350
351         for (list = entry_list; list; list = list->next) {
352                 struct entry_data *entry = list->data;
353
354                 if (interface != NULL &&
355                                 g_strcmp0(entry->interface, interface) != 0)
356                         continue;
357
358                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
359                         continue;
360
361                 if (g_strcmp0(entry->server, server) != 0)
362                         continue;
363
364                 matches = g_slist_append(matches, entry);
365         }
366
367         if (matches == NULL)
368                 return -ENOENT;
369
370         remove_entries(matches);
371
372         return 0;
373 }
374
375 /**
376  * connman_resolver_remove_all:
377  * @interface: network interface
378  *
379  * Remove all resolver server address for the specified interface
380  */
381 int connman_resolver_remove_all(const char *interface)
382 {
383         GSList *list, *matches = NULL;
384
385         DBG("interface %s", interface);
386
387         if (interface == NULL)
388                 return -EINVAL;
389
390         for (list = entry_list; list; list = list->next) {
391                 struct entry_data *entry = list->data;
392
393                 if (g_strcmp0(entry->interface, interface) != 0)
394                         continue;
395
396                 matches = g_slist_append(matches, entry);
397         }
398
399         if (matches == NULL)
400                 return -ENOENT;
401
402         remove_entries(matches);
403
404         return 0;
405 }
406
407 /**
408  * connman_resolver_append_public_server:
409  * @server: server address
410  *
411  * Append public resolver server address to current list
412  */
413 int connman_resolver_append_public_server(const char *server)
414 {
415         DBG("server %s", server);
416
417         return append_resolver(NULL, NULL, server, 0, RESOLVER_FLAG_PUBLIC);
418 }
419
420 /**
421  * connman_resolver_remove_public_server:
422  * @server: server address
423  *
424  * Remove public resolver server address to current list
425  */
426 int connman_resolver_remove_public_server(const char *server)
427 {
428         DBG("server %s", server);
429
430         return connman_resolver_remove(NULL, NULL, server);
431 }
432
433 /**
434  * connman_resolver_flush:
435  *
436  * Flush pending resolver requests
437  */
438 void connman_resolver_flush(void)
439 {
440         if (dnsproxy_enabled == TRUE)
441                 __connman_dnsproxy_flush();
442
443         return;
444 }
445
446 int __connman_resolver_init(connman_bool_t dnsproxy)
447 {
448         DBG("dnsproxy %d", dnsproxy);
449
450         if (dnsproxy == FALSE)
451                 return 0;
452
453         if (__connman_dnsproxy_init() < 0) {
454                 /* Fall back to resolv.conf */
455                 return 0;
456         }
457
458         dnsproxy_enabled = TRUE;
459
460         return 0;
461 }
462
463 void __connman_resolver_cleanup(void)
464 {
465         DBG("");
466
467         if (dnsproxy_enabled == TRUE)
468                 __connman_dnsproxy_cleanup();
469 }