b434fc1e7247725158bd401d7791ef06e337f301
[oweals/gnunet.git] / src / dns / dns_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 Christian Grothoff
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file dns/dns_api.c
23  * @brief
24  * @author Philipp Toelke
25  */
26 #include <platform.h>
27 #include <gnunet_common.h>
28 #include <gnunet_client_lib.h>
29 #include <gnunet_os_lib.h>
30 #include <gnunet_mesh_service.h>
31 #include <gnunet_protocols.h>
32 #include <gnunet_server_lib.h>
33 #include <gnunet_container_lib.h>
34 #include <block_dns.h>
35
36 #include "gnunet_dns_service.h"
37 #include "dns.h"
38
39 struct query_packet_list
40 {
41   struct query_packet_list *next;
42   struct query_packet_list *prev;
43   struct query_packet pkt;
44 };
45
46
47 struct GNUNET_DNS_Handle
48 {
49   struct query_packet_list *head;
50   struct query_packet_list *tail;
51   struct GNUNET_CLIENT_Connection *dns_connection;
52   unsigned char restart_hijack;
53
54   struct GNUNET_CLIENT_TransmitHandle *dns_transmit_handle;
55
56   const struct GNUNET_CONFIGURATION_Handle *cfg;
57
58   GNUNET_DNS_ResponseCallback process_answer_cb;
59   
60   void *process_answer_cb_cls;
61 };
62
63
64 /**
65  * Callback called by notify_transmit_ready; sends dns-queries or rehijack-messages
66  * to the service-dns
67  * {{{
68  */
69 size_t
70 send_query (void *cls GNUNET_UNUSED, size_t size, void *buf)
71 {
72   struct GNUNET_DNS_Handle *h = cls;
73
74   size_t len;
75
76   h->dns_transmit_handle = NULL;
77
78   /*
79    * Send the rehijack-message
80    */
81   if (h->restart_hijack == 1)
82   {
83     h->restart_hijack = 0;
84     /*
85      * The message is just a header
86      */
87     GNUNET_assert (sizeof (struct GNUNET_MessageHeader) <= size);
88     struct GNUNET_MessageHeader *hdr = buf;
89
90     len = sizeof (struct GNUNET_MessageHeader);
91     hdr->size = htons (len);
92     hdr->type = htons (GNUNET_MESSAGE_TYPE_REHIJACK);
93   }
94   else if (h->head != NULL)
95   {
96     struct query_packet_list *query = h->head;
97
98     len = ntohs (query->pkt.hdr.size);
99
100     GNUNET_assert (len <= size);
101
102     memcpy (buf, &query->pkt.hdr, len);
103
104     GNUNET_CONTAINER_DLL_remove (h->head, h->tail, query);
105
106     GNUNET_free (query);
107   }
108   else
109   {
110     GNUNET_break (0);
111     len = 0;
112   }
113
114   /*
115    * Check whether more data is to be sent
116    */
117   if (h->head != NULL)
118   {
119     h->dns_transmit_handle =
120       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
121                                            ntohs (h->head->pkt.hdr.size),
122                                              GNUNET_TIME_UNIT_FOREVER_REL,
123                                              GNUNET_YES, &send_query, h);
124   }
125   else if (h->restart_hijack == 1)
126   {
127     h->dns_transmit_handle =
128       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
129                                            sizeof (struct
130                                                      GNUNET_MessageHeader),
131                                              GNUNET_TIME_UNIT_FOREVER_REL,
132                                              GNUNET_YES, &send_query, h);
133   }
134
135   return len;
136 }
137
138 /* }}} */
139
140
141
142 /**
143  * This receives packets from the service-dns and schedules process_answer to
144  * handle it
145  */
146 static void
147 dns_answer_handler (void *cls,
148                     const struct GNUNET_MessageHeader *msg)
149 {
150   struct GNUNET_DNS_Handle *h = cls;
151
152   /* the service disconnected, reconnect after short wait */
153   if (msg == NULL)
154   {
155     if (h->dns_transmit_handle != NULL)
156       GNUNET_CLIENT_notify_transmit_ready_cancel (h->dns_transmit_handle);
157     h->dns_transmit_handle = NULL;
158     GNUNET_CLIENT_disconnect (h->dns_connection, GNUNET_NO);
159     h->dns_connection = NULL;
160 #if 0
161     h->conn_task =
162         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
163                                       &connect_to_service_dns, h);
164 #endif
165     return;
166   }
167
168   /* the service did something strange, reconnect immediately */
169   if (msg->type != htons (GNUNET_MESSAGE_TYPE_VPN_DNS_LOCAL_RESPONSE_DNS))
170   {
171     GNUNET_break (0);
172     GNUNET_CLIENT_disconnect (h->dns_connection, GNUNET_NO);
173     h->dns_connection = NULL;
174 #if 0
175     conn_task = GNUNET_SCHEDULER_add_now (&connect_to_service_dns, NULL);
176 #endif
177     return;
178   }
179   h->process_answer_cb (h->process_answer_cb_cls,
180                         (const struct answer_packet*) msg);
181   GNUNET_CLIENT_receive (h->dns_connection, &dns_answer_handler, h,
182                          GNUNET_TIME_UNIT_FOREVER_REL);
183 }
184
185
186 /**
187  * Connect to the service-dns
188  */
189 struct GNUNET_DNS_Handle *
190 GNUNET_DNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
191                     GNUNET_DNS_ResponseCallback cb,
192                     void *cb_cls)
193 {
194   struct GNUNET_DNS_Handle *h;
195
196   h = GNUNET_malloc (sizeof (struct GNUNET_DNS_Handle));
197   h->cfg = cfg;
198   h->process_answer_cb = cb;
199   h->process_answer_cb_cls = cb_cls;
200   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to service-dns\n");
201   h->dns_connection = GNUNET_CLIENT_connect ("dns", h->cfg);
202   /* This would most likely be a misconfiguration */
203   GNUNET_assert (NULL != h->dns_connection);
204   GNUNET_CLIENT_receive (h->dns_connection, 
205                          &dns_answer_handler, NULL,
206                          GNUNET_TIME_UNIT_FOREVER_REL);
207   /* If a packet is already in the list, schedule to send it */
208   if (h->dns_transmit_handle == NULL && h->head != NULL)
209     h->dns_transmit_handle =
210         GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
211                                              ntohs (h->head->pkt.hdr.size),
212                                              GNUNET_TIME_UNIT_FOREVER_REL,
213                                              GNUNET_YES, &send_query, h);
214   else if (h->dns_transmit_handle == NULL && h->restart_hijack == 1)
215   {
216     h->dns_transmit_handle =
217       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
218                                            sizeof (struct
219                                                      GNUNET_MessageHeader),
220                                              GNUNET_TIME_UNIT_FOREVER_REL,
221                                              GNUNET_YES, &send_query, NULL);
222   }
223   return h;
224 }
225
226
227 void
228 GNUNET_DNS_restart_hijack (struct GNUNET_DNS_Handle *h)
229 {
230   h->restart_hijack = 1;
231   if (NULL != h->dns_connection && h->dns_transmit_handle == NULL)
232     h->dns_transmit_handle =
233       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
234                                              sizeof (struct
235                                                      GNUNET_MessageHeader),
236                                              GNUNET_TIME_UNIT_FOREVER_REL,
237                                              GNUNET_YES, &send_query, h);
238 }
239
240
241 /**
242  * FIXME: we should not expost our internal structures like this.
243  * Just a quick initial hack.
244  */
245 static void
246 queue_request (struct GNUNET_DNS_Handle *h,
247                struct query_packet_list *q)
248 {
249   GNUNET_CONTAINER_DLL_insert_tail (h->head, h->tail, q);
250   if (h->dns_connection != NULL && h->dns_transmit_handle == NULL)
251     h->dns_transmit_handle =
252       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection, ntohs(q->pkt.hdr.size),
253                                            GNUNET_TIME_UNIT_FOREVER_REL,
254                                            GNUNET_YES, &send_query,
255                                            h);
256 }
257
258
259
260 /**
261  * Process a DNS request sent to an IPv4 resolver.  Pass it
262  * to the DNS service for resolution.
263  *
264  * @param h DNS handle
265  * @param dst_ip destination IPv4 address
266  * @param src_ip source IPv4 address (usually local machine)
267  * @param src_port source port (to be used for reply)
268  * @param udp_packet_len length of the UDP payload in bytes
269  * @param udp_packet UDP payload
270  */
271 void
272 GNUNET_DNS_queue_request_v4 (struct GNUNET_DNS_Handle *h,
273                              const struct in_addr *dst_ip,
274                              const struct in_addr *src_ip,
275                              uint16_t src_port,
276                              size_t udp_packet_len,
277                              const char *udp_packet)
278 {
279   size_t len = sizeof (struct query_packet) + udp_packet_len - 1;
280   struct query_packet_list *query =
281     GNUNET_malloc (len + sizeof (struct query_packet_list) -
282                    sizeof (struct query_packet));
283   query->pkt.hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_LOCAL_QUERY_DNS);
284   query->pkt.hdr.size = htons (len);
285   memcpy (query->pkt.orig_to, dst_ip, 4);
286   memcpy (query->pkt.orig_from, src_ip, 4);
287   query->pkt.addrlen = 4;
288   query->pkt.src_port = htons (src_port);
289   memcpy (query->pkt.data, udp_packet, udp_packet_len);  
290   queue_request (h, query);
291 }
292
293
294 /**
295  * Process a DNS request sent to an IPv6 resolver.  Pass it
296  * to the DNS service for resolution.
297  *
298  * @param h DNS handle
299  * @param dst_ip destination IPv6 address
300  * @param src_ip source IPv6 address (usually local machine)
301  * @param src_port source port (to be used for reply)
302  * @param udp_packet_len length of the UDP payload in bytes
303  * @param udp_packet UDP payload
304  */
305 void
306 GNUNET_DNS_queue_request_v6 (struct GNUNET_DNS_Handle *h,
307                              const struct in6_addr *dst_ip,
308                              const struct in6_addr *src_ip,
309                              uint16_t src_port,
310                              size_t udp_packet_len,
311                              const char *udp_packet)
312 {
313   size_t len =
314     sizeof (struct query_packet) + udp_packet_len - 1;
315   struct query_packet_list *query =
316     GNUNET_malloc (len + sizeof (struct query_packet_list) -
317                    sizeof (struct answer_packet));
318   query->pkt.hdr.type =
319     htons (GNUNET_MESSAGE_TYPE_VPN_DNS_LOCAL_QUERY_DNS);
320   query->pkt.hdr.size = htons (len);
321   memcpy (query->pkt.orig_to, dst_ip, 16);
322   memcpy (query->pkt.orig_from, src_ip, 16);
323   query->pkt.addrlen = 16;
324   query->pkt.src_port = htons (src_port);
325   memcpy (query->pkt.data, udp_packet,
326           udp_packet_len);
327   queue_request (h, query);
328 }
329
330
331 void
332 GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *h)
333 {
334   if (h->dns_connection != NULL)
335   {
336     GNUNET_CLIENT_disconnect (h->dns_connection, GNUNET_NO);
337     h->dns_connection = NULL;
338   }
339   GNUNET_free (h);
340 }
341
342 /* end of dns_api.c */