using DNS2GNS record type instead of recycling NS record type in GNS; fixing testcase...
[oweals/gnunet.git] / src / gns / gnunet-service-gns_interceptor.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009-2013 Christian Grothoff (and other contributing authors)
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  * @file gns/gnunet-service-gns_interceptor.c
22  * @brief GNUnet GNS interceptor logic
23  * @author Martin Schanzenbach
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dns_service.h"
29 #include "gnunet_dnsparser_lib.h"
30 #include "gnunet-service-gns_resolver.h"
31 #include "gnunet-service-gns_interceptor.h"
32 #include "gns.h"
33
34
35 /**
36  * Handle to a DNS intercepted
37  * reslution request
38  */
39 struct InterceptLookupHandle
40 {
41
42   /**
43    * We keep these in a DLL.
44    */
45   struct InterceptLookupHandle *next;
46
47   /**
48    * We keep these in a DLL.
49    */
50   struct InterceptLookupHandle *prev;
51
52   /**
53    * the request handle to reply to 
54    */
55   struct GNUNET_DNS_RequestHandle *request_handle;
56   
57   /**
58    * the dns parser packet received 
59    */
60   struct GNUNET_DNSPARSER_Packet *packet;
61
62   /**
63    * Handle for the lookup operation.
64    */
65   struct GNS_ResolverHandle *lookup;
66
67 };
68
69
70 /**
71  * Our handle to the DNS handler library
72  */
73 static struct GNUNET_DNS_Handle *dns_handle;
74
75 /**
76  * Key of the zone we start lookups in.
77  */
78 static struct GNUNET_CRYPTO_EccPublicSignKey zone;
79
80 /**
81  * Head of the DLL.
82  */
83 static struct InterceptLookupHandle *ilh_head;
84
85 /**
86  * Tail of the DLL.
87  */
88 static struct InterceptLookupHandle *ilh_tail;
89
90
91 /**
92  * Reply to dns request with the result from our lookup.
93  *
94  * @param cls the closure to the request (an InterceptLookupHandle)
95  * @param rd_count the number of records to return
96  * @param rd the record data
97  */
98 static void
99 reply_to_dns (void *cls, uint32_t rd_count,
100               const struct GNUNET_NAMESTORE_RecordData *rd)
101 {
102   struct InterceptLookupHandle *ilh = cls;
103   struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
104   struct GNUNET_DNSPARSER_Query *query = &packet->queries[0];
105   uint32_t i;
106   size_t len;
107   int ret;
108   char *buf;
109   unsigned int num_answers;
110     
111   /* Put records in the DNS packet */
112   num_answers = 0;
113   for (i=0; i < rd_count; i++)
114     if (rd[i].record_type == query->type)
115       num_answers++;
116
117   {
118     struct GNUNET_DNSPARSER_Record answer_records[num_answers];
119     struct GNUNET_DNSPARSER_Record additional_records[rd_count - num_answers];
120
121     packet->answers = answer_records;
122     packet->additional_records = additional_records;
123
124     /* FIXME: need to handle #GNUNET_NAMESTORE_RF_SHADOW_RECORD option
125        (by ignoring records where this flag is set if there is any
126        other record of that type in the result set) */
127     for (i=0; i < rd_count; i++)
128     {
129       if (rd[i].record_type == query->type)
130       {
131         answer_records[i].name = query->name;
132         answer_records[i].type = rd[i].record_type;
133         switch(rd[i].record_type)
134         {
135         case GNUNET_DNSPARSER_TYPE_NS:
136         case GNUNET_DNSPARSER_TYPE_CNAME:
137         case GNUNET_DNSPARSER_TYPE_PTR:
138           // FIXME: NO! need to use DNSPARSER!
139           answer_records[i].data.hostname = (char*)rd[i].data;
140           break;
141         case GNUNET_DNSPARSER_TYPE_SOA:
142           // FIXME: NO! need to use DNSPARSER!
143           answer_records[i].data.soa =
144             (struct GNUNET_DNSPARSER_SoaRecord *)rd[i].data;
145           break;
146         case GNUNET_DNSPARSER_TYPE_MX:
147           // FIXME: NO! need to use DNSPARSER!
148           answer_records[i].data.mx =
149             (struct GNUNET_DNSPARSER_MxRecord *)rd[i].data;
150           break;
151         default:
152           answer_records[i].data.raw.data_len = rd[i].data_size;
153           answer_records[i].data.raw.data = (char*)rd[i].data;
154           break;
155         }
156         GNUNET_break (0 == (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION));
157         answer_records[i].expiration_time.abs_value_us = rd[i].expiration_time;
158         answer_records[i].class = GNUNET_TUN_DNS_CLASS_INTERNET;
159       }
160       else
161       {
162         additional_records[i].name = query->name;
163         additional_records[i].type = rd[i].record_type;
164         switch(rd[i].record_type)
165         {
166         case GNUNET_DNSPARSER_TYPE_NS:
167         case GNUNET_DNSPARSER_TYPE_CNAME:
168         case GNUNET_DNSPARSER_TYPE_PTR:
169           // FIXME: NO! need to use DNSPARSER!
170           additional_records[i].data.hostname = (char*)rd[i].data;
171           break;
172         case GNUNET_DNSPARSER_TYPE_SOA:
173           // FIXME: NO! need to use DNSPARSER!
174           additional_records[i].data.soa =
175             (struct GNUNET_DNSPARSER_SoaRecord *)rd[i].data;
176           break;
177         case GNUNET_DNSPARSER_TYPE_MX:
178           // FIXME: NO! need to use DNSPARSER!
179           additional_records[i].data.mx =
180             (struct GNUNET_DNSPARSER_MxRecord *)rd[i].data;
181           break;
182         default:
183           // FIXME: NO! need to use DNSPARSER!
184           additional_records[i].data.raw.data_len = rd[i].data_size;
185           additional_records[i].data.raw.data = (char*)rd[i].data;
186           break;
187         }
188         GNUNET_break (0 == (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION));
189         additional_records[i].expiration_time.abs_value_us = rd[i].expiration_time;
190         additional_records[i].class = GNUNET_TUN_DNS_CLASS_INTERNET; 
191       }
192     }
193     packet->num_answers = num_answers;
194     packet->num_additional_records = rd_count - num_answers;
195     packet->flags.authoritative_answer = 1;
196     if (NULL == rd)
197       packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
198     else
199       packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
200     packet->flags.query_or_response = 1;
201     ret = GNUNET_DNSPARSER_pack (packet,
202                                  1024, /* maximum allowed size for DNS reply */
203                                  &buf,
204                                  &len);
205     if (GNUNET_OK != ret)
206     {
207       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
208                   _("Error converting GNS response to DNS response!\n"));
209     }   
210     else
211     {
212       GNUNET_DNS_request_answer (ilh->request_handle,
213                                  len,
214                                  buf);
215       GNUNET_free (buf);
216     } 
217     packet->num_answers = 0;
218     packet->answers = NULL;
219     packet->num_additional_records = 0;
220     packet->additional_records = NULL;
221     GNUNET_DNSPARSER_free_packet (packet);
222   }
223   GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
224   GNUNET_free (ilh);
225 }
226
227
228 /**
229  * The DNS request handler.  Called for every incoming DNS request.
230  *
231  * @param cls closure, unused
232  * @param rh request handle to user for reply
233  * @param request_length number of bytes in @a request
234  * @param request UDP payload of the DNS request
235  */
236 static void
237 handle_dns_request (void *cls,
238                     struct GNUNET_DNS_RequestHandle *rh,
239                     size_t request_length,
240                     const char *request)
241 {
242   struct GNUNET_DNSPARSER_Packet *p;
243   struct InterceptLookupHandle *ilh;
244
245   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
246               "Hijacked a DNS request. Processing.\n");
247   if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
248   {
249     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
250                 "Received malformed DNS packet, leaving it untouched.\n");
251     GNUNET_DNS_request_forward (rh);
252     GNUNET_DNSPARSER_free_packet (p);
253     return;
254   }
255   
256   /* Check TLD and decide if we or legacy dns is responsible */
257   if (1 != p->num_queries)
258   {
259     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260                 "Not exactly one query in DNS packet. Forwarding untouched.\n");
261     GNUNET_DNS_request_forward (rh);
262     GNUNET_DNSPARSER_free_packet(p);
263     return;
264   }
265
266   /* Check for GNS TLDs. */ 
267   if ( (GNUNET_YES == is_gnu_tld (p->queries[0].name)) ||
268        (GNUNET_YES == is_zkey_tld (p->queries[0].name)) ||
269        (0 == strcmp (p->queries[0].name, GNUNET_GNS_TLD)) )
270   {
271     /* Start resolution in GNS */
272     ilh = GNUNET_new (struct InterceptLookupHandle);
273     GNUNET_CONTAINER_DLL_insert (ilh_head, ilh_tail, ilh);
274     ilh->packet = p;
275     ilh->request_handle = rh;
276     ilh->lookup = GNS_resolver_lookup (&zone, 
277                                        p->queries[0].type, 
278                                        p->queries[0].name,
279                                        NULL /* FIXME: enable shorten for DNS intercepts? */,
280                                        GNUNET_NO,
281                                        &reply_to_dns, ilh);
282     return;
283   }
284   /* This request does not concern us. Forward to real DNS. */
285   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
286               "Request for `%s' is forwarded to DNS untouched.\n", 
287               p->queries[0].name);
288   GNUNET_DNS_request_forward (rh);
289   GNUNET_DNSPARSER_free_packet (p);
290 }
291
292
293 /**
294  * Initialized the interceptor
295  *
296  * @param gnu_zone the zone to work in
297  * @param c the configuration
298  * @return #GNUNET_OK on success
299  */
300 int
301 GNS_interceptor_init (const struct GNUNET_CRYPTO_EccPublicSignKey *gnu_zone,
302                       const struct GNUNET_CONFIGURATION_Handle *c)
303 {
304   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
305               "DNS hijacking enabled. Connecting to DNS service.\n");
306   zone = *gnu_zone;
307   dns_handle = GNUNET_DNS_connect (c,
308                                    GNUNET_DNS_FLAG_PRE_RESOLUTION,
309                                    &handle_dns_request,
310                                    NULL);
311   if (NULL == dns_handle)
312   {
313     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
314                 _("Failed to connect to the DNS service!\n"));
315     return GNUNET_SYSERR;
316   }
317   return GNUNET_YES;
318 }
319
320
321 /**
322  * Disconnect from interceptor
323  */
324 void
325 GNS_interceptor_done ()
326 {
327   struct InterceptLookupHandle *ilh;
328
329   while (NULL != (ilh = ilh_head))
330   {
331     GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
332     GNS_resolver_lookup_cancel (ilh->lookup);
333     GNUNET_DNS_request_drop (ilh->request_handle);
334     GNUNET_DNSPARSER_free_packet (ilh->packet);
335     GNUNET_free (ilh);
336   }
337   if (NULL != dns_handle)
338   {
339     GNUNET_DNS_disconnect (dns_handle);
340     dns_handle = NULL;
341   }
342 }
343
344 /* end of gnunet-service-gns_interceptor.c */