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