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