(no commit message)
[oweals/gnunet.git] / src / transport / test_transport_api_unreliability.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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 transport/test_transport_api_unreliability.c
22  * @brief test case for transports; ensures messages get
23  *        through, regardless of order
24  *
25  * This test case serves as a base for unreliable
26  * transport test cases to check that the transports
27  * achieve reliable message delivery.
28  */
29 #include "platform.h"
30 #include "gnunet_common.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_getopt_lib.h"
33 #include "gnunet_os_lib.h"
34 #include "gnunet_program_lib.h"
35 #include "gnunet_scheduler_lib.h"
36 #include "gnunet_server_lib.h"
37 #include "gnunet_transport_service.h"
38 #include "gauger.h"
39 #include "transport.h"
40
41 #define VERBOSE GNUNET_NO
42
43 #define VERBOSE_ARM GNUNET_NO
44
45 #define START_ARM GNUNET_YES
46
47 /**
48  * Note that this value must not significantly exceed
49  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
50  * messages may be dropped even for a reliable transport.
51  */
52 #define TOTAL_MSGS (80000 * 3) /* Total messages should be divisible by 8, so we can make a nice bitmap */
53
54 /**
55  * How long until we give up on transmitting the message?
56  */
57 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500)
58
59 #define UNRELIABLE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
60
61 #define MTYPE 12345
62
63 struct PeerContext
64 {
65   struct GNUNET_CONFIGURATION_Handle *cfg;
66   struct GNUNET_TRANSPORT_Handle *th;
67   struct GNUNET_PeerIdentity id;
68 #if START_ARM
69   struct GNUNET_OS_Process *arm_proc;
70 #endif
71 };
72
73 static struct PeerContext p1;
74
75 static struct PeerContext p2;
76
77 static int ok;
78
79 static int is_tcp;
80
81 static int is_tcp_nat;
82
83 static int is_http;
84
85 static int is_https;
86
87 static int is_udp;
88
89 static int is_unix;
90
91 static int connected;
92
93 static unsigned long long total_bytes;
94
95 static struct GNUNET_TIME_Absolute start_time;
96
97 static GNUNET_SCHEDULER_TaskIdentifier die_task;
98
99 static char *key_file_p1;
100 static char *cert_file_p1;
101
102 static char *key_file_p2;
103 static char *cert_file_p2;
104
105 static char *test_name;
106
107 static char bitmap[TOTAL_MSGS / 8];
108
109 static int msg_scheduled;
110 static int msg_sent;
111 static int msg_recv_expected;
112 static int msg_recv;
113 static struct GNUNET_TRANSPORT_TransmitHandle * transmit_handle;
114
115 #if VERBOSE
116 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
117 #else
118 #define OKPP do { ok++; } while (0)
119 #endif
120
121 /**
122  * Sets a bit active in the bitmap.
123  *
124  * @param bitIdx which bit to set
125  */
126 static void
127 set_bit (unsigned int bitIdx)
128 {
129   size_t arraySlot;
130   unsigned int targetBit;
131   if (bitIdx >= sizeof(bitmap) * 8)
132     {
133       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "tried to set bit %d of %d(!?!?)\n", bitIdx, sizeof(bitmap) * 8);
134       return;
135     }
136   arraySlot = bitIdx / 8;
137   targetBit = (1L << (bitIdx % 8));
138   bitmap[arraySlot] |= targetBit;
139 }
140
141 /**
142  * Obtain a bit from bitmap.
143  * @param map the bitmap
144  * @param bit index from bitmap
145  *
146  * @return Bit \a bit from hashcode \a code
147  */
148 int
149 get_bit (const char *map, unsigned int bit)
150 {
151   if (bit >= TOTAL_MSGS)
152     {
153       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "get bit %d of %d(!?!?)\n", bit, sizeof(bitmap) * 8);
154       return 0;
155     }
156   return ((map)[bit >> 3] & (1 << (bit & 7))) > 0;
157 }
158
159 static void
160 end ()
161 {
162   unsigned long long delta;
163   int i;
164   int result;
165   char *value_name;
166
167   result = 0;
168   for (i = 0; i < TOTAL_MSGS; i++)
169     {
170       if (get_bit(bitmap, i) == 0)
171         {
172           GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Did not receive message %d\n", i);
173           result = -1;
174         }
175     }
176
177   GNUNET_SCHEDULER_cancel (die_task);
178   die_task = GNUNET_SCHEDULER_NO_TASK;
179 #if VERBOSE
180   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from transports!\n");
181 #endif
182   GNUNET_TRANSPORT_disconnect (p1.th);
183   GNUNET_TRANSPORT_disconnect (p2.th);
184 #if VERBOSE
185   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
186               "Transports disconnected, returning success!\n");
187 #endif
188   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value;
189   GNUNET_asprintf(&value_name, "unreliable_%s", test_name);
190   GAUGER ("TRANSPORT", value_name, (int)(total_bytes * 1000 / 1024 /delta), "kb/s");
191   GNUNET_free(value_name);
192   fprintf (stderr,
193            "\nThroughput was %llu kb/s\n",
194            total_bytes * 1000 / 1024 / delta);
195   ok = result;
196
197 }
198
199 static void
200 end_unreliably ()
201 {
202   unsigned long long delta;
203   int i;
204   int num_failed;
205   char *value_name;
206   num_failed = 0;
207   for (i = 0; i < TOTAL_MSGS; i++)
208     {
209       if (get_bit(bitmap, i) == 0)
210         {
211           GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Did not receive message %d\n", i);
212           num_failed++;
213         }
214     }
215
216   die_task = GNUNET_SCHEDULER_NO_TASK;
217 #if VERBOSE
218   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from transports!\n");
219 #endif
220   GNUNET_TRANSPORT_disconnect (p1.th);
221   GNUNET_TRANSPORT_disconnect (p2.th);
222 #if VERBOSE
223   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
224               "Transports disconnected, returning success!\n");
225 #endif
226   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value;
227   fprintf (stderr,
228            "\nThroughput was %llu kb/s\n",
229            total_bytes * 1000 / 1024 / delta);
230   GNUNET_asprintf(&value_name, "unreliable_%s", test_name);
231   GAUGER ("TRANSPORT", value_name, (int)(total_bytes * 1000 / 1024 /delta), "kb/s");
232   GNUNET_free(value_name);
233   GNUNET_asprintf(&value_name, "unreliable_failed_%s", test_name);
234   GAUGER ("TRANSPORT", value_name, (int)num_failed, "msgs");
235   GNUNET_free(value_name);
236   GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Had %d failed messages!\n", num_failed);
237   ok = 0;
238
239 }
240
241
242
243 static void
244 stop_arm (struct PeerContext *p)
245 {
246 #if START_ARM
247   if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
248     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
249   GNUNET_OS_process_wait (p->arm_proc);
250   GNUNET_OS_process_close (p->arm_proc);
251   p->arm_proc = NULL;
252 #endif
253   GNUNET_CONFIGURATION_destroy (p->cfg);
254 }
255
256
257 static void
258 end_badly (void *cls,
259            const struct GNUNET_SCHEDULER_TaskContext *tc)
260 {
261   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
262                   "Reliability failed: \nLast message sent %u \nNext message scheduled %u\nLast message received %u\nMessage expected %u \n ", msg_sent, msg_scheduled, msg_recv, msg_recv_expected);
263   GNUNET_break (0);
264   GNUNET_TRANSPORT_disconnect (p1.th);
265   GNUNET_TRANSPORT_disconnect (p2.th);
266   ok = 1;
267 }
268
269
270 struct TestMessage
271 {
272   struct GNUNET_MessageHeader header;
273   uint32_t num;
274 };
275
276
277 static unsigned int
278 get_size (unsigned int iter)
279 {
280   unsigned int ret;
281
282   if (iter < 60000)
283     return iter + sizeof (struct TestMessage);
284   ret = (iter * iter * iter);
285   return sizeof (struct TestMessage) + (ret % 60000);
286 }
287
288
289 static void
290 notify_receive (void *cls,
291                 const struct GNUNET_PeerIdentity *peer,
292                 const struct GNUNET_MessageHeader *message,
293                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
294                 uint32_t ats_count)
295 {
296   static int n;
297   unsigned int s;
298   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
299   const struct TestMessage *hdr;
300
301   hdr = (const struct TestMessage*) message;
302
303   if (MTYPE != ntohs (message->type))
304     return;
305   msg_recv_expected = n;
306   msg_recv = ntohl(hdr->num);
307   s = get_size (ntohl(hdr->num));
308
309   if (ntohs (message->size) != s)
310     {
311       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
312                   "Expected message %u of size %u, got %u bytes of message %u\n",
313                   ntohl(hdr->num), s,
314                   ntohs (message->size),
315                   ntohl (hdr->num));
316       GNUNET_SCHEDULER_cancel (die_task);
317       die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
318       return;
319     }
320
321   memset (cbuf, ntohl(hdr->num), s - sizeof (struct TestMessage));
322   if (0 != memcmp (cbuf,
323                    &hdr[1],
324                    s - sizeof (struct TestMessage)))
325     {
326       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
327                   "Expected message %u with bits %u, but body did not match\n",
328                   ntohl(hdr->num), (unsigned char) n);
329       GNUNET_SCHEDULER_cancel (die_task);
330       die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
331       return;
332     }
333 #if VERBOSE
334   if (ntohl(hdr->num) % 5 == 0)
335     {
336       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337                   "Got message %u of size %u\n",
338                   ntohl (hdr->num),
339                   ntohs (message->size));
340     }
341 #endif
342   n++;
343   set_bit(ntohl(hdr->num));
344   if (0 == (n % (5000)))
345     {
346       fprintf (stderr, ".");
347       GNUNET_SCHEDULER_cancel (die_task);
348       die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
349                                                &end_badly,
350                                                NULL);
351     }
352   if (n == TOTAL_MSGS)
353     end ();
354 }
355
356
357 static size_t
358 notify_ready (void *cls, size_t size, void *buf)
359 {
360   static int n;
361   char *cbuf = buf;
362   struct TestMessage hdr;
363   unsigned int s;
364   unsigned int ret;
365
366   if (buf == NULL)
367     {
368       GNUNET_break (0);
369       ok = 42;
370       return 0;
371     }
372   ret = 0;
373   s = get_size (n);
374   GNUNET_assert (size >= s);
375   GNUNET_assert (buf != NULL);
376   cbuf = buf;
377   do
378     {
379       hdr.header.size = htons (s);
380       hdr.header.type = htons (MTYPE);
381       hdr.num = htonl (n);
382       msg_sent = n;
383       memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
384       ret += sizeof (struct TestMessage);
385       memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
386       ret += s - sizeof (struct TestMessage);
387 #if VERBOSE
388       if (n % 5000 == 0)
389         {
390           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
391                       "Sending message %u of size %u\n",
392                       n,
393                       s);
394         }
395 #endif
396       n++;
397       s = get_size (n);
398       if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
399         break; /* sometimes pack buffer full, sometimes not */
400     }
401   while (size - ret >= s);
402   if (n < TOTAL_MSGS)
403   {
404     GNUNET_TRANSPORT_notify_transmit_ready (p2.th,
405                                             &p1.id,
406                                             s, 0, TIMEOUT,
407                                             &notify_ready,
408                                             NULL);
409     msg_scheduled = n;
410   }
411   else
412     {
413       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "All messages scheduled to be sent!!\n");
414       GNUNET_SCHEDULER_cancel(die_task);
415       die_task = GNUNET_SCHEDULER_add_delayed (UNRELIABLE_TIMEOUT, &end_unreliably, NULL);
416     }
417   if (n % 5000 == 0)
418     {
419       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
420                   "Returning total message block of size %u\n",
421                   ret);
422     }
423   total_bytes += ret;
424   return ret;
425 }
426
427
428 static void
429 notify_connect (void *cls,
430                 const struct GNUNET_PeerIdentity *peer,
431                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
432                 uint32_t ats_count)
433 {
434   if (cls == &p1)
435     {
436       GNUNET_TRANSPORT_set_quota (p1.th,
437                                   &p2.id,
438                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
439                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
440                                   GNUNET_TIME_UNIT_FOREVER_REL,
441                                   NULL, NULL);
442       start_time = GNUNET_TIME_absolute_get ();
443       connected++;
444     }
445   else
446     {
447       GNUNET_TRANSPORT_set_quota (p2.th,
448                                   &p1.id,
449                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
450                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
451                                   GNUNET_TIME_UNIT_FOREVER_REL,
452                                   NULL, NULL);
453       connected++;
454     }
455
456   if (connected == 2)
457     {
458
459           if ((transmit_handle!=NULL) && (cls == NULL))
460                  GNUNET_TRANSPORT_notify_transmit_ready_cancel(transmit_handle);
461           if ((transmit_handle!=NULL) && (cls == &transmit_handle))
462                  transmit_handle=NULL;
463       GNUNET_TRANSPORT_notify_transmit_ready (p2.th,
464                                               &p1.id,
465                                               get_size (0), 0, TIMEOUT,
466                                               &notify_ready,
467                                               NULL);
468     }
469 #if VERBOSE
470   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471               "Peer `%4s' connected to us (%p)!\n", GNUNET_i2s (peer), cls);
472 #endif
473 }
474
475
476 static void
477 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
478 {
479 #if VERBOSE
480   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481               "Peer `%4s' disconnected (%p)!\n",
482               GNUNET_i2s (peer), cls);
483 #endif
484 }
485
486
487 static void
488 setup_peer (struct PeerContext *p, const char *cfgname)
489 {
490   p->cfg = GNUNET_CONFIGURATION_create ();
491 #if START_ARM
492   p->arm_proc = GNUNET_OS_start_process (NULL, NULL,
493                                         "gnunet-service-arm",
494                                         "gnunet-service-arm",
495 #if VERBOSE_ARM
496                                         "-L", "DEBUG",
497 #endif
498                                         "-c", cfgname, NULL);
499 #endif
500   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
501
502   if (is_https)
503   {
504           struct stat sbuf;
505           if (p==&p1)
506           {
507                   if (GNUNET_CONFIGURATION_have_value (p->cfg,
508                                                                                            "transport-https", "KEY_FILE"))
509                                 GNUNET_CONFIGURATION_get_value_string (p->cfg, "transport-https", "KEY_FILE", &key_file_p1);
510                   if (key_file_p1 == NULL)
511                           GNUNET_asprintf(&key_file_p1,"https_p1.key");
512                   if (0 == stat (key_file_p1, &sbuf ))
513                   {
514                           if (0 == remove(key_file_p1))
515                               GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Successfully removed existing private key file `%s'\n",key_file_p1);
516                           else
517                                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove private key file `%s'\n",key_file_p1);
518                   }
519                   if (GNUNET_CONFIGURATION_have_value (p->cfg,"transport-https", "CERT_FILE"))
520                           GNUNET_CONFIGURATION_get_value_string (p->cfg, "transport-https", "CERT_FILE", &cert_file_p1);
521                   if (cert_file_p1 == NULL)
522                           GNUNET_asprintf(&cert_file_p1,"https_p1.cert");
523                   if (0 == stat (cert_file_p1, &sbuf ))
524                   {
525                           if (0 == remove(cert_file_p1))
526                               GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Successfully removed existing certificate file `%s'\n",cert_file_p1);
527                           else
528                                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove existing certificate file `%s'\n",cert_file_p1);
529                   }
530           }
531           else if (p==&p2)
532           {
533                   if (GNUNET_CONFIGURATION_have_value (p->cfg,
534                                                                                            "transport-https", "KEY_FILE"))
535                                 GNUNET_CONFIGURATION_get_value_string (p->cfg, "transport-https", "KEY_FILE", &key_file_p2);
536                   if (key_file_p2 == NULL)
537                           GNUNET_asprintf(&key_file_p2,"https_p2.key");
538                   if (0 == stat (key_file_p2, &sbuf ))
539                   {
540                           if (0 == remove(key_file_p2))
541                               GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Successfully removed existing private key file `%s'\n",key_file_p2);
542                           else
543                                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove private key file `%s'\n",key_file_p2);
544                   }
545                   if (GNUNET_CONFIGURATION_have_value (p->cfg,"transport-https", "CERT_FILE"))
546                           GNUNET_CONFIGURATION_get_value_string (p->cfg, "transport-https", "CERT_FILE", &cert_file_p2);
547                   if (cert_file_p2 == NULL)
548                           GNUNET_asprintf(&cert_file_p2,"https_p2.cert");
549                   if (0 == stat (cert_file_p2, &sbuf ))
550                   {
551                           if (0 == remove(cert_file_p2))
552                               GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Successfully removed existing certificate file `%s'\n",cert_file_p2);
553                           else
554                                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove existing certificate file `%s'\n",cert_file_p2);
555                   }
556           }
557   }
558
559   p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL,
560                                     p,
561                                     &notify_receive,
562                                     &notify_connect,
563                                     &notify_disconnect);
564   GNUNET_assert (p->th != NULL);
565 }
566
567 static size_t
568 notify_ready_connect (void *cls, size_t size, void *buf)
569 {
570   return 0;
571 }
572
573 static void
574 exchange_hello_last (void *cls,
575                      const struct GNUNET_MessageHeader *message)
576 {
577   struct PeerContext *me = cls;
578   transmit_handle = NULL;
579   GNUNET_TRANSPORT_get_hello_cancel (p2.th, &exchange_hello_last, me);
580 #if VERBOSE
581   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
582               "Exchanging HELLO with peer (%p)!\n", cls);
583 #endif
584   GNUNET_assert (ok >= 3);
585   OKPP;
586   GNUNET_assert (message != NULL);
587   GNUNET_assert (GNUNET_OK ==
588                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
589                                       message, &me->id));
590
591   GNUNET_assert(NULL != (transmit_handle = GNUNET_TRANSPORT_notify_transmit_ready (p2.th,
592                                           &p1.id,
593                                           sizeof (struct GNUNET_MessageHeader), 0,
594                                           TIMEOUT,
595                                           &notify_ready_connect,
596                                           &transmit_handle)));
597
598   /* both HELLOs exchanged, get ready to test transmission! */
599   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
600               "Finished exchanging HELLOs, now waiting for transmission!\n");
601 }
602
603
604 static void
605 exchange_hello (void *cls,
606                 const struct GNUNET_MessageHeader *message)
607 {
608   struct PeerContext *me = cls;
609
610   GNUNET_TRANSPORT_get_hello_cancel (p1.th, &exchange_hello, me);
611 #if VERBOSE
612   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
613               "Exchanging HELLO with peer (%p)!\n", cls);
614 #endif
615   GNUNET_assert (ok >= 2);
616   OKPP;
617   GNUNET_assert (message != NULL);
618   GNUNET_assert (GNUNET_OK ==
619                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
620                                       message, &me->id));
621
622 #if VERBOSE
623   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624               "Received HELLO size %d\n",
625               GNUNET_HELLO_size((const struct GNUNET_HELLO_Message *)message));
626 #endif
627   GNUNET_TRANSPORT_offer_hello (p2.th, message, NULL, NULL);
628   GNUNET_TRANSPORT_get_hello (p2.th, &exchange_hello_last, &p2);
629 }
630
631 /**
632  * Return the actual path to a file found in the current
633  * PATH environment variable.
634  *
635  * @param binary the name of the file to find
636  */
637 static char *
638 get_path_from_PATH (char *binary)
639 {
640   char *path;
641   char *pos;
642   char *end;
643   char *buf;
644   const char *p;
645
646   p = getenv ("PATH");
647   if (p == NULL)
648     {
649       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
650                   _("PATH environment variable is unset.\n"));
651       return NULL;
652     }
653   path = GNUNET_strdup (p);     /* because we write on it */
654   buf = GNUNET_malloc (strlen (path) + 20);
655   pos = path;
656
657   while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
658     {
659       *end = '\0';
660       sprintf (buf, "%s/%s", pos, binary);
661       if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
662         {
663           GNUNET_free (path);
664           return buf;
665         }
666       pos = end + 1;
667     }
668   sprintf (buf, "%s/%s", pos, binary);
669   if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
670     {
671       GNUNET_free (path);
672       return buf;
673     }
674   GNUNET_free (buf);
675   GNUNET_free (path);
676   return NULL;
677 }
678
679 /**
680  * Check whether the suid bit is set on a file.
681  * Attempts to find the file using the current
682  * PATH environment variable as a search path.
683  *
684  * @param binary the name of the file to check
685  *
686  * @return GNUNET_YES if the binary is found and
687  *         can be run properly, GNUNET_NO otherwise
688  */
689 static int
690 check_gnunet_nat_binary(char *binary)
691 {
692   struct stat statbuf;
693   char *p;
694 #ifdef MINGW
695   SOCKET rawsock;
696 #endif
697
698 #ifdef MINGW
699   char *binaryexe;
700   GNUNET_asprintf (&binaryexe, "%s.exe", binary);
701   p = get_path_from_PATH (binaryexe);
702   free (binaryexe);
703 #else
704   p = get_path_from_PATH (binary);
705 #endif
706   if (p == NULL)
707     {
708       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
709                   _("Could not find binary `%s' in PATH!\n"),
710                   binary);
711       return GNUNET_NO;
712     }
713   if (0 != STAT (p, &statbuf))
714     {
715       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
716                   _("stat (%s) failed: %s\n"),
717                   p,
718                   STRERROR (errno));
719       GNUNET_free (p);
720       return GNUNET_SYSERR;
721     }
722   GNUNET_free (p);
723 #ifndef MINGW
724   if ( (0 != (statbuf.st_mode & S_ISUID)) &&
725        (statbuf.st_uid == 0) )
726     return GNUNET_YES;
727   return GNUNET_NO;
728 #else
729   rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
730   if (INVALID_SOCKET == rawsock)
731     {
732       DWORD err = GetLastError ();
733       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
734                   "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) have failed! GLE = %d\n", err);
735       return GNUNET_NO; /* not running as administrator */
736     }
737   closesocket (rawsock);
738   return GNUNET_YES;
739 #endif
740 }
741
742 static void
743 run (void *cls,
744      char *const *args,
745      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
746 {
747   GNUNET_assert (ok == 1);
748   OKPP;
749   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
750                                            &end_badly,
751                                            NULL);
752   if (is_tcp)
753     {
754       setup_peer (&p1, "test_transport_api_tcp_peer1.conf");
755       setup_peer (&p2, "test_transport_api_tcp_peer2.conf");
756     }
757   else if (is_http)
758     {
759       setup_peer (&p1, "test_transport_api_rel_http_peer1.conf");
760       setup_peer (&p2, "test_transport_api_rel_http_peer2.conf");
761     }
762   else if (is_https)
763     {
764       setup_peer (&p1, "test_transport_api_rel_https_peer1.conf");
765       setup_peer (&p2, "test_transport_api_rel_https_peer2.conf");
766     }
767   else if (is_udp)
768     {
769       setup_peer (&p1, "test_transport_api_udp_peer1.conf");
770       setup_peer (&p2, "test_transport_api_udp_peer2.conf");
771     }
772   else if (is_unix)
773     {
774       setup_peer (&p1, "test_transport_api_unix_peer1.conf");
775       setup_peer (&p2, "test_transport_api_unix_peer2.conf");
776     }
777   else if (is_tcp_nat)
778     {
779       setup_peer (&p1, "test_transport_api_tcp_nat_peer1.conf");
780       setup_peer (&p2, "test_transport_api_tcp_nat_peer2.conf");
781     }
782   else
783     GNUNET_assert (0);
784   GNUNET_assert(p1.th != NULL);
785   GNUNET_assert(p2.th != NULL);
786   GNUNET_TRANSPORT_get_hello (p1.th, &exchange_hello, &p1);
787 }
788
789
790 static int
791 check ()
792 {
793   char *const argv[] = { "test-transport-api-reliability",
794     "-c",
795     "test_transport_api_data.conf",
796 #if VERBOSE
797     "-L", "DEBUG",
798 #endif
799     NULL
800   };
801   struct GNUNET_GETOPT_CommandLineOption options[] = {
802     GNUNET_GETOPT_OPTION_END
803   };
804
805 #if WRITECONFIG
806   setTransportOptions("test_transport_api_data.conf");
807 #endif
808   ok = 1;
809
810   if ((GNUNET_YES == is_tcp_nat) && (check_gnunet_nat_binary("gnunet-nat-server") != GNUNET_YES))
811     {
812       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Not running NAT test case, binaries not properly installed.\n");
813       return 0;
814     }
815
816   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
817                       argv, "test-transport-api-reliability", "nohelp",
818                       options, &run, &ok);
819   stop_arm (&p1);
820   stop_arm (&p2);
821
822   if (is_https)
823   {
824     struct stat sbuf;
825     if (0 == stat (cert_file_p1, &sbuf ))
826     {
827       if (0 == remove(cert_file_p1))
828         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully removed existing certificate file `%s'\n",cert_file_p1);
829       else
830         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove certfile `%s'\n",cert_file_p1);
831     }
832
833     if (0 == stat (key_file_p1, &sbuf ))
834     {
835       if (0 == remove(key_file_p1))
836         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully removed private key file `%s'\n",key_file_p1);
837       else
838         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to private key file `%s'\n",key_file_p1);
839     }
840
841     if (0 == stat (cert_file_p2, &sbuf ))
842     {
843       if (0 == remove(cert_file_p2))
844         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully removed existing certificate file `%s'\n",cert_file_p2);
845       else
846         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove certfile `%s'\n",cert_file_p2);
847     }
848
849     if (0 == stat (key_file_p2, &sbuf ))
850     {
851       if (0 == remove(key_file_p2))
852         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully removed private key file `%s'\n",key_file_p2);
853       else
854         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to private key file `%s'\n",key_file_p2);
855     }
856     GNUNET_free(key_file_p1);
857     GNUNET_free(key_file_p2);
858     GNUNET_free(cert_file_p1);
859     GNUNET_free(cert_file_p2);
860   }
861
862   return ok;
863 }
864
865
866 int
867 main (int argc, char *argv[])
868 {
869   int ret;
870 #ifdef MINGW
871   return GNUNET_SYSERR;
872 #endif
873
874   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-transport-peer-1");
875   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-transport-peer-2");
876
877   if (strstr(argv[0], "tcp_nat") != NULL)
878     {
879       is_tcp_nat = GNUNET_YES;
880       GNUNET_asprintf(&test_name, "tcp_nat");
881     }
882   else if (strstr(argv[0], "tcp") != NULL)
883     {
884       is_tcp = GNUNET_YES;
885       GNUNET_asprintf(&test_name, "tcp");
886     }
887   else if (strstr(argv[0], "https") != NULL)
888     {
889       is_https = GNUNET_YES;
890       GNUNET_asprintf(&test_name, "https");
891     }
892   else if (strstr(argv[0], "http") != NULL)
893     {
894       is_http = GNUNET_YES;
895       GNUNET_asprintf(&test_name, "http");
896     }
897   else if (strstr(argv[0], "udp") != NULL)
898     {
899       is_udp = GNUNET_YES;
900       GNUNET_asprintf(&test_name, "udp");
901     }
902   else if (strstr(argv[0], "unix") != NULL)
903     {
904       is_unix = GNUNET_YES;
905       GNUNET_asprintf(&test_name, "unix");
906     }
907   GNUNET_log_setup ("test-transport-api-reliability",
908 #if VERBOSE
909                     "DEBUG",
910 #else
911                     "WARNING",
912 #endif
913                     NULL);
914   ret = check ();
915   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-transport-peer-1");
916   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-transport-peer-2");
917   GNUNET_free_non_null(test_name);
918   return ret;
919 }
920
921 /* end of test_transport_api_reliability.c */