Add a resolver flush method
[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         struct connman_resolver *resolver;
40         char *interface;
41         char *domain;
42         char *server;
43         unsigned int flags;
44 };
45
46 static GSList *entry_list = NULL;
47 static GSList *resolver_list = NULL;
48
49 static void remove_entries(GSList *entries)
50 {
51         GSList *list;
52
53         for (list = entries; list; list = list->next) {
54                 struct entry_data *entry = list->data;
55                 struct connman_resolver *resolver = entry->resolver;
56
57                 entry_list = g_slist_remove(entry_list, entry);
58
59                 if (resolver && resolver->remove)
60                         resolver->remove(entry->interface, entry->domain,
61                                                                 entry->server);
62
63                 g_free(entry->server);
64                 g_free(entry->domain);
65                 g_free(entry->interface);
66                 g_free(entry);
67         }
68
69         g_slist_free(entries);
70 }
71
72 static gint compare_priority(gconstpointer a, gconstpointer b)
73 {
74         const struct connman_resolver *resolver1 = a;
75         const struct connman_resolver *resolver2 = b;
76
77         return resolver2->priority - resolver1->priority;
78 }
79
80 /**
81  * connman_resolver_register:
82  * @resolver: resolver module
83  *
84  * Register a new resolver module
85  *
86  * Returns: %0 on success
87  */
88 int connman_resolver_register(struct connman_resolver *resolver)
89 {
90         GSList *list;
91
92         DBG("resolver %p name %s", resolver, resolver->name);
93
94         resolver_list = g_slist_insert_sorted(resolver_list, resolver,
95                                                         compare_priority);
96
97         if (resolver->append == NULL)
98                 return 0;
99
100         for (list = entry_list; list; list = list->next) {
101                 struct entry_data *entry = list->data;
102
103                 if (entry->resolver)
104                         continue;
105
106                 if (resolver->append(entry->interface, entry->domain,
107                                                         entry->server) == 0)
108                         entry->resolver = resolver;
109         }
110
111         return 0;
112 }
113
114 /**
115  * connman_resolver_unregister:
116  * @resolver: resolver module
117  *
118  * Remove a previously registered resolver module
119  */
120 void connman_resolver_unregister(struct connman_resolver *resolver)
121 {
122         GSList *list, *matches = NULL;
123
124         DBG("resolver %p name %s", resolver, resolver->name);
125
126         resolver_list = g_slist_remove(resolver_list, resolver);
127
128         for (list = entry_list; list; list = list->next) {
129                 struct entry_data *entry = list->data;
130
131                 if (entry->resolver != resolver)
132                         continue;
133
134                 matches = g_slist_append(matches, entry);
135         }
136
137         remove_entries(matches);
138 }
139
140 static int append_resolver(const char *interface, const char *domain,
141                                         const char *server, unsigned int flags)
142 {
143         struct entry_data *entry;
144         GSList *list;
145
146         DBG("interface %s domain %s server %s flags %d",
147                                         interface, domain, server, flags);
148
149         if (server == NULL)
150                 return -EINVAL;
151
152         entry = g_try_new0(struct entry_data, 1);
153         if (entry == NULL)
154                 return -ENOMEM;
155
156         entry->interface = g_strdup(interface);
157         entry->domain = g_strdup(domain);
158         entry->server = g_strdup(server);
159         entry->flags = flags;
160
161         entry_list = g_slist_append(entry_list, entry);
162
163         for (list = resolver_list; list; list = list->next) {
164                 struct connman_resolver *resolver = list->data;
165
166                 if (resolver->append == NULL)
167                         continue;
168
169                 if (resolver->append(interface, domain, server) == 0) {
170                         entry->resolver = resolver;
171                         break;
172                 }
173         }
174
175         return 0;
176 }
177
178 /**
179  * connman_resolver_append:
180  * @interface: network interface
181  * @domain: domain limitation
182  * @server: server address
183  *
184  * Append resolver server address to current list
185  */
186 int connman_resolver_append(const char *interface, const char *domain,
187                                                         const char *server)
188 {
189         DBG("interface %s domain %s server %s", interface, domain, server);
190
191         return append_resolver(interface, domain, server, 0);
192 }
193
194 /**
195  * connman_resolver_remove:
196  * @interface: network interface
197  * @domain: domain limitation
198  * @server: server address
199  *
200  * Remover resolver server address from current list
201  */
202 int connman_resolver_remove(const char *interface, const char *domain,
203                                                         const char *server)
204 {
205         GSList *list, *matches = NULL;
206
207         DBG("interface %s domain %s server %s", interface, domain, server);
208
209         if (server == NULL)
210                 return -EINVAL;
211
212         for (list = entry_list; list; list = list->next) {
213                 struct entry_data *entry = list->data;
214
215                 if (interface != NULL &&
216                                 g_strcmp0(entry->interface, interface) != 0)
217                         continue;
218
219                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
220                         continue;
221
222                 if (g_strcmp0(entry->server, server) != 0)
223                         continue;
224
225                 matches = g_slist_append(matches, entry);
226         }
227
228         if (matches == NULL)
229                 return -ENOENT;
230
231         remove_entries(matches);
232
233         return 0;
234 }
235
236 /**
237  * connman_resolver_remove_all:
238  * @interface: network interface
239  *
240  * Remove all resolver server address for the specified interface
241  */
242 int connman_resolver_remove_all(const char *interface)
243 {
244         GSList *list, *matches = NULL;
245
246         DBG("interface %s", interface);
247
248         if (interface == NULL)
249                 return -EINVAL;
250
251         for (list = entry_list; list; list = list->next) {
252                 struct entry_data *entry = list->data;
253
254                 if (g_strcmp0(entry->interface, interface) != 0)
255                         continue;
256
257                 matches = g_slist_append(matches, entry);
258         }
259
260         if (matches == NULL)
261                 return -ENOENT;
262
263         remove_entries(matches);
264
265         return 0;
266 }
267
268 /**
269  * connman_resolver_append_public_server:
270  * @server: server address
271  *
272  * Append public resolver server address to current list
273  */
274 int connman_resolver_append_public_server(const char *server)
275 {
276         DBG("server %s", server);
277
278         return append_resolver(NULL, NULL, server, RESOLVER_FLAG_PUBLIC);
279 }
280
281 /**
282  * connman_resolver_remove_public_server:
283  * @server: server address
284  *
285  * Remove public resolver server address to current list
286  */
287 int connman_resolver_remove_public_server(const char *server)
288 {
289         DBG("server %s", server);
290
291         return connman_resolver_remove(NULL, NULL, server);
292 }
293
294 /**
295  * connman_resolver_flush:
296  *
297  * Flush pending resolver requests
298  */
299 void connman_resolver_flush(void)
300 {
301         GSList *list;
302
303         for (list = resolver_list; list; list = list->next) {
304                 struct connman_resolver *resolver = list->data;
305
306                 if (resolver->flush == NULL)
307                         continue;
308
309                 resolver->flush();
310         }
311
312         return;
313 }
314
315 static int selftest_append(const char *interface, const char *domain,
316                                                         const char *server)
317 {
318         DBG("server %s", server);
319
320         return 0;
321 }
322
323 static int selftest_remove(const char *interface, const char *domain,
324                                                         const char *server)
325 {
326         DBG("server %s", server);
327
328         return 0;
329 }
330
331 static struct connman_resolver selftest_resolver = {
332         .name     = "selftest",
333         .priority = CONNMAN_RESOLVER_PRIORITY_HIGH + 42,
334         .append   = selftest_append,
335         .remove   = selftest_remove,
336 };
337
338 int __connman_resolver_selftest(void)
339 {
340         connman_resolver_append("wlan0", "lwn.net", "192.168.0.1");
341
342         connman_resolver_register(&selftest_resolver);
343
344         connman_resolver_append("eth0", "moblin.org", "192.168.42.1");
345         connman_resolver_append("wlan0", "lwn.net", "192.168.0.2");
346
347         connman_resolver_append_public_server("8.8.8.8");
348
349         connman_resolver_remove_public_server("8.8.8.8");
350
351         connman_resolver_remove_all("wlan0");
352
353         connman_resolver_unregister(&selftest_resolver);
354
355         return 0;
356 }
357
358 struct resolvfile_entry {
359         char *interface;
360         char *domain;
361         char *server;
362 };
363
364 static GList *resolvfile_list = NULL;
365
366 static void resolvfile_remove_entries(GList *entries)
367 {
368         GList *list;
369
370         for (list = entries; list; list = list->next) {
371                 struct resolvfile_entry *entry = list->data;
372
373                 resolvfile_list = g_list_remove(resolvfile_list, entry);
374
375                 g_free(entry->server);
376                 g_free(entry->domain);
377                 g_free(entry->interface);
378                 g_free(entry);
379         }
380
381         g_list_free(entries);
382 }
383
384 static int resolvfile_export(void)
385 {
386         GList *list;
387         GString *content;
388         int fd, err;
389         unsigned int count;
390         mode_t old_umask;
391
392         content = g_string_new("# Generated by Connection Manager\n"
393                                                 "options edns0\n");
394
395         /*
396          * Nameservers are added in reverse so that the most recently
397          * appended entry is the primary nameserver. No more than MAXNS
398          * nameservers are used.
399          */
400         for (count = 0, list = g_list_last(resolvfile_list);
401                                                 list && (count < MAXNS);
402                                                 list = g_list_previous(list)) {
403                 struct resolvfile_entry *entry = list->data;
404                 g_string_append_printf(content, "nameserver %s\n",
405                                                                 entry->server);
406                 count++;
407         }
408
409         old_umask = umask(022);
410
411         fd = open("/etc/resolv.conf", O_RDWR | O_CREAT,
412                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
413         if (fd < 0) {
414                 err = -errno;
415                 goto done;
416         }
417
418         if (ftruncate(fd, 0) < 0) {
419                 err = -errno;
420                 goto failed;
421         }
422
423         err = 0;
424
425         if (write(fd, content->str, content->len) < 0)
426                 err = -errno;
427
428 failed:
429         close(fd);
430
431 done:
432         g_string_free(content, TRUE);
433         umask(old_umask);
434
435         return err;
436 }
437
438 static int resolvfile_append(const char *interface, const char *domain,
439                                                         const char *server)
440 {
441         struct resolvfile_entry *entry;
442
443         DBG("interface %s server %s", interface, server);
444
445         if (interface == NULL)
446                 return -ENOENT;
447
448         entry = g_try_new0(struct resolvfile_entry, 1);
449         if (entry == NULL)
450                 return -ENOMEM;
451
452         entry->interface = g_strdup(interface);
453         entry->domain = g_strdup(domain);
454         entry->server = g_strdup(server);
455
456         resolvfile_list = g_list_append(resolvfile_list, entry);
457
458         return resolvfile_export();
459 }
460
461 static int resolvfile_remove(const char *interface, const char *domain,
462                                                         const char *server)
463 {
464         GList *list, *matches = NULL;
465
466         DBG("interface %s server %s", interface, server);
467
468         for (list = resolvfile_list; list; list = g_list_next(list)) {
469                 struct resolvfile_entry *entry = list->data;
470
471                 if (interface != NULL &&
472                                 g_strcmp0(entry->interface, interface) != 0)
473                         continue;
474
475                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
476                         continue;
477
478                 if (g_strcmp0(entry->server, server) != 0)
479                         continue;
480
481                 matches = g_list_append(matches, entry);
482         }
483
484         resolvfile_remove_entries(matches);
485
486         return resolvfile_export();
487 }
488
489 static struct connman_resolver resolvfile_resolver = {
490         .name           = "resolvfile",
491         .priority       = CONNMAN_RESOLVER_PRIORITY_LOW,
492         .append         = resolvfile_append,
493         .remove         = resolvfile_remove,
494 };
495
496 int __connman_resolver_init(void)
497 {
498         DBG("");
499
500         return connman_resolver_register(&resolvfile_resolver);
501 }
502
503 void __connman_resolver_cleanup(void)
504 {
505         DBG("");
506
507         connman_resolver_unregister(&resolvfile_resolver);
508 }