68fa4f7b234fbfab41be7663cd27e02f444fb435
[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 #define _GNU_SOURCE
27 #include <stdio.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <resolv.h>
34
35 #include "connman.h"
36
37 #define RESOLVER_FLAG_PUBLIC (1 << 0)
38
39 struct entry_data {
40         char *interface;
41         char *domain;
42         char *server;
43         unsigned int flags;
44         guint timeout;
45 };
46
47 static GSList *entry_list = NULL;
48 static connman_bool_t dnsproxy_enabled = FALSE;
49
50 struct resolvfile_entry {
51         char *interface;
52         char *domain;
53         char *server;
54 };
55
56 static GList *resolvfile_list = NULL;
57
58 static void resolvfile_remove_entries(GList *entries)
59 {
60         GList *list;
61
62         for (list = entries; list; list = list->next) {
63                 struct resolvfile_entry *entry = list->data;
64
65                 resolvfile_list = g_list_remove(resolvfile_list, entry);
66
67                 g_free(entry->server);
68                 g_free(entry->domain);
69                 g_free(entry->interface);
70                 g_free(entry);
71         }
72
73         g_list_free(entries);
74 }
75
76 static int resolvfile_export(void)
77 {
78         GList *list;
79         GString *content;
80         int fd, err;
81         unsigned int count;
82         mode_t old_umask;
83
84         content = g_string_new("# Generated by Connection Manager\n");
85
86         /*
87          * Domains and nameservers are added in reverse so that the most
88          * recently appended entry is the primary one. No more than
89          * MAXDNSRCH/MAXNS entries are used.
90          */
91
92         for (count = 0, list = g_list_last(resolvfile_list);
93                                                 list && (count < MAXDNSRCH);
94                                                 list = g_list_previous(list)) {
95                 struct resolvfile_entry *entry = list->data;
96
97                 if (!entry->domain)
98                         continue;
99
100                 if (count == 0)
101                         g_string_append_printf(content, "search ");
102
103                 g_string_append_printf(content, "%s ", entry->domain);
104                 count++;
105         }
106
107         if (count)
108                 g_string_append_printf(content, "\n");
109
110         for (count = 0, list = g_list_last(resolvfile_list);
111                                                 list && (count < MAXNS);
112                                                 list = g_list_previous(list)) {
113                 struct resolvfile_entry *entry = list->data;
114
115                 if (!entry->server)
116                         continue;
117
118                 g_string_append_printf(content, "nameserver %s\n",
119                                                                 entry->server);
120                 count++;
121         }
122
123         old_umask = umask(022);
124
125         fd = open("/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC,
126                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
127         if (fd < 0) {
128                 err = -errno;
129                 goto done;
130         }
131
132         if (ftruncate(fd, 0) < 0) {
133                 err = -errno;
134                 goto failed;
135         }
136
137         err = 0;
138
139         if (write(fd, content->str, content->len) < 0)
140                 err = -errno;
141
142 failed:
143         close(fd);
144
145 done:
146         g_string_free(content, TRUE);
147         umask(old_umask);
148
149         return err;
150 }
151
152 int __connman_resolvfile_append(const char *interface, const char *domain,
153                                                         const char *server)
154 {
155         struct resolvfile_entry *entry;
156
157         DBG("interface %s server %s", interface, server);
158
159         if (interface == NULL)
160                 return -ENOENT;
161
162         entry = g_try_new0(struct resolvfile_entry, 1);
163         if (entry == NULL)
164                 return -ENOMEM;
165
166         entry->interface = g_strdup(interface);
167         entry->domain = g_strdup(domain);
168         entry->server = g_strdup(server);
169
170         resolvfile_list = g_list_append(resolvfile_list, entry);
171
172         return resolvfile_export();
173 }
174
175 int __connman_resolvfile_remove(const char *interface, const char *domain,
176                                                         const char *server)
177 {
178         GList *list, *matches = NULL;
179
180         DBG("interface %s server %s", interface, server);
181
182         for (list = resolvfile_list; list; list = g_list_next(list)) {
183                 struct resolvfile_entry *entry = list->data;
184
185                 if (interface != NULL &&
186                                 g_strcmp0(entry->interface, interface) != 0)
187                         continue;
188
189                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
190                         continue;
191
192                 if (g_strcmp0(entry->server, server) != 0)
193                         continue;
194
195                 matches = g_list_append(matches, entry);
196         }
197
198         resolvfile_remove_entries(matches);
199
200         return resolvfile_export();
201 }
202
203 static void remove_entries(GSList *entries)
204 {
205         GSList *list;
206
207         for (list = entries; list; list = list->next) {
208                 struct entry_data *entry = list->data;
209
210                 entry_list = g_slist_remove(entry_list, entry);
211
212                 if (dnsproxy_enabled == TRUE) {
213                         __connman_dnsproxy_remove(entry->interface, entry->domain,
214                                                         entry->server);
215                 } else {
216                         __connman_resolvfile_remove(entry->interface, entry->domain,
217                                                         entry->server);
218                 }
219
220                 if (entry->timeout)
221                         g_source_remove(entry->timeout);
222                 g_free(entry->server);
223                 g_free(entry->domain);
224                 g_free(entry->interface);
225                 g_free(entry);
226         }
227
228         g_slist_free(entries);
229 }
230
231 static gboolean resolver_expire_cb(gpointer user_data)
232 {
233         struct entry_data *entry = user_data;
234         GSList *list;
235
236         DBG("interface %s domain %s server %s",
237                         entry->interface, entry->domain, entry->server);
238
239         list = g_slist_append(NULL, entry);
240         remove_entries(list);
241
242         return FALSE;
243 }
244
245 static int append_resolver(const char *interface, const char *domain,
246                                 const char *server, unsigned int lifetime,
247                                                         unsigned int flags)
248 {
249         struct entry_data *entry;
250
251         DBG("interface %s domain %s server %s lifetime %d flags %d",
252                                 interface, domain, server, lifetime, flags);
253
254         if (server == NULL && domain == NULL)
255                 return -EINVAL;
256
257         entry = g_try_new0(struct entry_data, 1);
258         if (entry == NULL)
259                 return -ENOMEM;
260
261         entry->interface = g_strdup(interface);
262         entry->domain = g_strdup(domain);
263         entry->server = g_strdup(server);
264         entry->flags = flags;
265         if (lifetime)
266                 entry->timeout = g_timeout_add_seconds(lifetime,
267                                                 resolver_expire_cb, 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, entry);
327                 return 0;
328         }
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 }