Adding connman-test subpackage
[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 #if defined TIZEN_EXT
126 /*
127  * Description: /etc path is read-only in SLP
128  *              /opt/etc/resolv.conf rather than /etc/resolv.conf
129  */
130         fd = open("/opt/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC,
131                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
132 #else
133         fd = open("/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC,
134                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
135 #endif
136         if (fd < 0) {
137                 err = -errno;
138                 goto done;
139         }
140
141         if (ftruncate(fd, 0) < 0) {
142                 err = -errno;
143                 goto failed;
144         }
145
146         err = 0;
147
148         if (write(fd, content->str, content->len) < 0)
149                 err = -errno;
150
151 failed:
152         close(fd);
153
154 done:
155         g_string_free(content, TRUE);
156         umask(old_umask);
157
158         return err;
159 }
160
161 int __connman_resolvfile_append(const char *interface, const char *domain,
162                                                         const char *server)
163 {
164         struct resolvfile_entry *entry;
165
166         DBG("interface %s server %s", interface, server);
167
168         if (interface == NULL)
169                 return -ENOENT;
170
171         entry = g_try_new0(struct resolvfile_entry, 1);
172         if (entry == NULL)
173                 return -ENOMEM;
174
175         entry->interface = g_strdup(interface);
176         entry->domain = g_strdup(domain);
177         entry->server = g_strdup(server);
178
179         resolvfile_list = g_list_append(resolvfile_list, entry);
180
181         return resolvfile_export();
182 }
183
184 int __connman_resolvfile_remove(const char *interface, const char *domain,
185                                                         const char *server)
186 {
187         GList *list, *matches = NULL;
188
189         DBG("interface %s server %s", interface, server);
190
191         for (list = resolvfile_list; list; list = g_list_next(list)) {
192                 struct resolvfile_entry *entry = list->data;
193
194                 if (interface != NULL &&
195                                 g_strcmp0(entry->interface, interface) != 0)
196                         continue;
197
198                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
199                         continue;
200
201                 if (g_strcmp0(entry->server, server) != 0)
202                         continue;
203
204                 matches = g_list_append(matches, entry);
205         }
206
207         resolvfile_remove_entries(matches);
208
209         return resolvfile_export();
210 }
211
212 static void remove_entries(GSList *entries)
213 {
214         GSList *list;
215
216         for (list = entries; list; list = list->next) {
217                 struct entry_data *entry = list->data;
218
219                 entry_list = g_slist_remove(entry_list, entry);
220
221                 if (dnsproxy_enabled == TRUE) {
222                         __connman_dnsproxy_remove(entry->interface, entry->domain,
223                                                         entry->server);
224                 } else {
225                         __connman_resolvfile_remove(entry->interface, entry->domain,
226                                                         entry->server);
227                 }
228
229                 if (entry->timeout)
230                         g_source_remove(entry->timeout);
231                 g_free(entry->server);
232                 g_free(entry->domain);
233                 g_free(entry->interface);
234                 g_free(entry);
235         }
236
237         g_slist_free(entries);
238 }
239
240 static gboolean resolver_expire_cb(gpointer user_data)
241 {
242         struct entry_data *entry = user_data;
243         GSList *list;
244         int index;
245
246         DBG("interface %s domain %s server %s",
247                         entry->interface, entry->domain, entry->server);
248
249         list = g_slist_append(NULL, entry);
250
251         index = connman_inet_ifindex(entry->interface);
252         if (index >= 0) {
253                 struct connman_service *service;
254                 service = __connman_service_lookup_from_index(index);
255                 if (service != NULL)
256                         __connman_service_nameserver_remove(service,
257                                                         entry->server, TRUE);
258         }
259
260         remove_entries(list);
261
262         return FALSE;
263 }
264
265 static int append_resolver(const char *interface, const char *domain,
266                                 const char *server, unsigned int lifetime,
267                                                         unsigned int flags)
268 {
269         struct entry_data *entry;
270
271         DBG("interface %s domain %s server %s lifetime %d flags %d",
272                                 interface, domain, server, lifetime, flags);
273
274         if (server == NULL && domain == NULL)
275                 return -EINVAL;
276
277         entry = g_try_new0(struct entry_data, 1);
278         if (entry == NULL)
279                 return -ENOMEM;
280
281         entry->interface = g_strdup(interface);
282         entry->domain = g_strdup(domain);
283         entry->server = g_strdup(server);
284         entry->flags = flags;
285         if (lifetime) {
286                 int index;
287                 entry->timeout = g_timeout_add_seconds(lifetime,
288                                                 resolver_expire_cb, entry);
289
290                 /*
291                  * We update the service only for those nameservers
292                  * that are automagically added via netlink (lifetime > 0)
293                  */
294                 index = connman_inet_ifindex(interface);
295                 if (index >= 0) {
296                         struct connman_service *service;
297                         service = __connman_service_lookup_from_index(index);
298                         if (service != NULL)
299                                 __connman_service_nameserver_append(service,
300                                                                 server, TRUE);
301                 }
302         }
303         entry_list = g_slist_append(entry_list, entry);
304
305         if (dnsproxy_enabled == TRUE)
306                 __connman_dnsproxy_append(interface, domain, server);
307         else
308                 __connman_resolvfile_append(interface, domain, server);
309
310         return 0;
311 }
312
313 /**
314  * connman_resolver_append:
315  * @interface: network interface
316  * @domain: domain limitation
317  * @server: server address
318  *
319  * Append resolver server address to current list
320  */
321 int connman_resolver_append(const char *interface, const char *domain,
322                                                 const char *server)
323 {
324         GSList *list, *matches = NULL;
325
326         DBG("interface %s domain %s server %s", interface, domain, server);
327
328         if (server == NULL && domain == NULL)
329                 return -EINVAL;
330
331         for (list = entry_list; list; list = list->next) {
332                 struct entry_data *entry = list->data;
333
334                 if (entry->timeout > 0 ||
335                                 g_strcmp0(entry->interface, interface) != 0 ||
336                                 g_strcmp0(entry->domain, domain) != 0 ||
337                                 g_strcmp0(entry->server, server) != 0)
338                         continue;
339
340                 matches = g_slist_append(matches, entry);
341         }
342
343         if (matches != NULL)
344                 remove_entries(matches);
345
346         return append_resolver(interface, domain, server, 0, 0);
347 }
348
349 /**
350  * connman_resolver_append_lifetime:
351  * @interface: network interface
352  * @domain: domain limitation
353  * @server: server address
354  * @timeout: server lifetime in seconds
355  *
356  * Append resolver server address to current list
357  */
358 int connman_resolver_append_lifetime(const char *interface, const char *domain,
359                                 const char *server, unsigned int lifetime)
360 {
361         GSList *list;
362
363         DBG("interface %s domain %s server %s lifetime %d",
364                                 interface, domain, server, lifetime);
365
366         if (server == NULL)
367                 return -EINVAL;
368
369         for (list = entry_list; list; list = list->next) {
370                 struct entry_data *entry = list->data;
371
372                 if (!entry->timeout ||
373                                 g_strcmp0(entry->interface, interface) ||
374                                 g_strcmp0(entry->domain, domain) ||
375                                 g_strcmp0(entry->server, server))
376                         continue;
377
378                 g_source_remove(entry->timeout);
379
380                 if (lifetime == 0) {
381                         resolver_expire_cb(entry);
382                         return 0;
383                 }
384
385                 entry->timeout = g_timeout_add_seconds(lifetime,
386                                                 resolver_expire_cb, entry);
387                 return 0;
388         }
389
390         return append_resolver(interface, domain, server, lifetime, 0);
391 }
392
393 /**
394  * connman_resolver_remove:
395  * @interface: network interface
396  * @domain: domain limitation
397  * @server: server address
398  *
399  * Remover resolver server address from current list
400  */
401 int connman_resolver_remove(const char *interface, const char *domain,
402                                                         const char *server)
403 {
404         GSList *list, *matches = NULL;
405
406         DBG("interface %s domain %s server %s", interface, domain, server);
407
408         if (server == NULL)
409                 return -EINVAL;
410
411         for (list = entry_list; list; list = list->next) {
412                 struct entry_data *entry = list->data;
413
414                 if (interface != NULL &&
415                                 g_strcmp0(entry->interface, interface) != 0)
416                         continue;
417
418                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
419                         continue;
420
421                 if (g_strcmp0(entry->server, server) != 0)
422                         continue;
423
424                 matches = g_slist_append(matches, entry);
425         }
426
427         if (matches == NULL)
428                 return -ENOENT;
429
430         remove_entries(matches);
431
432         return 0;
433 }
434
435 /**
436  * connman_resolver_remove_all:
437  * @interface: network interface
438  *
439  * Remove all resolver server address for the specified interface
440  */
441 int connman_resolver_remove_all(const char *interface)
442 {
443         GSList *list, *matches = NULL;
444
445         DBG("interface %s", interface);
446
447         if (interface == NULL)
448                 return -EINVAL;
449
450         for (list = entry_list; list; list = list->next) {
451                 struct entry_data *entry = list->data;
452
453                 if (g_strcmp0(entry->interface, interface) != 0)
454                         continue;
455
456                 matches = g_slist_append(matches, entry);
457         }
458
459         if (matches == NULL)
460                 return -ENOENT;
461
462         remove_entries(matches);
463
464         return 0;
465 }
466
467 /**
468  * connman_resolver_append_public_server:
469  * @server: server address
470  *
471  * Append public resolver server address to current list
472  */
473 int connman_resolver_append_public_server(const char *server)
474 {
475         DBG("server %s", server);
476
477         return append_resolver(NULL, NULL, server, 0, RESOLVER_FLAG_PUBLIC);
478 }
479
480 /**
481  * connman_resolver_remove_public_server:
482  * @server: server address
483  *
484  * Remove public resolver server address to current list
485  */
486 int connman_resolver_remove_public_server(const char *server)
487 {
488         DBG("server %s", server);
489
490         return connman_resolver_remove(NULL, NULL, server);
491 }
492
493 /**
494  * connman_resolver_flush:
495  *
496  * Flush pending resolver requests
497  */
498 void connman_resolver_flush(void)
499 {
500         if (dnsproxy_enabled == TRUE)
501                 __connman_dnsproxy_flush();
502
503         return;
504 }
505
506 int __connman_resolver_init(connman_bool_t dnsproxy)
507 {
508         DBG("dnsproxy %d", dnsproxy);
509
510         if (dnsproxy == FALSE)
511                 return 0;
512
513         if (__connman_dnsproxy_init() < 0) {
514                 /* Fall back to resolv.conf */
515                 return 0;
516         }
517
518         dnsproxy_enabled = TRUE;
519
520         return 0;
521 }
522
523 void __connman_resolver_cleanup(void)
524 {
525         DBG("");
526
527         if (dnsproxy_enabled == TRUE)
528                 __connman_dnsproxy_cleanup();
529 }