-again moving towards DNS API sanity
[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 GNUNET_PACKED;
42   struct query_packet_list *prev GNUNET_PACKED;
43   struct query_packet pkt;
44 };
45
46
47
48 struct GNUNET_DNS_Handle
49 {
50   struct query_packet_list *head;
51   struct query_packet_list *tail;
52   struct GNUNET_CLIENT_Connection *dns_connection;
53   unsigned char restart_hijack;
54
55   struct GNUNET_CLIENT_TransmitHandle *dns_transmit_handle;
56
57   const struct GNUNET_CONFIGURATION_Handle *cfg;
58
59   GNUNET_DNS_ResponseCallback process_answer_cb;
60   
61   void *process_answer_cb_cls;
62 };
63
64
65 /**
66  * Callback called by notify_transmit_ready; sends dns-queries or rehijack-messages
67  * to the service-dns
68  * {{{
69  */
70 size_t
71 send_query (void *cls GNUNET_UNUSED, size_t size, void *buf)
72 {
73   struct GNUNET_DNS_Handle *h = cls;
74
75   size_t len;
76
77   h->dns_transmit_handle = NULL;
78
79   /*
80    * Send the rehijack-message
81    */
82   if (h->restart_hijack == 1)
83   {
84     h->restart_hijack = 0;
85     /*
86      * The message is just a header
87      */
88     GNUNET_assert (sizeof (struct GNUNET_MessageHeader) <= size);
89     struct GNUNET_MessageHeader *hdr = buf;
90
91     len = sizeof (struct GNUNET_MessageHeader);
92     hdr->size = htons (len);
93     hdr->type = htons (GNUNET_MESSAGE_TYPE_REHIJACK);
94   }
95   else if (h->head != NULL)
96   {
97     struct query_packet_list *query = h->head;
98
99     len = ntohs (query->pkt.hdr.size);
100
101     GNUNET_assert (len <= size);
102
103     memcpy (buf, &query->pkt.hdr, len);
104
105     GNUNET_CONTAINER_DLL_remove (h->head, h->tail, query);
106
107     GNUNET_free (query);
108   }
109   else
110   {
111     GNUNET_break (0);
112     len = 0;
113   }
114
115   /*
116    * Check whether more data is to be sent
117    */
118   if (h->head != NULL)
119   {
120     h->dns_transmit_handle =
121       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
122                                            ntohs (h->head->pkt.hdr.size),
123                                              GNUNET_TIME_UNIT_FOREVER_REL,
124                                              GNUNET_YES, &send_query, h);
125   }
126   else if (h->restart_hijack == 1)
127   {
128     h->dns_transmit_handle =
129       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
130                                            sizeof (struct
131                                                      GNUNET_MessageHeader),
132                                              GNUNET_TIME_UNIT_FOREVER_REL,
133                                              GNUNET_YES, &send_query, h);
134   }
135
136   return len;
137 }
138
139 /* }}} */
140
141
142
143 /**
144  * This receives packets from the service-dns and schedules process_answer to
145  * handle it
146  */
147 static void
148 dns_answer_handler (void *cls,
149                     const struct GNUNET_MessageHeader *msg)
150 {
151   struct GNUNET_DNS_Handle *h = cls;
152
153   /* the service disconnected, reconnect after short wait */
154   if (msg == NULL)
155   {
156     if (h->dns_transmit_handle != NULL)
157       GNUNET_CLIENT_notify_transmit_ready_cancel (h->dns_transmit_handle);
158     h->dns_transmit_handle = NULL;
159     GNUNET_CLIENT_disconnect (h->dns_connection, GNUNET_NO);
160     h->dns_connection = NULL;
161 #if 0
162     h->conn_task =
163         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
164                                       &connect_to_service_dns, h);
165 #endif
166     return;
167   }
168
169   /* the service did something strange, reconnect immediately */
170   if (msg->type != htons (GNUNET_MESSAGE_TYPE_VPN_DNS_LOCAL_RESPONSE_DNS))
171   {
172     GNUNET_break (0);
173     GNUNET_CLIENT_disconnect (h->dns_connection, GNUNET_NO);
174     h->dns_connection = NULL;
175 #if 0
176     conn_task = GNUNET_SCHEDULER_add_now (&connect_to_service_dns, NULL);
177 #endif
178     return;
179   }
180   h->process_answer_cb (h->process_answer_cb_cls,
181                         (const struct answer_packet*) msg);
182   GNUNET_CLIENT_receive (h->dns_connection, &dns_answer_handler, h,
183                          GNUNET_TIME_UNIT_FOREVER_REL);
184 }
185
186
187 /**
188  * Connect to the service-dns
189  */
190 struct GNUNET_DNS_Handle *
191 GNUNET_DNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
192                     GNUNET_DNS_ResponseCallback cb,
193                     void *cb_cls)
194 {
195   struct GNUNET_DNS_Handle *h;
196
197   h = GNUNET_malloc (sizeof (struct GNUNET_DNS_Handle));
198   h->cfg = cfg;
199   h->process_answer_cb = cb;
200   h->process_answer_cb_cls = cb_cls;
201   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to service-dns\n");
202   h->dns_connection = GNUNET_CLIENT_connect ("dns", h->cfg);
203   /* This would most likely be a misconfiguration */
204   GNUNET_assert (NULL != h->dns_connection);
205   GNUNET_CLIENT_receive (h->dns_connection, 
206                          &dns_answer_handler, NULL,
207                          GNUNET_TIME_UNIT_FOREVER_REL);
208   /* If a packet is already in the list, schedule to send it */
209   if (h->dns_transmit_handle == NULL && h->head != NULL)
210     h->dns_transmit_handle =
211         GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
212                                              ntohs (h->head->pkt.hdr.size),
213                                              GNUNET_TIME_UNIT_FOREVER_REL,
214                                              GNUNET_YES, &send_query, h);
215   else if (h->dns_transmit_handle == NULL && h->restart_hijack == 1)
216   {
217     h->dns_transmit_handle =
218       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
219                                            sizeof (struct
220                                                      GNUNET_MessageHeader),
221                                              GNUNET_TIME_UNIT_FOREVER_REL,
222                                              GNUNET_YES, &send_query, NULL);
223   }
224   return h;
225 }
226
227
228 void
229 GNUNET_DNS_restart_hijack (struct GNUNET_DNS_Handle *h)
230 {
231   h->restart_hijack = 1;
232   if (NULL != h->dns_connection && h->dns_transmit_handle == NULL)
233     h->dns_transmit_handle =
234       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection,
235                                              sizeof (struct
236                                                      GNUNET_MessageHeader),
237                                              GNUNET_TIME_UNIT_FOREVER_REL,
238                                              GNUNET_YES, &send_query, h);
239 }
240
241
242 /**
243  * FIXME: we should not expost our internal structures like this.
244  * Just a quick initial hack.
245  */
246 static void
247 queue_request (struct GNUNET_DNS_Handle *h,
248                struct query_packet_list *q)
249 {
250   GNUNET_CONTAINER_DLL_insert_tail (h->head, h->tail, q);
251   if (h->dns_connection != NULL && h->dns_transmit_handle == NULL)
252     h->dns_transmit_handle =
253       GNUNET_CLIENT_notify_transmit_ready (h->dns_connection, ntohs(q->pkt.hdr.size),
254                                            GNUNET_TIME_UNIT_FOREVER_REL,
255                                            GNUNET_YES, &send_query,
256                                            h);
257 }
258
259
260
261 /**
262  * Process a DNS request sent to an IPv4 resolver.  Pass it
263  * to the DNS service for resolution.
264  *
265  * @param h DNS handle
266  * @param dst_ip destination IPv4 address
267  * @param src_ip source IPv4 address (usually local machine)
268  * @param src_port source port (to be used for reply)
269  * @param udp_packet_len length of the UDP payload in bytes
270  * @param udp_packet UDP payload
271  */
272 void
273 GNUNET_DNS_queue_request_v4 (struct GNUNET_DNS_Handle *h,
274                              const struct in_addr *dst_ip,
275                              const struct in_addr *src_ip,
276                              uint16_t src_port,
277                              size_t udp_packet_len,
278                              const char *udp_packet)
279 {
280   size_t len = sizeof (struct query_packet) + udp_packet_len - 1;
281   struct query_packet_list *query =
282     GNUNET_malloc (len + sizeof (struct answer_packet_list) -
283                    sizeof (struct answer_packet));
284   query->pkt.hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_LOCAL_QUERY_DNS);
285   query->pkt.hdr.size = htons (len);
286   memcpy (query->pkt.orig_to, dst_ip, 4);
287   memcpy (query->pkt.orig_from, src_ip, 4);
288   query->pkt.addrlen = 4;
289   query->pkt.src_port = htons (src_port);
290   memcpy (query->pkt.data, udp_packet, udp_packet_len);  
291   queue_request (h, query);
292 }
293
294
295 /**
296  * Process a DNS request sent to an IPv6 resolver.  Pass it
297  * to the DNS service for resolution.
298  *
299  * @param h DNS handle
300  * @param dst_ip destination IPv6 address
301  * @param src_ip source IPv6 address (usually local machine)
302  * @param src_port source port (to be used for reply)
303  * @param udp_packet_len length of the UDP payload in bytes
304  * @param udp_packet UDP payload
305  */
306 void
307 GNUNET_DNS_queue_request_v6 (struct GNUNET_DNS_Handle *h,
308                              const struct in6_addr *dst_ip,
309                              const struct in6_addr *src_ip,
310                              uint16_t src_port,
311                              size_t udp_packet_len,
312                              const char *udp_packet)
313 {
314   size_t len =
315     sizeof (struct query_packet) + udp_packet_len - 1;
316   struct query_packet_list *query =
317     GNUNET_malloc (len + sizeof (struct answer_packet_list) -
318                    sizeof (struct answer_packet));
319   query->pkt.hdr.type =
320     htons (GNUNET_MESSAGE_TYPE_VPN_DNS_LOCAL_QUERY_DNS);
321   query->pkt.hdr.size = htons (len);
322   memcpy (query->pkt.orig_to, dst_ip, 16);
323   memcpy (query->pkt.orig_from, src_ip, 16);
324   query->pkt.addrlen = 16;
325   query->pkt.src_port = htons (src_port);
326   memcpy (query->pkt.data, udp_packet,
327           udp_packet_len);
328   queue_request (h, query);
329 }
330
331
332 void
333 GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *h)
334 {
335   if (h->dns_connection != NULL)
336   {
337     GNUNET_CLIENT_disconnect (h->dns_connection, GNUNET_NO);
338     h->dns_connection = NULL;
339   }
340   GNUNET_free (h);
341 }
342
343 /* end of dns_api.c */