-fix no-dot-in-path issue on FreeBSD bot
[oweals/gnunet.git] / src / gns / gnunet-service-gns_interceptor.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011, 2012 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_transport_service.h"
28 #include "gnunet_dns_service.h"
29 #include "gnunet_dnsparser_lib.h"
30 #include "gnunet-service-gns_resolver.h"
31 #include "gns.h"
32
33 /**
34  * Handle to a DNS intercepted
35  * reslution request
36  */
37 struct InterceptLookupHandle
38 {
39   /**
40    * the request handle to reply to 
41    */
42   struct GNUNET_DNS_RequestHandle *request_handle;
43   
44   /**
45    * the dns parser packet received 
46    */
47   struct GNUNET_DNSPARSER_Packet *packet;
48   
49   /**
50    * the query parsed from the packet 
51    */
52   struct GNUNET_DNSPARSER_Query *query;
53 };
54
55
56 /**
57  * Our handle to the DNS handler library
58  */
59 static struct GNUNET_DNS_Handle *dns_handle;
60
61 /**
62  * The root zone for this interceptor
63  */
64 static struct GNUNET_CRYPTO_ShortHashCode our_zone;
65
66 /**
67  * Our priv key
68  */
69 static struct GNUNET_CRYPTO_RsaPrivateKey *our_key;
70
71 /**
72  * Default timeout
73  */
74 static struct GNUNET_TIME_Relative default_lookup_timeout;
75
76
77 /**
78  * Reply to dns request with the result from our lookup.
79  *
80  * @param cls the closure to the request (an InterceptLookupHandle)
81  * @param rd_count the number of records to return
82  * @param rd the record data
83  */
84 static void
85 reply_to_dns (void* cls, uint32_t rd_count,
86               const struct GNUNET_NAMESTORE_RecordData *rd)
87 {
88   uint32_t i;
89   size_t len;
90   int ret;
91   char *buf;
92   struct InterceptLookupHandle* ilh = (struct InterceptLookupHandle*)cls;
93   struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
94   unsigned int num_answers = 0;
95   
96   
97   /**
98    * Put records in the DNS packet and modify it
99    * to a response
100    */
101   for (i=0; i < rd_count; i++)
102   {
103     if (rd[i].record_type == ilh->query->type)
104       num_answers++;
105   }
106
107   struct GNUNET_DNSPARSER_Record answer_records[num_answers];
108   struct GNUNET_DNSPARSER_Record additional_records[rd_count-(num_answers)];
109   packet->answers = answer_records;
110   packet->additional_records = additional_records;
111
112   for (i=0; i < rd_count; i++)
113   {
114     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
115                "Adding type %d to DNS response\n", rd[i].record_type);
116     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Name: %s\n", ilh->query->name);
117     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Record %d/%d\n", i+1, rd_count);
118     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Record len %d\n", rd[i].data_size);
119     
120     if (rd[i].record_type == ilh->query->type)
121     {
122       answer_records[i].name = ilh->query->name;
123       answer_records[i].type = rd[i].record_type;
124       switch(rd[i].record_type)
125       {
126        case GNUNET_GNS_RECORD_NS:
127        case GNUNET_GNS_RECORD_CNAME:
128        case GNUNET_GNS_RECORD_PTR:
129          answer_records[i].data.hostname = (char*)rd[i].data;
130          break;
131        case GNUNET_GNS_RECORD_SOA:
132          answer_records[i].data.soa =
133            (struct GNUNET_DNSPARSER_SoaRecord *)rd[i].data;
134          break;
135        case GNUNET_GNS_RECORD_MX:
136          answer_records[i].data.mx =
137            (struct GNUNET_DNSPARSER_MxRecord *)rd[i].data;
138          break;
139        default:
140         answer_records[i].data.raw.data_len = rd[i].data_size;
141         answer_records[i].data.raw.data = (char*)rd[i].data;
142       }
143       GNUNET_break (0 == (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION));
144       answer_records[i].expiration_time.abs_value = rd[i].expiration_time;
145       answer_records[i].class = GNUNET_DNSPARSER_CLASS_INTERNET;//hmmn
146     }
147     else
148     {
149       additional_records[i].name = ilh->query->name;
150       additional_records[i].type = rd[i].record_type;
151       switch(rd[i].record_type)
152       {
153        case GNUNET_GNS_RECORD_NS:
154        case GNUNET_GNS_RECORD_CNAME:
155        case GNUNET_GNS_RECORD_PTR:
156          additional_records[i].data.hostname = (char*)rd[i].data;
157          break;
158        case GNUNET_GNS_RECORD_SOA:
159          additional_records[i].data.soa =
160            (struct GNUNET_DNSPARSER_SoaRecord *)rd[i].data;
161          break;
162        case GNUNET_GNS_RECORD_MX:
163          additional_records[i].data.mx =
164            (struct GNUNET_DNSPARSER_MxRecord *)rd[i].data;
165          break;
166        default:
167         additional_records[i].data.raw.data_len = rd[i].data_size;
168         additional_records[i].data.raw.data = (char*)rd[i].data;
169       }
170       GNUNET_break (0 == (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION));
171       additional_records[i].expiration_time.abs_value = rd[i].expiration_time;
172       additional_records[i].class = GNUNET_DNSPARSER_CLASS_INTERNET;//hmmn
173     }
174   }
175   
176   packet->num_answers = num_answers;
177   packet->num_additional_records = rd_count-(num_answers);
178   
179   packet->flags.authoritative_answer = 1;
180
181   if (rd == NULL)
182     packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NAME_ERROR;
183   else
184     packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NO_ERROR;
185   
186   packet->flags.query_or_response = 1;
187
188   
189   /**
190    * Reply to DNS
191    */
192   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
193              "Building DNS response\n");
194   ret = GNUNET_DNSPARSER_pack (packet,
195                                1024, /* FIXME magic from dns redirector */
196                                &buf,
197                                &len);
198   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
199               "Built DNS response! (ret=%d,len=%d)\n",
200               ret, len);
201   if (ret == GNUNET_OK)
202   {
203     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
204                 "Answering DNS request\n");
205     GNUNET_DNS_request_answer (ilh->request_handle,
206                                len,
207                                buf);
208
209     GNUNET_free (buf);
210     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211                 "Answered DNS request\n");
212   }
213   else
214   {
215     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
216                 "Error building DNS response! (ret=%d)", ret);
217   }
218   
219   packet->num_answers = 0;
220   packet->answers = NULL;
221   packet->num_additional_records = 0;
222   packet->additional_records = NULL;
223   GNUNET_DNSPARSER_free_packet(packet);
224   GNUNET_free(ilh);
225 }
226
227
228 /**
229  * Entry point for name resolution
230  * Setup a new query and try to resolve
231  *
232  * @param request the request handle of the DNS request from a client
233  * @param p the DNS query packet we received
234  * @param q the DNS query we received parsed from p
235  */
236 static void
237 start_resolution_for_dns (struct GNUNET_DNS_RequestHandle *request,
238                           struct GNUNET_DNSPARSER_Packet *p,
239                           struct GNUNET_DNSPARSER_Query *q)
240 {
241   struct InterceptLookupHandle* ilh;
242   
243   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
244               "Starting resolution for %s (type=%d)!\n",
245               q->name, q->type);
246   ilh = GNUNET_malloc(sizeof(struct InterceptLookupHandle));
247   ilh->packet = p;
248   ilh->query = q;
249   ilh->request_handle = request;
250   
251   /* Start resolution in our zone */
252   gns_resolver_lookup_record(our_zone, our_zone, q->type, q->name,
253                              our_key,
254                              default_lookup_timeout,
255                              GNUNET_NO,
256                              &reply_to_dns, ilh);
257 }
258
259
260 /**
261  * The DNS request handler
262  * Called for every incoming DNS request.
263  *
264  * @param cls closure
265  * @param rh request handle to user for reply
266  * @param request_length number of bytes in request
267  * @param request udp payload of the DNS request
268  */
269 static void
270 handle_dns_request (void *cls,
271                     struct GNUNET_DNS_RequestHandle *rh,
272                     size_t request_length,
273                     const char *request)
274 {
275   struct GNUNET_DNSPARSER_Packet *p;
276
277   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
278               "Hijacked a DNS request...processing\n");
279   if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
280   {
281     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
282                 "Received malformed DNS packet, leaving it untouched\n");
283     GNUNET_DNS_request_forward (rh);
284     GNUNET_DNSPARSER_free_packet (p);
285     return;
286   }
287   
288   /**
289    * Check tld and decide if we or
290    * legacy dns is responsible
291    *
292    * FIXME now in theory there could be more than 1 query in the request
293    * but if this is case we get into trouble:
294    * either we query the GNS or the DNS. We cannot do both!
295    * So I suggest to either only allow a single query per request or
296    * only allow GNS or DNS requests.
297    * The way it is implemented here now is buggy and will lead to erratic
298    * behaviour (if multiple queries are present).
299    */
300   if (0 == p->num_queries)
301   {
302     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303                 "No Queries in DNS packet... forwarding\n");
304     GNUNET_DNS_request_forward (rh);
305     GNUNET_DNSPARSER_free_packet(p);
306     return;
307   }
308
309   /**
310    * Check for .gads/.zkey
311    */
312   
313   if ((is_gads_tld(p->queries[0].name) == GNUNET_YES) ||
314       (is_zkey_tld(p->queries[0].name) == GNUNET_YES) ||
315       (strcmp(p->queries[0].name, GNUNET_GNS_TLD) == 0))
316   {
317     if (p->num_queries > 1)
318     {
319       /* Note: We could also look for .gads */
320       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
321                   ">1 queriy in DNS packet... odd. We only process #1\n");
322     }
323     start_resolution_for_dns (rh, p, p->queries);
324     return;
325   }
326   /**
327    * This request does not concern us. Forward to real DNS.
328    */
329   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330               "Request for %s is forwarded to DNS\n", 
331               p->queries[0].name);
332   GNUNET_DNS_request_forward (rh);
333   GNUNET_DNSPARSER_free_packet (p);
334 }
335
336
337 /**
338  * Initialized the interceptor
339  *
340  * @param zone the zone to work in
341  * @param key the prov key of the zone (can be null, needed for caching)
342  * @param c the configuration
343  * @return GNUNET_OK on success
344  */
345 int
346 gns_interceptor_init (struct GNUNET_CRYPTO_ShortHashCode zone,
347                       struct GNUNET_CRYPTO_RsaPrivateKey *key,
348                       const struct GNUNET_CONFIGURATION_Handle *c)
349 {
350   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
351              "DNS hijacking enabled... connecting to service.\n");
352   our_zone = zone;
353   our_key = key;
354   /**
355    * Do gnunet dns init here
356    */
357   dns_handle = GNUNET_DNS_connect (c,
358                                    GNUNET_DNS_FLAG_PRE_RESOLUTION,
359                                    &handle_dns_request, /* rh */
360                                    NULL); /* Closure */
361
362   if (GNUNET_OK !=
363       GNUNET_CONFIGURATION_get_value_time (c, "gns",
364                                            "DEFAULT_LOOKUP_TIMEOUT",
365                                            &default_lookup_timeout))
366     default_lookup_timeout = GNUNET_TIME_UNIT_ZERO;
367   if (NULL == dns_handle)
368   {
369     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
370              "Failed to connect to the dnsservice!\n");
371     return GNUNET_SYSERR;
372   }
373   return GNUNET_YES;
374 }
375
376
377 /**
378  * Disconnect from interceptor
379  */
380 void
381 gns_interceptor_stop ()
382 {
383   if (NULL != dns_handle)
384   {
385     GNUNET_DNS_disconnect(dns_handle);
386     dns_handle = NULL;
387   }
388 }
389
390 /* end of gns_interceptor.c */