Fix error handling of init routine
[connman] / plugins / dnsproxy.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  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 <errno.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <arpa/inet.h>
30 #include <netinet/in.h>
31
32 #include <connman/plugin.h>
33 #include <connman/resolver.h>
34 #include <connman/log.h>
35
36 #include <glib.h>
37
38 struct server_data {
39         char *interface;
40         char *server;
41         GIOChannel *channel;
42         guint watch;
43 };
44
45 struct request_data {
46         struct sockaddr_in sin;
47         socklen_t len;
48         guint16 id;
49 };
50
51 static GSList *server_list = NULL;
52 static GSList *request_list = NULL;
53
54 static GIOChannel *listener_channel = NULL;
55 static guint listener_watch = 0;
56
57 static struct request_data *find_request(guint16 id)
58 {
59         GSList *list;
60
61         for (list = request_list; list; list = list->next) {
62                 struct request_data *data = list->data;
63
64                 if (data->id == id)
65                         return data;
66         }
67
68         return NULL;
69 }
70
71 static struct server_data *find_server(const char *interface,
72                                                         const char *server)
73 {
74         GSList *list;
75
76         DBG("interface %s server %s", interface, server);
77
78         for (list = server_list; list; list = list->next) {
79                 struct server_data *data = list->data;
80
81                 if (data->interface == NULL || data->server == NULL)
82                         continue;
83
84                 if (g_str_equal(data->interface, interface) == TRUE &&
85                                 g_str_equal(data->server, server) == TRUE)
86                         return data;
87         }
88
89         return NULL;
90 }
91
92 static gboolean server_event(GIOChannel *channel, GIOCondition condition,
93                                                         gpointer user_data)
94 {
95         struct request_data *req;
96         unsigned char buf[768];
97         int sk, err, len;
98
99         sk = g_io_channel_unix_get_fd(channel);
100
101         len = recv(sk, buf, sizeof(buf), 0);
102         if (len < 2)
103                 return TRUE;
104
105         DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
106
107         req = find_request(buf[0] | buf[1] << 8);
108         if (req == NULL)
109                 return TRUE;
110
111         request_list = g_slist_remove(request_list, req);
112
113         sk = g_io_channel_unix_get_fd(listener_channel);
114
115         err = sendto(sk, buf, len, 0, (struct sockaddr *) &req->sin, req->len);
116
117         g_free(req);
118
119         return TRUE;
120 }
121
122 static struct server_data *create_server(const char *interface,
123                                                         const char *server)
124 {
125         struct server_data *data;
126         struct sockaddr_in sin;
127         int sk;
128
129         DBG("interface %s server %s", interface, server);
130
131         sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
132         if (sk < 0) {
133                 connman_error("Failed to create server %s socket", server);
134                 return NULL;
135         }
136
137         if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
138                                 interface, strlen(interface) + 1) < 0) {
139                 connman_error("Failed to bind server %s to interface %s",
140                                                         server, interface);
141                 close(sk);
142                 return NULL;
143         }
144
145         memset(&sin, 0, sizeof(sin));
146         sin.sin_family = AF_INET;
147         sin.sin_port = htons(53);
148         sin.sin_addr.s_addr = inet_addr(server);
149
150         if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
151                 connman_error("Failed to connect server %s", server);
152                 close(sk);
153                 return NULL;
154         }
155
156         data = g_try_new0(struct server_data, 1);
157         if (data == NULL) {
158                 connman_error("Failed to allocate server %s data", server);
159                 close(sk);
160                 return NULL;
161         }
162
163         data->channel = g_io_channel_unix_new(sk);
164         if (data->channel == NULL) {
165                 connman_error("Failed to create server %s channel", server);
166                 close(sk);
167                 g_free(data);
168                 return NULL;
169         }
170
171         g_io_channel_set_close_on_unref(data->channel, TRUE);
172
173         data->watch = g_io_add_watch(data->channel, G_IO_IN,
174                                                         server_event, data);
175
176         data->interface = g_strdup(interface);
177         data->server = g_strdup(server);
178
179         return data;
180 }
181
182 static void destroy_server(struct server_data *data)
183 {
184         DBG("interface %s server %s", data->interface, data->server);
185
186         if (data->watch > 0)
187                 g_source_remove(data->watch);
188
189         g_io_channel_unref(data->channel);
190
191         g_free(data->interface);
192         g_free(data->server);
193         g_free(data);
194 }
195
196 static int dnsproxy_append(const char *interface, const char *domain,
197                                                         const char *server)
198 {
199         struct server_data *data;
200
201         DBG("interface %s server %s", interface, server);
202
203         data = create_server(interface, server);
204         if (data == NULL)
205                 return -EIO;
206
207         server_list = g_slist_append(server_list, data);
208
209         return 0;
210 }
211
212 static int dnsproxy_remove(const char *interface, const char *domain,
213                                                         const char *server)
214 {
215         struct server_data *data;
216
217         DBG("interface %s server %s", interface, server);
218
219         data = find_server(interface, server);
220         if (data == NULL)
221                 return 0;
222
223         server_list = g_slist_remove(server_list, data);
224
225         destroy_server(data);
226
227         return 0;
228 }
229
230 static struct connman_resolver dnsproxy_resolver = {
231         .name           = "dnsproxy",
232         .priority       = CONNMAN_RESOLVER_PRIORITY_HIGH,
233         .append         = dnsproxy_append,
234         .remove         = dnsproxy_remove,
235 };
236
237 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
238                                                         gpointer user_data)
239 {
240         GSList *list;
241         unsigned char buf[768];
242         struct request_data *req;
243         struct sockaddr_in sin;
244         socklen_t size = sizeof(sin);
245         int sk, err, len;
246
247         sk = g_io_channel_unix_get_fd(channel);
248
249         memset(&sin, 0, sizeof(sin));
250         len = recvfrom(sk, buf, sizeof(buf), 0,
251                                         (struct sockaddr *) &sin, &size);
252         if (len < 2)
253                 return TRUE;
254
255         DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
256
257         if (g_slist_length(server_list) == 0)
258                 return TRUE;
259
260         req = find_request(buf[0] | (buf[1] << 8));
261         if (req == NULL) {
262                 req = g_try_new0(struct request_data, 1);
263                 if (req == NULL)
264                         return TRUE;
265
266                 memcpy(&req->sin, &sin, sizeof(sin));
267                 req->len = size;
268                 req->id = buf[0] | (buf[1] << 8);
269
270                 request_list = g_slist_append(request_list, req);
271         } else {
272                 memcpy(&req->sin, &sin, sizeof(sin));
273                 req->len = size;
274         }
275
276         for (list = server_list; list; list = list->next) {
277                 struct server_data *data = list->data;
278
279                 sk = g_io_channel_unix_get_fd(data->channel);
280
281                 err = send(sk, buf, len, 0);
282         }
283
284         return TRUE;
285 }
286
287 static int create_listener(void)
288 {
289         const char *ifname = "lo";
290         struct sockaddr_in sin;
291         int sk;
292
293         DBG("");
294
295         sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
296         if (sk < 0) {
297                 connman_error("Failed to create listener socket");
298                 return -EIO;
299         }
300
301         //setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
302         //setsockopt(sk, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
303
304         if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
305                                         ifname, strlen(ifname) + 1) < 0) {
306                 connman_error("Failed to bind listener interface");
307                 close(sk);
308                 return -EIO;
309         }
310
311         memset(&sin, 0, sizeof(sin));
312         sin.sin_family = AF_INET;
313         sin.sin_port = htons(53);
314         sin.sin_addr.s_addr = inet_addr("127.0.0.1");
315         //sin.sin_addr.s_addr = INADDR_ANY;
316
317         if (bind(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
318                 connman_error("Failed to bind listener socket");
319                 close(sk);
320                 return -EIO;
321         }
322
323         listener_channel = g_io_channel_unix_new(sk);
324         if (listener_channel == NULL) {
325                 connman_error("Failed to create listener channel");
326                 close(sk);
327                 return -EIO;
328         }
329
330         g_io_channel_set_close_on_unref(listener_channel, TRUE);
331
332         listener_watch = g_io_add_watch(listener_channel, G_IO_IN,
333                                                         listener_event, NULL);
334
335         return 0;
336 }
337
338 static void destroy_listener(void)
339 {
340         GSList *list;
341
342         DBG("");
343
344         if (listener_watch > 0)
345                 g_source_remove(listener_watch);
346
347         for (list = request_list; list; list = list->next) {
348                 struct request_data *data = list->data;
349
350                 DBG("Dropping request (id 0x%04x)", data->id);
351
352                 g_free(data);
353                 list->data = NULL;
354         }
355
356         g_slist_free(request_list);
357         request_list = NULL;
358
359         g_io_channel_unref(listener_channel);
360 }
361
362 static int dnsproxy_init(void)
363 {
364         int err;
365
366         err = create_listener();
367         if (err < 0)
368                 return err;
369
370         err = connman_resolver_register(&dnsproxy_resolver);
371         if (err < 0)
372                 destroy_listener();
373
374         return err;
375 }
376
377 static void dnsproxy_exit(void)
378 {
379         destroy_listener();
380
381         connman_resolver_unregister(&dnsproxy_resolver);
382 }
383
384 CONNMAN_PLUGIN_DEFINE(dnsproxy, "DNS proxy resolver plugin", VERSION,
385                                                 dnsproxy_init, dnsproxy_exit)