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