66d608c35c812047ebb632d2c13bb237952bf95c
[connman] / 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 <stdint.h>
30 #include <arpa/inet.h>
31 #include <netinet/in.h>
32
33 #define CONNMAN_API_SUBJECT_TO_CHANGE
34 #include <connman/plugin.h>
35 #include <connman/resolver.h>
36 #include <connman/log.h>
37
38 #include <glib.h>
39
40 struct server_data {
41         char *interface;
42         char *server;
43         GIOChannel *channel;
44         guint watch;
45 };
46
47 struct request_data {
48         struct sockaddr_in sin;
49         socklen_t len;
50         guint16 id;
51 };
52
53 static GSList *server_list = NULL;
54 static GSList *request_list = NULL;
55
56 static GIOChannel *listener_channel = NULL;
57 static guint listener_watch = 0;
58
59 static struct request_data *find_request(guint16 id)
60 {
61         GSList *list;
62
63         for (list = request_list; list; list = list->next) {
64                 struct request_data *data = list->data;
65
66                 if (data->id == id)
67                         return data;
68         }
69
70         return NULL;
71 }
72
73 static struct server_data *find_server(const char *interface,
74                                                         const char *server)
75 {
76         GSList *list;
77
78         DBG("interface %s server %s", interface, server);
79
80         for (list = server_list; list; list = list->next) {
81                 struct server_data *data = list->data;
82
83                 if (data->interface == NULL || data->server == NULL)
84                         continue;
85
86                 if (g_str_equal(data->interface, interface) == TRUE &&
87                                 g_str_equal(data->server, server) == TRUE)
88                         return data;
89         }
90
91         return NULL;
92 }
93
94 static gboolean server_event(GIOChannel *channel, GIOCondition condition,
95                                                         gpointer user_data)
96 {
97         struct request_data *req;
98         unsigned char buf[768];
99         int sk, err, len;
100
101         sk = g_io_channel_unix_get_fd(channel);
102
103         len = recv(sk, buf, sizeof(buf), 0);
104         if (len < 2)
105                 return TRUE;
106
107         DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
108
109         req = find_request(buf[0] | buf[1] << 8);
110         if (req == NULL)
111                 return TRUE;
112
113         request_list = g_slist_remove(request_list, req);
114
115         sk = g_io_channel_unix_get_fd(listener_channel);
116
117         err = sendto(sk, buf, len, 0, (struct sockaddr *) &req->sin, req->len);
118
119         g_free(req);
120
121         return TRUE;
122 }
123
124 static struct server_data *create_server(const char *interface,
125                                                         const char *server)
126 {
127         struct server_data *data;
128         struct sockaddr_in sin;
129         int sk;
130
131         DBG("interface %s server %s", interface, server);
132
133         sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
134         if (sk < 0) {
135                 connman_error("Failed to create server %s socket", server);
136                 return NULL;
137         }
138
139         if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
140                                 interface, strlen(interface) + 1) < 0) {
141                 connman_error("Failed to bind server %s to interface %s",
142                                                         server, interface);
143                 close(sk);
144                 return NULL;
145         }
146
147         memset(&sin, 0, sizeof(sin));
148         sin.sin_family = AF_INET;
149         sin.sin_port = htons(53);
150         sin.sin_addr.s_addr = inet_addr(server);
151
152         if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
153                 connman_error("Failed to connect server %s", server);
154                 close(sk);
155                 return NULL;
156         }
157
158         data = g_try_new0(struct server_data, 1);
159         if (data == NULL) {
160                 connman_error("Failed to allocate server %s data", server);
161                 close(sk);
162                 return NULL;
163         }
164
165         data->channel = g_io_channel_unix_new(sk);
166         if (data->channel == NULL) {
167                 connman_error("Failed to create server %s channel", server);
168                 close(sk);
169                 g_free(data);
170                 return NULL;
171         }
172
173         g_io_channel_set_close_on_unref(data->channel, TRUE);
174
175         data->watch = g_io_add_watch(data->channel, G_IO_IN,
176                                                         server_event, data);
177
178         data->interface = g_strdup(interface);
179         data->server = g_strdup(server);
180
181         return data;
182 }
183
184 static void destroy_server(struct server_data *data)
185 {
186         DBG("interface %s server %s", data->interface, data->server);
187
188         if (data->watch > 0)
189                 g_source_remove(data->watch);
190
191         g_io_channel_unref(data->channel);
192
193         g_free(data->interface);
194         g_free(data->server);
195         g_free(data);
196 }
197
198 static int dnsproxy_append(const char *interface, const char *domain,
199                                                         const char *server)
200 {
201         struct server_data *data;
202
203         DBG("interface %s server %s", interface, server);
204
205         data = create_server(interface, server);
206         if (data == NULL)
207                 return -EIO;
208
209         server_list = g_slist_append(server_list, data);
210
211         return 0;
212 }
213
214 static int dnsproxy_remove(const char *interface, const char *domain,
215                                                         const char *server)
216 {
217         struct server_data *data;
218
219         DBG("interface %s server %s", interface, server);
220
221         data = find_server(interface, server);
222         if (data == NULL)
223                 return 0;
224
225         server_list = g_slist_remove(server_list, data);
226
227         destroy_server(data);
228
229         return 0;
230 }
231
232 static struct connman_resolver dnsproxy_resolver = {
233         .name           = "dnsproxy",
234         .priority       = CONNMAN_RESOLVER_PRIORITY_HIGH,
235         .append         = dnsproxy_append,
236         .remove         = dnsproxy_remove,
237 };
238
239 #if __BYTE_ORDER == __LITTLE_ENDIAN
240 struct domain_hdr {
241         uint16_t id;
242         uint8_t rd:1;
243         uint8_t tc:1;
244         uint8_t aa:1;
245         uint8_t opcode:4;
246         uint8_t qr:1;
247         uint8_t rcode:4;
248         uint8_t z:3;
249         uint8_t ra:1;
250         uint16_t qdcount;
251         uint16_t ancount;
252         uint16_t nscount;
253         uint16_t arcount;
254 } __attribute__ ((packed));
255 #elif __BYTE_ORDER == __BIG_ENDIAN
256 struct domain_hdr {
257         uint16_t id;
258         uint8_t qr:1;
259         uint8_t opcode:4;
260         uint8_t aa:1;
261         uint8_t tc:1;
262         uint8_t rd:1;
263         uint8_t ra:1;
264         uint8_t z:3;
265         uint8_t rcode:4;
266         uint16_t qdcount;
267         uint16_t ancount;
268         uint16_t nscount;
269         uint16_t arcount;
270 } __attribute__ ((packed));
271 #else
272 #error "Unknown byte order"
273 #endif
274
275 static void parse_request(unsigned char *buf, int len)
276 {
277         struct domain_hdr *hdr = (void *) buf;
278         uint16_t qdcount = ntohs(hdr->qdcount);
279         unsigned char *ptr;
280         char name[512];
281         unsigned int remain, used = 0;
282
283         if (len < 12)
284                 return;
285
286         DBG("id 0x%04x qr %d opcode %d qdcount %d",
287                                 hdr->id, hdr->qr, hdr->opcode, qdcount);
288
289         if (hdr->qr != 0 || qdcount != 1)
290                 return;
291
292         memset(name, 0, sizeof(name));
293
294         ptr = buf + 12;
295         remain = len - 12;
296
297         while (remain > 0) {
298                 uint8_t len = *ptr;
299
300                 if (len == 0x00)
301                         break;
302
303                 if (used + len + 1 > sizeof(name))
304                         return;
305
306                 strncat(name, (char *) (ptr + 1), len);
307                 strcat(name, ".");
308
309                 used += len + 1;
310
311                 ptr += len + 1;
312                 remain -= len + 1;
313         }
314
315         DBG("domain name %s", name);
316 }
317
318 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
319                                                         gpointer user_data)
320 {
321         GSList *list;
322         unsigned char buf[768];
323         struct request_data *req;
324         struct sockaddr_in sin;
325         socklen_t size = sizeof(sin);
326         int sk, err, len;
327
328         sk = g_io_channel_unix_get_fd(channel);
329
330         memset(&sin, 0, sizeof(sin));
331         len = recvfrom(sk, buf, sizeof(buf), 0,
332                                         (struct sockaddr *) &sin, &size);
333         if (len < 2)
334                 return TRUE;
335
336         DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
337
338         parse_request(buf, len);
339
340         if (g_slist_length(server_list) == 0)
341                 return TRUE;
342
343         req = find_request(buf[0] | (buf[1] << 8));
344         if (req == NULL) {
345                 req = g_try_new0(struct request_data, 1);
346                 if (req == NULL)
347                         return TRUE;
348
349                 memcpy(&req->sin, &sin, sizeof(sin));
350                 req->len = size;
351                 req->id = buf[0] | (buf[1] << 8);
352
353                 request_list = g_slist_append(request_list, req);
354         } else {
355                 memcpy(&req->sin, &sin, sizeof(sin));
356                 req->len = size;
357         }
358
359         for (list = server_list; list; list = list->next) {
360                 struct server_data *data = list->data;
361
362                 sk = g_io_channel_unix_get_fd(data->channel);
363
364                 err = send(sk, buf, len, 0);
365         }
366
367         return TRUE;
368 }
369
370 static int create_listener(void)
371 {
372         const char *ifname = "lo";
373         struct sockaddr_in sin;
374         int sk;
375
376         DBG("");
377
378         sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
379         if (sk < 0) {
380                 connman_error("Failed to create listener socket");
381                 return -EIO;
382         }
383
384         //setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
385         //setsockopt(sk, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
386
387         if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
388                                         ifname, strlen(ifname) + 1) < 0) {
389                 connman_error("Failed to bind listener interface");
390                 close(sk);
391                 return -EIO;
392         }
393
394         memset(&sin, 0, sizeof(sin));
395         sin.sin_family = AF_INET;
396         sin.sin_port = htons(53);
397         sin.sin_addr.s_addr = inet_addr("127.0.0.1");
398         //sin.sin_addr.s_addr = INADDR_ANY;
399
400         if (bind(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
401                 connman_error("Failed to bind listener socket");
402                 close(sk);
403                 return -EIO;
404         }
405
406         listener_channel = g_io_channel_unix_new(sk);
407         if (listener_channel == NULL) {
408                 connman_error("Failed to create listener channel");
409                 close(sk);
410                 return -EIO;
411         }
412
413         g_io_channel_set_close_on_unref(listener_channel, TRUE);
414
415         listener_watch = g_io_add_watch(listener_channel, G_IO_IN,
416                                                         listener_event, NULL);
417
418         return 0;
419 }
420
421 static void destroy_listener(void)
422 {
423         GSList *list;
424
425         DBG("");
426
427         if (listener_watch > 0)
428                 g_source_remove(listener_watch);
429
430         for (list = request_list; list; list = list->next) {
431                 struct request_data *data = list->data;
432
433                 DBG("Dropping request (id 0x%04x)", data->id);
434
435                 g_free(data);
436                 list->data = NULL;
437         }
438
439         g_slist_free(request_list);
440         request_list = NULL;
441
442         g_io_channel_unref(listener_channel);
443 }
444
445 static int dnsproxy_init(void)
446 {
447         int err;
448
449         err = create_listener();
450         if (err < 0)
451                 return err;
452
453         err = connman_resolver_register(&dnsproxy_resolver);
454         if (err < 0)
455                 destroy_listener();
456
457         return err;
458 }
459
460 static void dnsproxy_exit(void)
461 {
462         destroy_listener();
463
464         connman_resolver_unregister(&dnsproxy_resolver);
465 }
466
467 CONNMAN_PLUGIN_DEFINE(dnsproxy, "DNS proxy resolver plugin", VERSION,
468                  CONNMAN_PLUGIN_PRIORITY_DEFAULT, dnsproxy_init, dnsproxy_exit)