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