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