Remove selftest file and option
[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 struct resolvfile_entry {
316         char *interface;
317         char *domain;
318         char *server;
319 };
320
321 static GList *resolvfile_list = NULL;
322
323 static void resolvfile_remove_entries(GList *entries)
324 {
325         GList *list;
326
327         for (list = entries; list; list = list->next) {
328                 struct resolvfile_entry *entry = list->data;
329
330                 resolvfile_list = g_list_remove(resolvfile_list, entry);
331
332                 g_free(entry->server);
333                 g_free(entry->domain);
334                 g_free(entry->interface);
335                 g_free(entry);
336         }
337
338         g_list_free(entries);
339 }
340
341 static int resolvfile_export(void)
342 {
343         GList *list;
344         GString *content;
345         int fd, err;
346         unsigned int count;
347         mode_t old_umask;
348
349         content = g_string_new("# Generated by Connection Manager\n");
350
351         /*
352          * Nameservers are added in reverse so that the most recently
353          * appended entry is the primary nameserver. No more than MAXNS
354          * nameservers are used.
355          */
356         for (count = 0, list = g_list_last(resolvfile_list);
357                                                 list && (count < MAXNS);
358                                                 list = g_list_previous(list)) {
359                 struct resolvfile_entry *entry = list->data;
360                 g_string_append_printf(content, "nameserver %s\n",
361                                                                 entry->server);
362                 count++;
363         }
364
365         old_umask = umask(022);
366
367         fd = open("/etc/resolv.conf", O_RDWR | O_CREAT,
368                                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
369         if (fd < 0) {
370                 err = -errno;
371                 goto done;
372         }
373
374         if (ftruncate(fd, 0) < 0) {
375                 err = -errno;
376                 goto failed;
377         }
378
379         err = 0;
380
381         if (write(fd, content->str, content->len) < 0)
382                 err = -errno;
383
384 failed:
385         close(fd);
386
387 done:
388         g_string_free(content, TRUE);
389         umask(old_umask);
390
391         return err;
392 }
393
394 static int resolvfile_append(const char *interface, const char *domain,
395                                                         const char *server)
396 {
397         struct resolvfile_entry *entry;
398
399         DBG("interface %s server %s", interface, server);
400
401         if (interface == NULL)
402                 return -ENOENT;
403
404         entry = g_try_new0(struct resolvfile_entry, 1);
405         if (entry == NULL)
406                 return -ENOMEM;
407
408         entry->interface = g_strdup(interface);
409         entry->domain = g_strdup(domain);
410         entry->server = g_strdup(server);
411
412         resolvfile_list = g_list_append(resolvfile_list, entry);
413
414         return resolvfile_export();
415 }
416
417 static int resolvfile_remove(const char *interface, const char *domain,
418                                                         const char *server)
419 {
420         GList *list, *matches = NULL;
421
422         DBG("interface %s server %s", interface, server);
423
424         for (list = resolvfile_list; list; list = g_list_next(list)) {
425                 struct resolvfile_entry *entry = list->data;
426
427                 if (interface != NULL &&
428                                 g_strcmp0(entry->interface, interface) != 0)
429                         continue;
430
431                 if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
432                         continue;
433
434                 if (g_strcmp0(entry->server, server) != 0)
435                         continue;
436
437                 matches = g_list_append(matches, entry);
438         }
439
440         resolvfile_remove_entries(matches);
441
442         return resolvfile_export();
443 }
444
445 static struct connman_resolver resolvfile_resolver = {
446         .name           = "resolvfile",
447         .priority       = CONNMAN_RESOLVER_PRIORITY_LOW,
448         .append         = resolvfile_append,
449         .remove         = resolvfile_remove,
450 };
451
452 int __connman_resolver_init(void)
453 {
454         DBG("");
455
456         return connman_resolver_register(&resolvfile_resolver);
457 }
458
459 void __connman_resolver_cleanup(void)
460 {
461         DBG("");
462
463         connman_resolver_unregister(&resolvfile_resolver);
464 }