Remove moblin.org domain from resolver selftest
[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", "connman.net", "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
394         /*
395          * Nameservers are added in reverse so that the most recently
396          * appended entry is the primary nameserver. No more than MAXNS
397          * nameservers are used.
398          */
399         for (count = 0, list = g_list_last(resolvfile_list);
400                                                 list && (count < MAXNS);
401                                                 list = g_list_previous(list)) {
402                 struct resolvfile_entry *entry = list->data;
403                 g_string_append_printf(content, "nameserver %s\n",
404                                                                 entry->server);
405                 count++;
406         }
407
408         old_umask = umask(022);
409
410         fd = open("/etc/resolv.conf", O_RDWR | O_CREAT,
411                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
412         if (fd < 0) {
413                 err = -errno;
414                 goto done;
415         }
416
417         if (ftruncate(fd, 0) < 0) {
418                 err = -errno;
419                 goto failed;
420         }
421
422         err = 0;
423
424         if (write(fd, content->str, content->len) < 0)
425                 err = -errno;
426
427 failed:
428         close(fd);
429
430 done:
431         g_string_free(content, TRUE);
432         umask(old_umask);
433
434         return err;
435 }
436
437 static int resolvfile_append(const char *interface, const char *domain,
438                                                         const char *server)
439 {
440         struct resolvfile_entry *entry;
441
442         DBG("interface %s server %s", interface, server);
443
444         if (interface == NULL)
445                 return -ENOENT;
446
447         entry = g_try_new0(struct resolvfile_entry, 1);
448         if (entry == NULL)
449                 return -ENOMEM;
450
451         entry->interface = g_strdup(interface);
452         entry->domain = g_strdup(domain);
453         entry->server = g_strdup(server);
454
455         resolvfile_list = g_list_append(resolvfile_list, entry);
456
457         return resolvfile_export();
458 }
459
460 static int resolvfile_remove(const char *interface, const char *domain,
461                                                         const char *server)
462 {
463         GList *list, *matches = NULL;
464
465         DBG("interface %s server %s", interface, server);
466
467         for (list = resolvfile_list; list; list = g_list_next(list)) {
468                 struct resolvfile_entry *entry = list->data;
469
470                 if (interface != NULL &&
471                                 g_strcmp0(entry->interface, interface) != 0)
472                         continue;
473
474                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
475                         continue;
476
477                 if (g_strcmp0(entry->server, server) != 0)
478                         continue;
479
480                 matches = g_list_append(matches, entry);
481         }
482
483         resolvfile_remove_entries(matches);
484
485         return resolvfile_export();
486 }
487
488 static struct connman_resolver resolvfile_resolver = {
489         .name           = "resolvfile",
490         .priority       = CONNMAN_RESOLVER_PRIORITY_LOW,
491         .append         = resolvfile_append,
492         .remove         = resolvfile_remove,
493 };
494
495 int __connman_resolver_init(void)
496 {
497         DBG("");
498
499         return connman_resolver_register(&resolvfile_resolver);
500 }
501
502 void __connman_resolver_cleanup(void)
503 {
504         DBG("");
505
506         connman_resolver_unregister(&resolvfile_resolver);
507 }