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