Add support for using public DNS servers
[framework/connectivity/connman.git] / src / resolver.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  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
33 #include "connman.h"
34
35 struct entry_data {
36         struct connman_resolver *resolver;
37         char *interface;
38         char *domain;
39         char *server;
40 };
41
42 static GSList *entry_list = NULL;
43 static GSList *resolver_list = NULL;
44
45 static void remove_entries(GSList *entries)
46 {
47         GSList *list;
48
49         for (list = entries; list; list = list->next) {
50                 struct entry_data *entry = list->data;
51                 struct connman_resolver *resolver = entry->resolver;
52
53                 entry_list = g_slist_remove(entry_list, entry);
54
55                 if (resolver && resolver->remove)
56                         resolver->remove(entry->interface, entry->domain,
57                                                                 entry->server);
58
59                 g_free(entry->server);
60                 g_free(entry->domain);
61                 g_free(entry->interface);
62                 g_free(entry);
63         }
64
65         g_slist_free(entries);
66 }
67
68 static gint compare_priority(gconstpointer a, gconstpointer b)
69 {
70         const struct connman_resolver *resolver1 = a;
71         const struct connman_resolver *resolver2 = b;
72
73         return resolver2->priority - resolver1->priority;
74 }
75
76 /**
77  * connman_resolver_register:
78  * @resolver: resolver module
79  *
80  * Register a new resolver module
81  *
82  * Returns: %0 on success
83  */
84 int connman_resolver_register(struct connman_resolver *resolver)
85 {
86         GSList *list;
87
88         DBG("resolver %p name %s", resolver, resolver->name);
89
90         resolver_list = g_slist_insert_sorted(resolver_list, resolver,
91                                                         compare_priority);
92
93         if (resolver->append == NULL)
94                 return 0;
95
96         for (list = entry_list; list; list = list->next) {
97                 struct entry_data *entry = list->data;
98
99                 if (entry->resolver)
100                         continue;
101
102                 if (resolver->append(entry->interface, entry->domain,
103                                                         entry->server) == 0)
104                         entry->resolver = resolver;
105         }
106
107         return 0;
108 }
109
110 /**
111  * connman_resolver_unregister:
112  * @resolver: resolver module
113  *
114  * Remove a previously registered resolver module
115  */
116 void connman_resolver_unregister(struct connman_resolver *resolver)
117 {
118         GSList *list, *matches = NULL;
119
120         DBG("resolver %p name %s", resolver, resolver->name);
121
122         resolver_list = g_slist_remove(resolver_list, resolver);
123
124         for (list = entry_list; list; list = list->next) {
125                 struct entry_data *entry = list->data;
126
127                 if (entry->resolver != resolver)
128                         continue;
129
130                 matches = g_slist_append(matches, entry);
131         }
132
133         remove_entries(matches);
134 }
135
136 /**
137  * connman_resolver_append:
138  * @interface: network interface
139  * @domain: domain limitation
140  * @server: server address
141  *
142  * Append resolver server address to current list
143  */
144 int connman_resolver_append(const char *interface, const char *domain,
145                                                         const char *server)
146 {
147         struct entry_data *entry;
148         GSList *list;
149
150         DBG("interface %s domain %s server %s", interface, domain, server);
151
152         if (server == NULL)
153                 return -EINVAL;
154
155         entry = g_try_new0(struct entry_data, 1);
156         if (entry == NULL)
157                 return -ENOMEM;
158
159         entry->interface = g_strdup(interface);
160         entry->domain = g_strdup(domain);
161         entry->server = g_strdup(server);
162
163         entry_list = g_slist_append(entry_list, entry);
164
165         for (list = resolver_list; list; list = list->next) {
166                 struct connman_resolver *resolver = list->data;
167
168                 if (resolver->append == NULL)
169                         continue;
170
171                 if (resolver->append(interface, domain, server) == 0) {
172                         entry->resolver = resolver;
173                         break;
174                 }
175         }
176
177         return 0;
178 }
179
180 /**
181  * connman_resolver_remove:
182  * @interface: network interface
183  * @domain: domain limitation
184  * @server: server address
185  *
186  * Remover resolver server address from current list
187  */
188 int connman_resolver_remove(const char *interface, const char *domain,
189                                                         const char *server)
190 {
191         GSList *list, *matches = NULL;
192
193         DBG("interface %s domain %s server %s", interface, domain, server);
194
195         if (server == NULL)
196                 return -EINVAL;
197
198         for (list = entry_list; list; list = list->next) {
199                 struct entry_data *entry = list->data;
200
201                 if (interface != NULL &&
202                                 g_strcmp0(entry->interface, interface) != 0)
203                         continue;
204
205                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
206                         continue;
207
208                 if (g_strcmp0(entry->server, server) != 0)
209                         continue;
210
211                 matches = g_slist_append(matches, entry);
212         }
213
214         if (matches == NULL)
215                 return -ENOENT;
216
217         remove_entries(matches);
218
219         return 0;
220 }
221
222 /**
223  * connman_resolver_remove_all:
224  * @interface: network interface
225  *
226  * Remove all resolver server address for the specified interface
227  */
228 int connman_resolver_remove_all(const char *interface)
229 {
230         GSList *list, *matches = NULL;
231
232         DBG("interface %s", interface);
233
234         if (interface == NULL)
235                 return -EINVAL;
236
237         for (list = entry_list; list; list = list->next) {
238                 struct entry_data *entry = list->data;
239
240                 if (g_strcmp0(entry->interface, interface) != 0)
241                         continue;
242
243                 matches = g_slist_append(matches, entry);
244         }
245
246         if (matches == NULL)
247                 return -ENOENT;
248
249         remove_entries(matches);
250
251         return 0;
252 }
253
254 /**
255  * connman_resolver_append_public_server:
256  * @server: server address
257  *
258  * Append public resolver server address to current list
259  */
260 int connman_resolver_append_public_server(const char *server)
261 {
262         DBG("server %s", server);
263
264         return connman_resolver_append(NULL, NULL, server);
265 }
266
267 /**
268  * connman_resolver_remove_public_server:
269  * @server: server address
270  *
271  * Remove public resolver server address to current list
272  */
273 int connman_resolver_remove_public_server(const char *server)
274 {
275         DBG("server %s", server);
276
277         return connman_resolver_remove(NULL, NULL, server);
278 }
279
280 static int selftest_append(const char *interface, const char *domain,
281                                                         const char *server)
282 {
283         DBG("server %s", server);
284
285         return 0;
286 }
287
288 static int selftest_remove(const char *interface, const char *domain,
289                                                         const char *server)
290 {
291         DBG("server %s", server);
292
293         return 0;
294 }
295
296 static struct connman_resolver selftest_resolver = {
297         .name     = "selftest",
298         .priority = CONNMAN_RESOLVER_PRIORITY_HIGH + 42,
299         .append   = selftest_append,
300         .remove   = selftest_remove,
301 };
302
303 int __connman_resolver_selftest(void)
304 {
305         connman_resolver_append("wlan0", "lwn.net", "192.168.0.1");
306
307         connman_resolver_register(&selftest_resolver);
308
309         connman_resolver_append("eth0", "moblin.org", "192.168.42.1");
310         connman_resolver_append("wlan0", "lwn.net", "192.168.0.2");
311
312         connman_resolver_append_public_server("8.8.8.8");
313
314         connman_resolver_remove_public_server("8.8.8.8");
315
316         connman_resolver_remove_all("wlan0");
317
318         connman_resolver_unregister(&selftest_resolver);
319
320         return 0;
321 }
322
323 static int resolvfile_append(const char *interface, const char *domain,
324                                                         const char *server)
325 {
326         char *cmd;
327         int fd, len, err;
328
329         DBG("interface %s server %s", interface, server);
330
331         fd = open("/etc/resolv.conf", O_RDWR | O_CREAT,
332                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
333         if (fd < 0)
334                 return -errno;
335
336         err = ftruncate(fd, 0);
337
338         cmd = g_strdup_printf("# Generated by Connection Manager\n"
339                                                 "options edns0\n"
340                                                 "nameserver %s\n", server);
341
342         len = write(fd, cmd, strlen(cmd));
343
344         g_free(cmd);
345
346         close(fd);
347
348         return 0;
349 }
350
351 static int resolvfile_remove(const char *interface, const char *domain,
352                                                         const char *server)
353 {
354         DBG("interface %s server %s", interface, server);
355
356         return 0;
357 }
358
359 static struct connman_resolver resolvfile_resolver = {
360         .name           = "resolvfile",
361         .priority       = CONNMAN_RESOLVER_PRIORITY_LOW,
362         .append         = resolvfile_append,
363         .remove         = resolvfile_remove,
364 };
365
366 int __connman_resolver_init(void)
367 {
368         DBG("");
369
370         return connman_resolver_register(&resolvfile_resolver);
371 }
372
373 void __connman_resolver_cleanup(void)
374 {
375         DBG("");
376
377         connman_resolver_unregister(&resolvfile_resolver);
378 }