f4df6d48c45376ee82691f38db44c150502bf63a
[oweals/gnunet.git] / src / testing / testing.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 2009, 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 /**
22  * @file testing/testing.c
23  * @brief convenience API for writing testcases for GNUnet
24  *        Many testcases need to start and stop a peer/service
25  *        and this library is supposed to make that easier
26  *        for TESTCASES.  Normal programs should always
27  *        use functions from gnunet_{util,arm}_lib.h.  This API is
28  *        ONLY for writing testcases (or internal use of the testbed).
29  * @author Christian Grothoff
30  *
31  */
32 #include "platform.h"
33 #include "gnunet_util_lib.h"
34 #include "gnunet_testing_lib-new.h"
35
36 #define LOG(kind,...)                                           \
37   GNUNET_log_from (kind, "testing-api", __VA_ARGS__)
38
39 /**
40  * Lowest port used for GNUnet testing.  Should be high enough to not
41  * conflict with other applications running on the hosts but be low
42  * enough to not conflict with client-ports (typically starting around
43  * 32k).
44  */
45 #define LOW_PORT 12000
46
47 /**
48  * Highest port used for GNUnet testing.  Should be low enough to not
49  * conflict with the port range for "local" ports (client apps; see
50  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
51  */
52 #define HIGH_PORT 56000
53
54
55 /**
56  * Handle for a system on which GNUnet peers are executed;
57  * a system is used for reserving unique paths and ports.
58  */
59 struct GNUNET_TESTING_System
60 {
61   /**
62    * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
63    * SERVICEHOME.    */
64   char *tmppath;
65
66   /**
67    * The hostname of the controller
68    */
69   char *controller;
70
71   /**
72    * our hostname
73    */
74   char *hostname;
75
76   /**
77    * Hostkeys data, contains "GNUNET_TESTING_HOSTKEYFILESIZE * total_hostkeys" bytes.
78    */
79   char *hostkeys_data;
80
81   /**
82    * memory map for 'hostkeys_data'.
83    */
84   struct GNUNET_DISK_MapHandle *map;
85
86   /**
87    * File descriptor for the map.
88    */
89   struct GNUNET_DISK_FileHandle *map_fd;
90
91   /**
92    * Bitmap where each TCP port that has already been reserved for
93    * some GNUnet peer is recorded.  Note that we additionally need to
94    * test if a port is already in use by non-GNUnet components before
95    * assigning it to a peer/service.  If we detect that a port is
96    * already in use, we also mark it in this bitmap.  So all the bits
97    * that are zero merely indicate ports that MIGHT be available for
98    * peers.
99    */
100   uint32_t reserved_tcp_ports[65536 / 32];
101
102   /**
103    * Bitmap where each UDP port that has already been reserved for
104    * some GNUnet peer is recorded.  Note that we additionally need to
105    * test if a port is already in use by non-GNUnet components before
106    * assigning it to a peer/service.  If we detect that a port is
107    * already in use, we also mark it in this bitmap.  So all the bits
108    * that are zero merely indicate ports that MIGHT be available for
109    * peers.
110    */
111   uint32_t reserved_udp_ports[65536 / 32];
112
113   /**
114    * Counter we use to make service home paths unique on this system;
115    * the full path consists of the tmppath and this number.  Each
116    * UNIXPATH for a peer is also modified to include the respective
117    * path counter to ensure uniqueness.  This field is incremented
118    * by one for each configured peer.  Even if peers are destroyed,
119    * we never re-use path counters.
120    */
121   uint32_t path_counter;  
122
123   /**
124    * The number of hostkeys
125    */
126   uint32_t total_hostkeys;
127
128   /**
129    * Lowest port we are allowed to use.
130    */
131   uint16_t lowport;
132
133   /**
134    * Highest port we are allowed to use.
135    */
136   uint16_t highport;
137 };
138
139
140 /**
141  * Handle for a GNUnet peer controlled by testing.
142  */
143 struct GNUNET_TESTING_Peer
144 {
145   /**
146    * The TESTING system associated with this peer
147    */
148   struct GNUNET_TESTING_System *system;
149
150   /**
151    * Path to the configuration file for this peer.
152    */
153   char *cfgfile;
154
155   /**
156    * Binary to be executed during 'GNUNET_TESTING_peer_start'.
157    * Typically 'gnunet-service-arm' (but can be set to a 
158    * specific service by 'GNUNET_TESTING_service_run' if
159    * necessary).
160    */ 
161   char *main_binary;
162   
163   /**
164    * Handle to the running binary of the service, NULL if the
165    * peer/service is currently not running.
166    */
167   struct GNUNET_OS_Process *main_process;
168
169   /**
170    * The keynumber of this peer's hostkey
171    */
172   uint32_t key_number;
173 };
174
175
176 /**
177  * Testing includes a number of pre-created hostkeys for faster peer
178  * startup. This function loads such keys into memory from a file.
179  *
180  * @param system the testing system handle
181  * @return GNUNET_OK on success; GNUNET_SYSERR on error
182  */
183 static int
184 hostkeys_load (struct GNUNET_TESTING_System *system)
185 {
186   uint64_t fs; 
187   char *data_dir;
188   char *filename;
189   
190   GNUNET_assert (NULL == system->hostkeys_data);
191   data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
192   GNUNET_asprintf (&filename, "%s/testing_hostkeys.dat", data_dir);
193   GNUNET_free (data_dir);  
194
195   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
196   {
197     LOG (GNUNET_ERROR_TYPE_ERROR,
198          _("Hostkeys file not found: %s\n"), filename);
199     GNUNET_free (filename);
200     return GNUNET_SYSERR;
201   }
202   /* Check hostkey file size, read entire thing into memory */
203   if (GNUNET_OK != 
204       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
205     fs = 0;
206   if (0 == fs)
207   {
208     GNUNET_free (filename);
209     return GNUNET_SYSERR;       /* File is empty */
210   }
211   if (0 != (fs % GNUNET_TESTING_HOSTKEYFILESIZE))
212   {
213     LOG (GNUNET_ERROR_TYPE_ERROR,
214          _("Incorrect hostkey file format: %s\n"), filename);
215     GNUNET_free (filename);
216     return GNUNET_SYSERR;
217   }
218   system->map_fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
219                                          GNUNET_DISK_PERM_NONE);
220   if (NULL == system->map_fd)
221   {
222     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename);
223     GNUNET_free (filename);
224     return GNUNET_SYSERR;
225   }
226   system->total_hostkeys = fs / GNUNET_TESTING_HOSTKEYFILESIZE;
227   system->hostkeys_data = GNUNET_DISK_file_map (system->map_fd,
228                                                 &system->map,
229                                                 GNUNET_DISK_MAP_TYPE_READ,
230                                                 fs);
231   GNUNET_free (filename);
232   return GNUNET_OK;
233 }
234
235
236 /**
237  * Function to remove the loaded hostkeys
238  *
239  * @param system the testing system handle
240  */
241 static void
242 hostkeys_unload (struct GNUNET_TESTING_System *system)
243 {
244   GNUNET_break (NULL != system->hostkeys_data);
245   system->hostkeys_data = NULL;
246   GNUNET_DISK_file_unmap (system->map);
247   system->map = NULL;
248   GNUNET_DISK_file_close (system->map_fd);
249   system->map_fd = NULL;
250   system->hostkeys_data = NULL;
251   system->total_hostkeys = 0;
252 }
253
254
255 /**
256  * Create a system handle.  There must only be one system
257  * handle per operating system.
258  *
259  * @param testdir only the directory name without any path. This is used for
260  *          all service homes; the directory will be created in a temporary
261  *          location depending on the underlying OS
262  * @param controller hostname of the controlling host, 
263  *        service configurations are modified to allow 
264  *        control connections from this host; can be NULL
265  * @param hostname the hostname of the system we are using for testing; NULL for
266  *          localhost
267  * @param lowport lowest port number this system is allowed to allocate (inclusive)
268  * @param highport highest port number this system is allowed to allocate (exclusive)
269  * @return handle to this system, NULL on error
270  */
271 struct GNUNET_TESTING_System *
272 GNUNET_TESTING_system_create_with_portrange (const char *testdir,
273                                              const char *controller,
274                                              const char *hostname,
275                                              uint16_t lowport,
276                                              uint16_t highport)
277 {
278   struct GNUNET_TESTING_System *system;
279
280   GNUNET_assert (NULL != testdir);
281   system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System));
282   system->tmppath = GNUNET_DISK_mkdtemp (testdir);
283   system->lowport = lowport;
284   system->highport = highport;
285   if (NULL == system->tmppath)
286   {
287     GNUNET_free (system);
288     return NULL;
289   }
290   if (NULL != controller)
291     system->controller = GNUNET_strdup (controller);
292   if (NULL != hostname)
293     system->hostname = GNUNET_strdup (hostname);
294   if (GNUNET_OK != hostkeys_load (system))
295   {
296     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
297     return NULL;
298   }
299   return system;
300 }
301
302
303 /**
304  * Create a system handle.  There must only be one system handle per operating
305  * system.  Uses a default range for allowed ports.  Ports are still tested for
306  * availability.
307  *
308  * @param testdir only the directory name without any path. This is used for all
309  *          service homes; the directory will be created in a temporary location
310  *          depending on the underlying OS
311  * @param controller hostname of the controlling host, service configurations
312  *        are modified to allow control connections from this host; can be NULL
313  * @param hostname the hostname of the system we are using for testing; NULL for
314  *          localhost
315  * @return handle to this system, NULL on error
316  */
317 struct GNUNET_TESTING_System *
318 GNUNET_TESTING_system_create (const char *testdir,
319                               const char *controller,
320                               const char *hostname)
321 {
322   return GNUNET_TESTING_system_create_with_portrange (testdir,
323                                                       controller,
324                                                       hostname,
325                                                       LOW_PORT,
326                                                       HIGH_PORT);
327 }
328
329
330 /**
331  * Free system resources.
332  *
333  * @param system system to be freed
334  * @param remove_paths should the 'testdir' and all subdirectories
335  *        be removed (clean up on shutdown)?
336  */
337 void
338 GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
339                                int remove_paths)
340 {
341   if (NULL != system->hostkeys_data)
342     hostkeys_unload (system);
343   if (GNUNET_YES == remove_paths)
344     GNUNET_DISK_directory_remove (system->tmppath);
345   GNUNET_free (system->tmppath);
346   GNUNET_free_non_null (system->controller);
347   GNUNET_free_non_null (system->hostname);
348   GNUNET_free (system);
349 }
350
351
352 /**
353  * Reserve a TCP or UDP port for a peer.
354  *
355  * @param system system to use for reservation tracking
356  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
357  * @return 0 if no free port was available
358  */
359 uint16_t 
360 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system,
361                              int is_tcp)
362 {
363   struct GNUNET_NETWORK_Handle *socket;
364   struct addrinfo hint;
365   struct addrinfo *ret;
366   struct addrinfo *ai;
367   uint32_t *port_buckets;
368   char *open_port_str;
369   int bind_status;
370   uint32_t xor_image;
371   uint16_t index;
372   uint16_t open_port;
373   uint16_t pos;
374
375   /*
376   FIXME: Instead of using getaddrinfo we should try to determine the port
377          status by the following heurestics.
378   
379          On systems which support both IPv4 and IPv6, only ports open on both
380          address families are considered open.
381          On system with either IPv4 or IPv6. A port is considered open if it's
382          open in the respective address family
383   */
384   hint.ai_family = AF_UNSPEC;   /* IPv4 and IPv6 */
385   hint.ai_socktype = (GNUNET_YES == is_tcp)? SOCK_STREAM : SOCK_DGRAM;
386   hint.ai_protocol = 0;
387   hint.ai_addrlen = 0;
388   hint.ai_addr = NULL;
389   hint.ai_canonname = NULL;
390   hint.ai_next = NULL;
391   hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV;  /* Wild card address */
392   port_buckets = (GNUNET_YES == is_tcp) ?
393     system->reserved_tcp_ports : system->reserved_udp_ports;
394   for (index = (system->lowport / 32) + 1; index < (system->highport / 32); index++)
395   {
396     xor_image = (UINT32_MAX ^ port_buckets[index]);
397     if (0 == xor_image)        /* Ports in the bucket are full */
398       continue;
399     pos = system->lowport % 32;
400     while (pos < 32)
401     {
402       if (0 == ((xor_image >> pos) & 1U))
403       {
404         pos++;
405         continue;
406       }
407       open_port = (index * 32) + pos;
408       if (open_port >= system->highport)
409         return 0;
410       GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port);
411       ret = NULL;
412       GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
413       GNUNET_free (open_port_str);
414       bind_status = GNUNET_NO;
415       for (ai = ret; NULL != ai; ai = ai->ai_next)
416       {
417         socket = GNUNET_NETWORK_socket_create (ai->ai_family,
418                                                (GNUNET_YES == is_tcp) ?
419                                                SOCK_STREAM : SOCK_DGRAM,
420                                                0);
421         if (NULL == socket)
422           continue;
423         bind_status = GNUNET_NETWORK_socket_bind (socket,
424                                                   ai->ai_addr,
425                                                   ai->ai_addrlen);
426         GNUNET_NETWORK_socket_close (socket);
427         if (GNUNET_OK != bind_status)
428           break;
429       }
430       port_buckets[index] |= (1U << pos); /* Set the port bit */
431       freeaddrinfo (ret);
432       if (GNUNET_OK == bind_status)
433       {
434         LOG (GNUNET_ERROR_TYPE_DEBUG,
435              "Found a free port %u\n", (unsigned int) open_port);
436         return open_port;
437       }
438       pos++;
439     }
440   }
441   return 0;
442 }
443
444
445 /**
446  * Release reservation of a TCP or UDP port for a peer
447  * (used during GNUNET_TESTING_peer_destroy).
448  *
449  * @param system system to use for reservation tracking
450  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
451  * @param port reserved port to release
452  */
453 void
454 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
455                              int is_tcp,
456                              uint16_t port)
457 {
458   uint32_t *port_buckets;
459   uint16_t bucket;
460   uint16_t pos;
461
462   port_buckets = (GNUNET_YES == is_tcp) ?
463     system->reserved_tcp_ports : system->reserved_udp_ports;
464   bucket = port / 32;
465   pos = port % 32;
466   LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
467   if (0 == (port_buckets[bucket] & (1U << pos)))
468   {
469     GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
470     return;
471   }
472   port_buckets[bucket] &= ~(1U << pos);
473 }
474
475
476 /**
477  * Reserve a SERVICEHOME path for a peer.
478  *
479  * @param system system to use for reservation tracking
480  * @return NULL on error, otherwise fresh unique path to use
481  *         as the servicehome for the peer; must be freed by the caller
482  */
483 // static 
484 char *
485 reserve_path (struct GNUNET_TESTING_System *system)
486 {
487   char *reserved_path;
488
489   GNUNET_asprintf (&reserved_path,
490                    "%s/%u", system->tmppath, system->path_counter++);
491   return reserved_path;
492 }             
493
494
495 /**
496  * Testing includes a number of pre-created hostkeys for
497  * faster peer startup.  This function can be used to
498  * access the n-th key of those pre-created hostkeys; note
499  * that these keys are ONLY useful for testing and not
500  * secure as the private keys are part of the public 
501  * GNUnet source code.
502  *
503  * This is primarily a helper function used internally
504  * by 'GNUNET_TESTING_peer_configure'.
505  *
506  * @param system the testing system handle
507  * @param key_number desired pre-created hostkey to obtain
508  * @param id set to the peer's identity (hash of the public
509  *        key; if NULL, GNUNET_SYSERR is returned immediately
510  * @return NULL on error (not enough keys)
511  */
512 struct GNUNET_CRYPTO_RsaPrivateKey *
513 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
514                             uint32_t key_number,
515                             struct GNUNET_PeerIdentity *id)
516 {  
517   struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
518   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
519   
520   if ((NULL == id) || (NULL == system->hostkeys_data))
521     return NULL;
522   if (key_number >= system->total_hostkeys)
523   {
524     LOG (GNUNET_ERROR_TYPE_ERROR,
525          _("Key number %u does not exist\n"), key_number);
526     return NULL;
527   }   
528   private_key = GNUNET_CRYPTO_rsa_decode_key (system->hostkeys_data +
529                                               (key_number *
530                                                GNUNET_TESTING_HOSTKEYFILESIZE),
531                                               GNUNET_TESTING_HOSTKEYFILESIZE);
532   if (NULL == private_key)
533   {
534     LOG (GNUNET_ERROR_TYPE_ERROR,
535          _("Error while decoding key %u\n"), key_number);
536     return NULL;
537   }
538   GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
539   GNUNET_CRYPTO_hash (&public_key,
540                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
541                       &(id->hashPubKey));
542   return private_key;
543 }
544
545
546 /**
547  * Structure for holding data to build new configurations from a configuration
548  * template
549  */
550 struct UpdateContext
551 {
552   /**
553    * The system for which we are building configurations
554    */
555   struct GNUNET_TESTING_System *system;
556   
557   /**
558    * The configuration we are building
559    */
560   struct GNUNET_CONFIGURATION_Handle *cfg;
561
562   /**
563    * The customized service home path for this peer
564    */
565   char *service_home;
566
567   /**
568    * build status - to signal error while building a configuration
569    */
570   int status;
571 };
572
573
574 /**
575  * Function to iterate over options.  Copies
576  * the options to the target configuration,
577  * updating PORT values as needed.
578  *
579  * @param cls the UpdateContext
580  * @param section name of the section
581  * @param option name of the option
582  * @param value value of the option
583  */
584 static void
585 update_config (void *cls, const char *section, const char *option,
586                const char *value)
587 {
588   struct UpdateContext *uc = cls;
589   unsigned int ival;
590   char cval[12];
591   char uval[128];
592   char *single_variable;
593   char *per_host_variable;
594   unsigned long long num_per_host;
595   uint16_t new_port;
596
597   if (GNUNET_OK != uc->status)
598     return;
599   if (! ((0 == strcmp (option, "PORT"))
600          || (0 == strcmp (option, "UNIXPATH"))
601          || (0 == strcmp (option, "HOSTNAME"))))
602     return;
603   GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
604   GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
605   if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival)))
606   {
607     if ((ival != 0) &&
608         (GNUNET_YES !=
609          GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
610                                                single_variable)))
611     {
612       /* FIXME: What about UDP? */
613       new_port = GNUNET_TESTING_reserve_port (uc->system, GNUNET_YES);
614       if (0 == new_port)
615       {
616         uc->status = GNUNET_SYSERR;
617         return;
618       }
619       GNUNET_snprintf (cval, sizeof (cval), "%u", new_port);
620       value = cval;
621     }
622     else if ((ival != 0) &&
623              (GNUNET_YES ==
624               GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
625                                                     single_variable)) &&
626              GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
627                                                     per_host_variable,
628                                                     &num_per_host))
629     {
630       /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
631       /*                  ival + ctx->fdnum % num_per_host); */
632       /* value = cval; */
633       GNUNET_break (0);         /* FIXME */
634     }
635   }
636   if (0 == strcmp (option, "UNIXPATH"))
637   {
638     if (GNUNET_YES !=
639         GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
640                                               single_variable))
641     {
642       GNUNET_snprintf (uval, sizeof (uval), "%s/%s.sock",
643                        uc->service_home, section);
644       value = uval;
645     }
646     else if ((GNUNET_YES ==
647               GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
648                                                      per_host_variable,
649                                                      &num_per_host)) &&
650              (num_per_host > 0))
651     {
652       GNUNET_break(0);          /* FIXME */
653     }
654   }
655   if (0 == strcmp (option, "HOSTNAME"))
656   {
657     value = (NULL == uc->system->hostname) ? "localhost" : uc->system->hostname;
658   }
659   GNUNET_free (single_variable);
660   GNUNET_free (per_host_variable);
661   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
662 }
663
664
665 /**
666  * Section iterator to set ACCEPT_FROM/ACCEPT_FROM6 depending on the ip of the
667  * controller in all sections
668  *
669  * @param cls the UpdateContext
670  * @param section name of the section
671  */
672 static void
673 update_config_sections (void *cls,
674                         const char *section)
675 {
676   struct UpdateContext *uc = cls;  
677   char **ikeys;
678   char *val;
679   char *ptr;
680   char *orig_allowed_hosts;
681   char *allowed_hosts;
682   char *ACCEPT_FROM_key;
683   uint16_t ikeys_cnt;
684   uint16_t key;
685   
686   ikeys_cnt = 0;
687   val = NULL;
688   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section,
689                                                      "TESTING_IGNORE_KEYS"))
690   {
691     GNUNET_assert 
692       (GNUNET_YES == 
693        GNUNET_CONFIGURATION_get_value_string (uc->cfg, section,
694                                               "TESTING_IGNORE_KEYS", &val));
695     ptr = val;
696     for (ikeys_cnt = 0; NULL != (ptr = strstr (ptr, ";")); ikeys_cnt++)
697       ptr++;
698     if (0 == ikeys_cnt)
699       GNUNET_break (0);
700     else
701     {
702       ikeys = GNUNET_malloc ((sizeof (char *)) * ikeys_cnt);
703       ptr = val;
704       for (key = 0; key < ikeys_cnt; key++)
705       {
706         ikeys[key] = ptr;
707         ptr = strstr (ptr, ";");
708         *ptr = '\0';
709         ptr++;
710       }
711     }
712   }
713   if (0 != ikeys_cnt)
714   {
715     for (key = 0; key < ikeys_cnt; key++)
716     {
717       if (NULL != strstr (ikeys[key], "ADVERTISED_PORT"))
718         break;
719     }
720     if ((key == ikeys_cnt) &&
721         (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section,
722                                                         "ADVERTISED_PORT")))
723     {
724       if (GNUNET_OK == 
725           GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "PORT", &ptr))
726       {
727         GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, 
728                                                "ADVERTISED_PORT", ptr);
729         GNUNET_free (ptr);
730       }
731     }
732     for (key = 0; key < ikeys_cnt; key++)
733     {
734       if (NULL != strstr (ikeys[key], "ACCEPT_FROM"))
735       {
736         GNUNET_free (ikeys);
737         GNUNET_free (val);
738         return;
739       }
740     }
741     GNUNET_free (ikeys);
742   }
743   GNUNET_free_non_null (val);
744   ACCEPT_FROM_key = "ACCEPT_FROM";  
745   if ((NULL != uc->system->controller) && 
746       (NULL != strstr (uc->system->controller, ":"))) /* IPv6 in use */
747     ACCEPT_FROM_key = "ACCEPT_FROM6";
748   if (GNUNET_OK != 
749       GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, ACCEPT_FROM_key,
750                                              &orig_allowed_hosts))
751   {
752     orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;");
753   }
754   if (NULL == uc->system->controller)
755     allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
756   else
757     GNUNET_asprintf (&allowed_hosts, "%s%s;", orig_allowed_hosts,
758                      uc->system->controller);
759   GNUNET_free (orig_allowed_hosts);
760   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, ACCEPT_FROM_key,
761                                          allowed_hosts);
762   GNUNET_free (allowed_hosts);  
763 }
764
765
766 /**
767  * Create a new configuration using the given configuration as a template;
768  * ports and paths will be modified to select available ports on the local
769  * system. The default configuration will be available in PATHS section under
770  * the option DEFAULTCONFIG after the call. SERVICE_HOME is also set in PATHS
771  * section to the temporary directory specific to this configuration. If we run
772  * out of "*port" numbers, return SYSERR.
773  *
774  * This is primarily a helper function used internally
775  * by 'GNUNET_TESTING_peer_configure'.
776  *
777  * @param system system to use to coordinate resource usage
778  * @param cfg template configuration to update
779  * @return GNUNET_OK on success, GNUNET_SYSERR on error - the configuration will
780  *           be incomplete and should not be used there upon
781  */
782 int
783 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
784                                      struct GNUNET_CONFIGURATION_Handle *cfg)
785 {
786   struct UpdateContext uc;
787   char *default_config;
788   
789   uc.system = system;
790   uc.cfg = cfg;
791   uc.status = GNUNET_OK;
792   GNUNET_asprintf (&uc.service_home, "%s/%u", system->tmppath,
793                    system->path_counter++);
794   GNUNET_asprintf (&default_config, "%s/config", uc.service_home);
795   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "DEFAULTCONFIG",
796                                          default_config);
797   GNUNET_free (default_config);
798   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "SERVICEHOME",
799                                          uc.service_home);
800   /* make PORTs and UNIXPATHs unique */
801   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
802   /* allow connections to services from system controller host */
803   GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
804   /* enable loopback-based connections between peers */
805   GNUNET_CONFIGURATION_set_value_string (cfg, 
806                                          "nat",
807                                          "USE_LOCALADDR", "YES");
808   GNUNET_free (uc.service_home);
809   return uc.status;
810 }
811
812
813 /**
814  * Configure a GNUnet peer.  GNUnet must be installed on the local
815  * system and available in the PATH. 
816  *
817  * @param system system to use to coordinate resource usage
818  * @param cfg configuration to use; will be UPDATED (to reflect needed
819  *            changes in port numbers and paths)
820  * @param key_number number of the hostkey to use for the peer
821  * @param id identifier for the daemon, will be set, can be NULL
822  * @param emsg set to freshly allocated error message (set to NULL on success), 
823  *          can be NULL
824  * @return handle to the peer, NULL on error
825  */
826 struct GNUNET_TESTING_Peer *
827 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
828                                struct GNUNET_CONFIGURATION_Handle *cfg,
829                                uint32_t key_number,
830                                struct GNUNET_PeerIdentity *id,
831                                char **emsg)
832 {
833   struct GNUNET_TESTING_Peer *peer;
834   struct GNUNET_DISK_FileHandle *fd;
835   char *service_home;  
836   char hostkey_filename[128];
837   char *config_filename;
838   char *emsg_;
839   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
840
841   if (NULL != emsg)
842     *emsg = NULL;
843   if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg))
844   {
845     GNUNET_asprintf (&emsg_,
846                        _("Failed to create configuration for peer (not enough free ports?)\n"));
847     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
848     if (NULL != emsg)
849       *emsg = emsg_;
850     else
851       GNUNET_free (emsg_);
852     return NULL;
853   }
854   if (key_number >= system->total_hostkeys)
855   {
856     GNUNET_asprintf (&emsg_,
857                      _("You attempted to create a testbed with more than %u hosts.  Please precompute more hostkeys first.\n"),
858                      (unsigned int) system->total_hostkeys);    
859     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
860     if (NULL != emsg)
861       *emsg = emsg_;
862     else
863       GNUNET_free (emsg_);
864     return NULL;
865   }
866   pk = NULL;
867   if ((NULL != id) &&
868       (NULL == (pk = GNUNET_TESTING_hostkey_get (system, key_number, id))))
869   {
870     GNUNET_asprintf (&emsg_,
871                      _("Failed to initialize hostkey for peer %u\n"),
872                      (unsigned int) key_number);
873     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
874     if (NULL != emsg)
875       *emsg = emsg_;
876     else
877       GNUNET_free (emsg_);
878     return NULL;
879   }
880   if (NULL != pk)
881     GNUNET_CRYPTO_rsa_key_free (pk);
882   GNUNET_assert (GNUNET_OK == 
883                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
884                                                         "SERVICEHOME",
885                                                         &service_home));
886   GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/.hostkey",
887                    service_home);
888   GNUNET_free (service_home);
889   fd = GNUNET_DISK_file_open (hostkey_filename,
890                               GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
891                               GNUNET_DISK_PERM_USER_READ 
892                               | GNUNET_DISK_PERM_USER_WRITE);
893   if (NULL == fd)
894   {
895     GNUNET_break (0); 
896     return NULL;
897   }
898   if (GNUNET_TESTING_HOSTKEYFILESIZE !=
899       GNUNET_DISK_file_write (fd, system->hostkeys_data 
900                               + (key_number * GNUNET_TESTING_HOSTKEYFILESIZE),
901                               GNUNET_TESTING_HOSTKEYFILESIZE))
902   {
903     GNUNET_asprintf (&emsg_,
904                      _("Failed to write hostkey file for peer %u: %s\n"),
905                      (unsigned int) key_number,
906                      STRERROR (errno));
907     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
908     if (NULL != emsg)
909       *emsg = emsg_;
910     else
911       GNUNET_free (emsg_);
912     GNUNET_DISK_file_close (fd);
913     return NULL;
914   }
915   GNUNET_DISK_file_close (fd);
916   GNUNET_assert (GNUNET_OK ==
917                  GNUNET_CONFIGURATION_get_value_string 
918                  (cfg, "PATHS", "DEFAULTCONFIG", &config_filename));  
919   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
920   {
921     GNUNET_asprintf (&emsg_,
922                      _("Failed to write configuration file `%s' for peer %u: %s\n"),
923                      config_filename,
924                      (unsigned int) key_number,
925                      STRERROR (errno));
926     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
927     if (NULL != emsg)
928       *emsg = emsg_;
929     else
930       GNUNET_free (emsg_);
931     GNUNET_free (config_filename);
932     return NULL;
933   }
934   peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
935   peer->cfgfile = config_filename; /* Free in peer_destroy */
936   peer->main_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
937   peer->system = system;
938   peer->key_number = key_number;
939   return peer;
940 }
941
942
943 /**
944  * Obtain the peer identity from a peer handle.
945  *
946  * @param peer peer handle for which we want the peer's identity
947  * @param id identifier for the daemon, will be set
948  */
949 void
950 GNUNET_TESTING_peer_get_identity (const struct GNUNET_TESTING_Peer *peer,
951                                   struct GNUNET_PeerIdentity *id)
952 {
953   GNUNET_CRYPTO_rsa_key_free (GNUNET_TESTING_hostkey_get (peer->system,
954                                                           peer->key_number,
955                                                           id));
956 }
957
958
959 /**
960  * Start the peer. 
961  *
962  * @param peer peer to start
963  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
964  */
965 int
966 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
967 {
968   if (NULL != peer->main_process)
969   {
970     GNUNET_break (0);
971     return GNUNET_SYSERR;
972   }
973   GNUNET_assert (NULL != peer->cfgfile);
974   peer->main_process = GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL,
975                                                 peer->main_binary,
976                                                 peer->main_binary,
977                                                 "-c",
978                                                 peer->cfgfile,
979                                                 NULL);
980   if (NULL == peer->main_process)
981   {
982     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
983                 _("Failed to start `%s': %s\n"),
984                 peer->main_binary,
985                 STRERROR (errno));
986     return GNUNET_SYSERR;
987   }
988   return GNUNET_OK;
989 }
990
991
992 /**
993  * Stop the peer. 
994  *
995  * @param peer peer to stop
996  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
997  */
998 int
999 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
1000 {
1001   if (NULL == peer->main_process)
1002   {
1003     GNUNET_break (0);
1004     return GNUNET_SYSERR;
1005   }
1006   (void) GNUNET_OS_process_kill (peer->main_process, SIGTERM);
1007   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (peer->main_process));
1008   GNUNET_OS_process_destroy (peer->main_process);
1009   peer->main_process = NULL;
1010   return GNUNET_OK;
1011 }
1012
1013
1014 /**
1015  * Destroy the peer.  Releases resources locked during peer configuration.
1016  * If the peer is still running, it will be stopped AND a warning will be
1017  * printed (users of the API should stop the peer explicitly first).
1018  *
1019  * @param peer peer to destroy
1020  */
1021 void
1022 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
1023 {
1024   if (NULL != peer->main_process)
1025   {
1026     GNUNET_break (0);
1027     GNUNET_TESTING_peer_stop (peer);
1028   }
1029   GNUNET_free (peer->cfgfile);
1030   GNUNET_free (peer->main_binary);
1031   GNUNET_free (peer);
1032 }
1033
1034
1035 /**
1036  * Start a single peer and run a test using the testing library.
1037  * Starts a peer using the given configuration and then invokes the
1038  * given callback.  This function ALSO initializes the scheduler loop
1039  * and should thus be called directly from "main".  The testcase
1040  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
1041  *
1042  * @param testdir only the directory name without any path. This is used for
1043  *          all service homes; the directory will be created in a temporary
1044  *          location depending on the underlying OS
1045  * @param cfgfilename name of the configuration file to use;
1046  *         use NULL to only run with defaults
1047  * @param tm main function of the testcase
1048  * @param tm_cls closure for 'tm'
1049  * @return 0 on success, 1 on error
1050  */
1051 int
1052 GNUNET_TESTING_peer_run (const char *testdir,
1053                          const char *cfgfilename,
1054                          GNUNET_TESTING_TestMain tm,
1055                          void *tm_cls)
1056 {
1057   return GNUNET_TESTING_service_run (testdir, "arm",
1058                                      cfgfilename, tm, tm_cls);
1059 }
1060
1061
1062 /**
1063  * Structure for holding service data
1064  */
1065 struct ServiceContext
1066 {
1067   /**
1068    * The configuration of the peer in which the service is run
1069    */
1070   const struct GNUNET_CONFIGURATION_Handle *cfg;
1071
1072   /**
1073    * Callback to signal service startup
1074    */
1075   GNUNET_TESTING_TestMain tm;
1076   
1077   /**
1078    * The peer in which the service is run.
1079    */
1080   struct GNUNET_TESTING_Peer *peer;
1081
1082   /**
1083    * Closure for the above callback
1084    */
1085   void *tm_cls;
1086 };
1087
1088
1089 /**
1090  * Callback to be called when SCHEDULER has been started
1091  *
1092  * @param cls the ServiceContext
1093  * @param tc the TaskContext
1094  */
1095 static void
1096 service_run_main (void *cls,
1097                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1098 {
1099   struct ServiceContext *sc = cls;
1100
1101   sc->tm (sc->tm_cls, sc->cfg, sc->peer);
1102 }
1103
1104
1105 /**
1106  * Start a single service (no ARM, except of course if the given
1107  * service name is 'arm') and run a test using the testing library.
1108  * Starts a service using the given configuration and then invokes the
1109  * given callback.  This function ALSO initializes the scheduler loop
1110  * and should thus be called directly from "main".  The testcase
1111  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
1112  *
1113  * This function is useful if the testcase is for a single service
1114  * and if that service doesn't itself depend on other services.
1115  *
1116  * @param testdir only the directory name without any path. This is used for
1117  *          all service homes; the directory will be created in a temporary
1118  *          location depending on the underlying OS
1119  * @param service_name name of the service to run
1120  * @param cfgfilename name of the configuration file to use;
1121  *         use NULL to only run with defaults
1122  * @param tm main function of the testcase
1123  * @param tm_cls closure for 'tm'
1124  * @return 0 on success, 1 on error
1125  */
1126 int
1127 GNUNET_TESTING_service_run (const char *testdir,
1128                             const char *service_name,
1129                             const char *cfgfilename,
1130                             GNUNET_TESTING_TestMain tm,
1131                             void *tm_cls)
1132 {
1133   struct ServiceContext sc;
1134   struct GNUNET_TESTING_System *system;
1135   struct GNUNET_TESTING_Peer *peer;
1136   struct GNUNET_CONFIGURATION_Handle *cfg;
1137   char *binary;
1138
1139   GNUNET_log_setup (testdir, "WARNING", NULL);
1140   system = GNUNET_TESTING_system_create (testdir, "127.0.0.1", NULL);
1141   if (NULL == system)
1142     return 1;
1143   cfg = GNUNET_CONFIGURATION_create ();
1144   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
1145   {
1146     LOG (GNUNET_ERROR_TYPE_ERROR,
1147          _("Failed to load configuration from %s\n"), cfgfilename);
1148     GNUNET_CONFIGURATION_destroy (cfg);
1149     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1150     return 1;
1151   }
1152   peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
1153   if (NULL == peer)
1154   {
1155     GNUNET_CONFIGURATION_destroy (cfg);
1156     hostkeys_unload (system);
1157     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1158     return 1;
1159   }
1160   GNUNET_free (peer->main_binary);
1161   GNUNET_asprintf (&binary, "gnunet-service-%s", service_name);
1162   peer->main_binary = GNUNET_OS_get_libexec_binary_path (binary);
1163   GNUNET_free (binary);
1164   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
1165   {    
1166     GNUNET_TESTING_peer_destroy (peer);
1167     GNUNET_CONFIGURATION_destroy (cfg);
1168     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1169     return 1;
1170   }
1171   sc.cfg = cfg;
1172   sc.tm = tm;
1173   sc.tm_cls = tm_cls;
1174   sc.peer = peer;
1175   GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
1176   if ((NULL != peer->main_process) &&
1177       (GNUNET_OK != GNUNET_TESTING_peer_stop (peer)))
1178   {
1179     GNUNET_TESTING_peer_destroy (peer);
1180     GNUNET_CONFIGURATION_destroy (cfg);
1181     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1182     return 1;
1183   }
1184   GNUNET_TESTING_peer_destroy (peer);
1185   GNUNET_CONFIGURATION_destroy (cfg);
1186   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1187   return 0;
1188 }
1189
1190
1191 /**
1192  * Sometimes we use the binary name to determine which specific
1193  * test to run.  In those cases, the string after the last "_"
1194  * in 'argv[0]' specifies a string that determines the configuration
1195  * file or plugin to use.  
1196  *
1197  * This function returns the respective substring, taking care
1198  * of issues such as binaries ending in '.exe' on W32.
1199  *
1200  * @param argv0 the name of the binary
1201  * @return string between the last '_' and the '.exe' (or the end of the string),
1202  *         NULL if argv0 has no '_' 
1203  */
1204 char *
1205 GNUNET_TESTING_get_testname_from_underscore (const char *argv0)
1206 {
1207   size_t slen = strlen (argv0) + 1;
1208   char sbuf[slen];
1209   char *ret;
1210   char *dot;
1211
1212   memcpy (sbuf, argv0, slen);
1213   ret = strrchr (sbuf, '_');
1214   if (NULL == ret)
1215     return NULL;
1216   ret++; /* skip underscore */
1217   dot = strchr (ret, '.');
1218   if (NULL != dot)
1219     *dot = '\0';
1220   return GNUNET_strdup (ret);
1221 }
1222
1223
1224 /* end of testing.c */