-typo
[oweals/gnunet.git] / src / gns / test_gns_ns_lookup.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012, 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/test_gns_ns_lookup.c
22  * @brief base testcase for testing a local GNS record lookup through NS
23  * @author Martin Schanzenbach
24  */
25 #include "platform.h"
26 #include "gnunet_testing_lib.h"
27 #include "gnunet_core_service.h"
28 #include "block_dns.h"
29 #include "gnunet_signatures.h"
30 #include "gnunet_namestore_service.h"
31 #include "gnunet_resolver_service.h"
32 #include "gnunet_dnsparser_lib.h"
33 #include "gnunet_gns_service.h"
34
35 /**
36  * Timeout for entire testcase 
37  */
38 #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 20)
39
40 /**
41  * Name to resolve for testing.  NS record on 'homepage.gads' redirects to
42  * DNS 'TEST_RECORD_NS' domain and thus names should be resolved within
43  * that target domain.
44  */
45 #define TEST_DOMAIN "www.homepage.gads"
46
47 /**
48  * Name to resolve for testing.  NS record on 'homepage.gads' redirects to
49  * DNS 'TEST_RECORD_NS' domain and thus names should be resolved within
50  * that target domain.
51  */
52 #define TEST_DOMAIN_ALT "homepage.gads"
53
54 /**
55  * Name to resolve for testing.  NS record on 'homepage.gads' redirects to
56  * DNS 'TEST_RECORD_NS' domain and thus names should be resolved within
57  * that target domain.
58  */
59 #define TEST_DOMAIN_ALT2 "uk.homepage.gads"
60
61 /**
62  * Expected test value (matching TEST_DOMAIN_ALT2).
63  * Currently 'uk.gnunet.org' / 'stat.wensley.org.uk'.
64  */
65 #define TEST_IP_ALT2 "81.187.252.184"
66
67 /**
68  * Must be the IP address for TEST_RECORD_NS in DNS and TEST_DOMAIN in GADS;
69  * used to check that DNS is working as expected.  We use the IPv4
70  * address of gnunet.org.
71  */
72 #define TEST_IP "131.159.74.67"
73
74 /**
75  * DNS domain name used for testing.
76  */
77 #define TEST_RECORD_NS "gnunet.org"
78
79 /**
80  * Nameserver for 'TEST_RECORD_NS', currently 'a.ns.joker.com'.
81  */
82 #define TEST_IP_NS "184.172.157.218" 
83
84 /**
85  * Name we use within our GADS zone.
86  */
87 #define TEST_RECORD_NAME "homepage"
88
89 /**
90  * Task handle to use to schedule test failure 
91  */
92 static GNUNET_SCHEDULER_TaskIdentifier die_task;
93
94 /**
95  * Global return value (0 for success, anything else for failure) 
96  */
97 static int ok;
98
99 /**
100  * Flag we set if the DNS resolver seems to be working.
101  */
102 static int resolver_working;
103
104 /**
105  * Handle to namestore.
106  */
107 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
108
109 /**
110  * Handle to GNS resolver.
111  */
112 static struct GNUNET_GNS_Handle *gns_handle;
113
114 /**
115  * Handle for DNS request.
116  */
117 static struct GNUNET_RESOLVER_RequestHandle *resolver_handle;
118
119 /**
120  * Our configuration.
121  */
122 static const struct GNUNET_CONFIGURATION_Handle *cfg;
123
124 /**
125  * Handle for active GNS lookup.
126  */
127 static struct GNUNET_GNS_LookupRequest *lr;
128
129 /**
130  * Queue for storing records in namestore.
131  */
132 static struct GNUNET_NAMESTORE_QueueEntry *qe;
133
134 /**
135  * Our private key for signing records.
136  */
137 static struct GNUNET_CRYPTO_EccPrivateKey *alice_key;
138
139
140 /**
141  * Check if the get_handle is being used, if so stop the request.  Either
142  * way, schedule the end_badly_cont function which actually shuts down the
143  * test.
144  */
145 static void
146 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
147 {
148   die_task = GNUNET_SCHEDULER_NO_TASK;
149   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test failed, shutting down...\n");
150   if (NULL != lr)
151   {
152     GNUNET_GNS_cancel_lookup_request (lr);
153     lr = NULL;
154   }
155   if (NULL != resolver_handle)
156   {
157     GNUNET_RESOLVER_request_cancel (resolver_handle);
158     resolver_handle = NULL;
159   }
160   if (NULL != qe)
161   {
162     GNUNET_NAMESTORE_cancel (qe);
163     qe = NULL;
164   }
165   if (NULL != gns_handle)
166   {
167     GNUNET_GNS_disconnect(gns_handle);
168     gns_handle = NULL;
169   }
170   if (NULL != namestore_handle)
171   {
172     GNUNET_NAMESTORE_disconnect (namestore_handle);
173     namestore_handle = NULL;
174   }
175   if (NULL != alice_key)
176   {
177     GNUNET_CRYPTO_ecc_key_free (alice_key);
178     alice_key = NULL;
179   }
180   GNUNET_break (0);
181   GNUNET_SCHEDULER_shutdown ();
182   ok = 1;
183 }
184
185
186 /**
187  * We hit a hard failure, shutdown now.
188  */
189 static void
190 end_badly_now ()
191 {
192   GNUNET_SCHEDULER_cancel (die_task);
193   die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
194 }
195
196
197 /**
198  * Testcase is finished, terminate everything.
199  */
200 static void
201 end_now (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
202 {
203   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 
204              "Test successful, shutting down...\n");
205   if (GNUNET_SCHEDULER_NO_TASK != die_task)
206   {
207       GNUNET_SCHEDULER_cancel (die_task);
208       die_task = GNUNET_SCHEDULER_NO_TASK;
209   }
210   if (NULL != qe)
211   {
212     GNUNET_NAMESTORE_cancel (qe);
213     qe = NULL;
214   }
215   if (NULL != resolver_handle)
216   {
217     GNUNET_RESOLVER_request_cancel (resolver_handle);
218     resolver_handle = NULL;
219   }
220   if (NULL != gns_handle)
221   {
222     GNUNET_GNS_disconnect(gns_handle);
223     gns_handle = NULL;
224   }
225   if (NULL != namestore_handle)
226   {
227     GNUNET_NAMESTORE_disconnect (namestore_handle);
228     namestore_handle = NULL;
229   }
230   if (NULL != alice_key)
231   {
232     GNUNET_CRYPTO_ecc_key_free (alice_key);
233     alice_key = NULL;
234   }
235   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down peer!\n");
236   GNUNET_SCHEDULER_shutdown ();
237 }
238
239
240 /**
241  * We got resolution result for 'TEST_DOMAIN_ALT2', check if
242  * they match our expectations, then finish the test with success.
243  *
244  * @param cls unused
245  * @param rd_count number of records in rd
246  * @param rd records returned from naming system for the name
247  */
248 static void
249 on_lookup_result_alt2 (void *cls, uint32_t rd_count,
250                        const struct GNUNET_NAMESTORE_RecordData *rd)
251 {
252   struct in_addr a;
253   uint32_t i;
254   char* addr;
255
256   lr = NULL;
257   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received alternative results 2\n");
258   if (0 == rd_count)
259   {
260     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
261                 "Lookup for `%s' failed\n",
262                 TEST_DOMAIN_ALT2);
263     ok = 2;
264     GNUNET_SCHEDULER_add_now (&end_now, NULL);
265     return;
266   }
267   ok = 1;
268   for (i=0; i<rd_count; i++)
269   {
270     if (rd[i].record_type == GNUNET_DNSPARSER_TYPE_A)
271     {
272       memcpy(&a, rd[i].data, sizeof(a));
273       addr = inet_ntoa(a);
274       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "address: %s\n", addr);
275       if (0 == strcmp(addr, TEST_IP_ALT2))
276       {
277         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
278                     "%s correctly resolved to %s!\n", 
279                     TEST_DOMAIN_ALT2, addr);
280         ok = 0;
281       }
282       else
283         GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
284                     "Got unexpected address %s for %s\n",
285                     addr,
286                     TEST_DOMAIN);
287     }
288   }
289   if (1 == ok)
290   {
291     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
292                 "None of the results matched the expected value %s for %s\n",
293                 TEST_IP,
294                 TEST_DOMAIN);
295     GNUNET_SCHEDULER_add_now (&end_now, NULL);
296     return;
297   }
298
299   GNUNET_SCHEDULER_add_now (&end_now, NULL);
300 }
301
302
303 /**
304  * We got resolution result for 'TEST_DOMAIN_ALT', check if
305  * they match our expectations, then move on to the next
306  * resolution.
307  *
308  * @param cls unused
309  * @param rd_count number of records in rd
310  * @param rd records returned from naming system for the name
311  */
312 static void
313 on_lookup_result_alt (void *cls, uint32_t rd_count,
314                       const struct GNUNET_NAMESTORE_RecordData *rd)
315 {
316   struct in_addr a;
317   uint32_t i;
318   char* addr;
319
320   lr = NULL;
321   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received alternative results\n");
322   if (0 == rd_count)
323   {
324     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
325                 "Lookup for `%s' failed\n",
326                 TEST_DOMAIN_ALT);
327     ok = 2;
328     GNUNET_SCHEDULER_add_now (&end_now, NULL);
329     return;
330   }
331   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
332               "Received %u results for %s\n",
333               (unsigned int) rd_count,
334               TEST_DOMAIN_ALT);
335   ok = 1;
336   for (i=0; i<rd_count; i++)
337   {
338     if (rd[i].record_type == GNUNET_DNSPARSER_TYPE_A)
339     {
340       memcpy (&a, rd[i].data, sizeof(a));
341       addr = inet_ntoa (a);
342       if (0 == strcmp(addr, TEST_IP))
343       {
344         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
345                     "%s correctly resolved to %s!\n", TEST_DOMAIN, addr);
346         ok = 0;
347       }
348       else
349         GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
350                     "Got unexpected address %s for %s\n",
351                     addr,
352                     TEST_DOMAIN_ALT);
353     }
354   }
355   if (1 == ok)
356   {
357     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
358                 "None of the results matched the expected value %s for %s\n",
359                 TEST_IP,
360                 TEST_DOMAIN_ALT);
361     GNUNET_SCHEDULER_add_now (&end_now, NULL);
362     return;
363   }
364
365   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
366               "Starting lookup for `%s'\n",
367               TEST_DOMAIN_ALT2);
368   lr = GNUNET_GNS_lookup (gns_handle, 
369                           TEST_DOMAIN_ALT2, GNUNET_DNSPARSER_TYPE_A,
370                           GNUNET_YES,
371                           NULL,
372                           &on_lookup_result_alt2, NULL);
373 }
374
375
376 /**
377  * We got resolution result for 'TEST_DOMAIN', check if
378  * they match our expectations, then move on to the next
379  * resolution.
380  *
381  * @param cls unused
382  * @param rd_count number of records in rd
383  * @param rd records returned from naming system for the name
384  */
385 static void
386 on_lookup_result (void *cls, uint32_t rd_count,
387                   const struct GNUNET_NAMESTORE_RecordData *rd)
388 {
389   struct in_addr a;
390   uint32_t i;
391   char* addr;
392
393   lr = NULL;
394   if (0 == rd_count)
395   {
396     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
397                 "Lookup for `%s' failed\n",
398                 TEST_DOMAIN);
399     ok = 2;
400     GNUNET_SCHEDULER_add_now (&end_now, NULL);
401     return;
402   }
403   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
404               "Received %u results for %s\n",
405               (unsigned int) rd_count,
406               TEST_DOMAIN);
407   ok = 1;
408   for (i=0; i<rd_count; i++)
409   {
410     if (rd[i].record_type == GNUNET_DNSPARSER_TYPE_A)
411     {
412       memcpy (&a, rd[i].data, sizeof(a));
413       addr = inet_ntoa(a);
414       if (0 == strcmp (addr, TEST_IP))
415       {
416         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
417                     "%s correctly resolved to %s!\n", 
418                     TEST_DOMAIN, addr);
419         ok = 0;
420       }
421       else
422         GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
423                     "Got unexpected address %s for %s\n",
424                     addr,
425                     TEST_DOMAIN);
426     }
427   }
428   if (1 == ok)
429   {
430     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
431                 "None of the results matched the expected value %s for %s\n",
432                 TEST_IP,
433                 TEST_DOMAIN);
434     GNUNET_SCHEDULER_add_now (&end_now, NULL);
435     return;
436   }
437   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
438               "Starting lookup for `%s'\n",
439               TEST_DOMAIN_ALT);
440
441   lr = GNUNET_GNS_lookup (gns_handle, TEST_DOMAIN_ALT, GNUNET_DNSPARSER_TYPE_A,
442                           GNUNET_YES,
443                           NULL,
444                           &on_lookup_result_alt, NULL);
445 }
446
447
448 /**
449  * Start the actual NS-based lookup.
450  */
451 static void
452 start_lookup ()
453 {
454   gns_handle = GNUNET_GNS_connect (cfg);
455   if (NULL == gns_handle)
456   {
457     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
458                 "Failed to connect to GNS!\n");
459     end_badly_now ();
460     return;
461   }
462   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
463               "Records ready, starting lookup for `%s'\n",
464               TEST_DOMAIN);
465   lr = GNUNET_GNS_lookup (gns_handle, TEST_DOMAIN, GNUNET_DNSPARSER_TYPE_A,
466                           GNUNET_YES,
467                           NULL,
468                           &on_lookup_result, NULL);
469 }
470
471
472 /**
473  * Function called with the result of resolving the "NS" record
474  * for TEST_RECORD_NS.  Check if the NS record is set as expected,
475  * and if so, continue with the test.
476  *
477  * @param cls closure, unused
478  * @param addr NULL for last address,
479  * @param addrlen number of bytes in addr
480  */
481 static void
482 handle_dns_test (void *cls,
483                  const struct sockaddr *addr,
484                  socklen_t addrlen)
485 {
486   struct sockaddr_in* sai;
487
488   resolver_handle = NULL;
489   if (NULL == addr)
490   {
491     /* end of results */
492     if (GNUNET_YES != resolver_working)
493     {
494       ok = 0;
495       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
496                   "System resolver not working as expected. Test inconclusive!\n");
497       GNUNET_SCHEDULER_add_now (&end_now, NULL);
498       return;
499     }
500     /* done preparing records, start lookup */
501     GNUNET_NAMESTORE_disconnect (namestore_handle);
502     namestore_handle = NULL;
503     start_lookup ();
504     return;
505   }
506   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
507               "Received DNS response\n");
508   if (addrlen == sizeof (struct sockaddr_in))
509   {
510     sai = (struct sockaddr_in*) addr;
511     if (0 == strcmp (TEST_IP, inet_ntoa (sai->sin_addr)))
512     {
513       resolver_working = GNUNET_YES;
514       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515                   "Resolver is working (returned expected A record %s for %s)\n",
516                   TEST_IP,
517                   TEST_RECORD_NS);
518     }
519   }
520 }
521
522
523 /**
524  * Function scheduled to be run on the successful start of services
525  * tries to look up the dns record for TEST_DOMAIN
526  *
527  * @param cls closure, unused
528  * @param success GNUNET_OK on success
529  * @param emsg error message, NULL on success
530  */
531 static void
532 commence_testing (void *cls, int32_t success, const char *emsg)
533 {
534   qe = NULL;
535   if (NULL != emsg)
536     FPRINTF (stderr, "Failed to create record: %s\n", emsg);
537   GNUNET_assert (GNUNET_YES == success);
538   resolver_working = GNUNET_NO;
539   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
540               "Resolving NS record for %s\n",
541               TEST_RECORD_NS);
542   GNUNET_RESOLVER_connect (cfg);
543   resolver_handle = GNUNET_RESOLVER_ip_get (TEST_RECORD_NS,
544                                             AF_INET,
545                                             TIMEOUT,
546                                             &handle_dns_test,
547                                             NULL);
548 }
549
550
551 /**
552  * Peer is ready, run the actual test.  Begins by storing
553  * a record in the namestore.
554  *
555  * @param cls closure, NULL
556  * @param ccfg our configuration
557  * @param peer handle to the peer
558  */
559 static void
560 do_check (void *cls,
561           const struct GNUNET_CONFIGURATION_Handle *ccfg,
562           struct GNUNET_TESTING_Peer *peer)
563 {
564   struct GNUNET_CRYPTO_EccPublicKey alice_pkey;
565   char* alice_keyfile;
566   struct GNUNET_NAMESTORE_RecordData rd[2];
567   struct in_addr ns;
568   
569   cfg = ccfg;
570   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
571
572   /* put records into namestore */
573   namestore_handle = GNUNET_NAMESTORE_connect (cfg);
574   if (NULL == namestore_handle)
575   {
576     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
577                 "Failed to connect to namestore\n");
578     end_badly_now ();
579     return;
580   }
581
582   if (GNUNET_OK !=
583       GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
584                                                "ZONEKEY",
585                                                &alice_keyfile))
586   {
587     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
588                 "Failed to get key from cfg\n");
589     end_badly_now ();
590     return;
591   }
592
593   alice_key = GNUNET_CRYPTO_ecc_key_create_from_file (alice_keyfile);
594   GNUNET_CRYPTO_ecc_key_get_public (alice_key, &alice_pkey);
595   GNUNET_free (alice_keyfile);
596
597   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
598               "Creating NS records\n");
599   rd[0].expiration_time = UINT64_MAX;
600   GNUNET_assert(1 == inet_pton (AF_INET, TEST_IP_NS, &ns));
601   rd[0].data_size = sizeof (struct in_addr);
602   rd[0].data = &ns;
603   rd[0].record_type = GNUNET_DNSPARSER_TYPE_A;
604   rd[0].flags = GNUNET_NAMESTORE_RF_AUTHORITY;
605   
606   rd[1].expiration_time = UINT64_MAX;
607   rd[1].data_size = strlen (TEST_RECORD_NS);
608   rd[1].data = TEST_RECORD_NS;
609   rd[1].record_type = GNUNET_DNSPARSER_TYPE_NS;
610   rd[1].flags = GNUNET_NAMESTORE_RF_AUTHORITY;
611
612   qe = GNUNET_NAMESTORE_record_put_by_authority (namestore_handle,
613                                                  alice_key,
614                                                  TEST_RECORD_NAME,
615                                                  2, rd,
616                                                  &commence_testing,
617                                                  NULL);
618 }
619
620
621 int
622 main (int argc, char *argv[])
623 {
624   ok = 1;
625   GNUNET_TESTING_peer_run ("test-gns-simple-ns-lookup", "test_gns_simple_lookup.conf",
626                            &do_check, NULL);
627   return ok;
628 }
629
630 /* end of test_gns_ns_lookup.c */