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