indentation
[oweals/gnunet.git] / src / transport / test_transport_api.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.c
22  * @brief base test case for transport implementations
23  *
24  * This test case serves as a base for tcp, udp, and udp-nat
25  * transport test cases.  Based on the executable being run
26  * the correct test case will be performed.  Conservation of
27  * C code apparently.
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_transport_service.h"
37 #include "transport.h"
38 #include "transport-testing.h"
39
40 #define VERBOSE GNUNET_NO
41
42 #define VERBOSE_ARM GNUNET_NO
43
44 #define START_ARM GNUNET_YES
45
46 /**
47  * How long until we give up on transmitting the message?
48  */
49 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
50
51 /**
52  * How long until we give up on transmitting the message?
53  */
54 #define TIMEOUT_TRANSMIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
55
56 #define MTYPE 12345
57
58 static int ok;
59
60 static GNUNET_SCHEDULER_TaskIdentifier die_task;
61
62 struct PeerContext *p1;
63
64 struct PeerContext *p2;
65
66 struct GNUNET_TRANSPORT_TransmitHandle *th;
67
68 char *cfg_file_p1;
69
70 char *cfg_file_p2;
71
72 #if VERBOSE
73 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
74 #else
75 #define OKPP do { ok++; } while (0)
76 #endif
77
78
79 static void
80 end ()
81 {
82   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping peers\n");
83
84   if (die_task != GNUNET_SCHEDULER_NO_TASK)
85     GNUNET_SCHEDULER_cancel (die_task);
86
87   if (th != NULL)
88     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
89   th = NULL;
90
91   GNUNET_TRANSPORT_TESTING_stop_peer (p1);
92   GNUNET_TRANSPORT_TESTING_stop_peer (p2);
93 }
94
95 static void
96 end_badly ()
97 {
98   if (die_task != GNUNET_SCHEDULER_NO_TASK)
99     GNUNET_SCHEDULER_cancel (die_task);
100
101   die_task = GNUNET_SCHEDULER_NO_TASK;
102   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Fail! Stopping peers\n");
103
104   if (th != NULL)
105     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
106   th = NULL;
107
108   if (p1 != NULL)
109     GNUNET_TRANSPORT_TESTING_stop_peer (p1);
110   if (p2 != NULL)
111     GNUNET_TRANSPORT_TESTING_stop_peer (p2);
112
113   ok = GNUNET_SYSERR;
114 }
115
116
117 static void
118 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
119                 const struct GNUNET_MessageHeader *message,
120                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
121                 uint32_t ats_count)
122 {
123   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
124               "Received message of type %d from peer %s!\n",
125               ntohs (message->type), GNUNET_i2s (peer));
126
127   if ((MTYPE == ntohs (message->type)) &&
128       (sizeof (struct GNUNET_MessageHeader) == ntohs (message->size)))
129   {
130     ok = 0;
131     end ();
132   }
133   else
134   {
135     GNUNET_break (0);
136     ok = 1;
137     end ();
138   }
139 }
140
141
142 static size_t
143 notify_ready (void *cls, size_t size, void *buf)
144 {
145   struct PeerContext *p = cls;
146   struct GNUNET_MessageHeader *hdr;
147
148   th = NULL;
149
150   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
151               "Transmitting message with %u bytes to peer %s\n",
152               sizeof (struct GNUNET_MessageHeader), GNUNET_i2s (&p->id));
153   GNUNET_assert (size >= 256);
154
155   if (buf != NULL)
156   {
157     hdr = buf;
158     hdr->size = htons (sizeof (struct GNUNET_MessageHeader));
159     hdr->type = htons (MTYPE);
160   }
161   return sizeof (struct GNUNET_MessageHeader);
162 }
163
164
165 static void
166 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
167                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
168                 uint32_t ats_count)
169 {
170   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' connected to us (%p)!\n",
171               GNUNET_i2s (peer), cls);
172 }
173
174
175 static void
176 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
177 {
178   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' disconnected (%p)!\n",
179               GNUNET_i2s (peer), cls);
180 }
181
182 static void
183 sendtask ()
184 {
185   th = GNUNET_TRANSPORT_notify_transmit_ready (p1->th, &p2->id, 256, 0, TIMEOUT,
186                                                &notify_ready, &p1);
187 }
188
189 static void
190 testing_connect_cb (struct PeerContext *p1, struct PeerContext *p2, void *cls)
191 {
192   char *p1_c = strdup (GNUNET_i2s (&p1->id));
193
194   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected: %s <-> %s\n", p1_c,
195               GNUNET_i2s (&p2->id));
196   GNUNET_free (p1_c);
197
198   // FIXME: THIS IS REQUIRED! SEEMS TO BE A BUG!
199   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &sendtask, NULL);
200 }
201
202 static void
203 run (void *cls, char *const *args, const char *cfgfile,
204      const struct GNUNET_CONFIGURATION_Handle *cfg)
205 {
206   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
207
208   p1 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p1, &notify_receive,
209                                             &notify_connect, &notify_disconnect,
210                                             NULL);
211   p2 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p2, &notify_receive,
212                                             &notify_connect, &notify_disconnect,
213                                             NULL);
214
215   GNUNET_TRANSPORT_TESTING_connect_peers (p1, p2, &testing_connect_cb, NULL);
216 }
217
218 static int
219 check ()
220 {
221   static char *const argv[] = { "test-transport-api",
222     "-c",
223     "test_transport_api_data.conf",
224 #if VERBOSE
225     "-L", "DEBUG",
226 #endif
227     NULL
228   };
229   static struct GNUNET_GETOPT_CommandLineOption options[] = {
230     GNUNET_GETOPT_OPTION_END
231   };
232
233 #if WRITECONFIG
234   setTransportOptions ("test_transport_api_data.conf");
235 #endif
236   ok = 1;
237   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
238                       "test-transport-api", "nohelp", options, &run, &ok);
239
240   return ok;
241 }
242
243 /**
244  * Return the actual path to a file found in the current
245  * PATH environment variable.
246  *
247  * @param binary the name of the file to find
248  */
249 static char *
250 get_path_from_PATH (char *binary)
251 {
252   char *path;
253   char *pos;
254   char *end;
255   char *buf;
256   const char *p;
257
258   p = getenv ("PATH");
259   if (p == NULL)
260   {
261     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
262                 _("PATH environment variable is unset.\n"));
263     return NULL;
264   }
265   path = GNUNET_strdup (p);     /* because we write on it */
266   buf = GNUNET_malloc (strlen (path) + 20);
267   pos = path;
268
269   while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
270   {
271     *end = '\0';
272     sprintf (buf, "%s/%s", pos, binary);
273     if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
274     {
275       GNUNET_free (path);
276       return buf;
277     }
278     pos = end + 1;
279   }
280   sprintf (buf, "%s/%s", pos, binary);
281   if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
282   {
283     GNUNET_free (path);
284     return buf;
285   }
286   GNUNET_free (buf);
287   GNUNET_free (path);
288   return NULL;
289 }
290
291 /**
292  * Check whether the suid bit is set on a file.
293  * Attempts to find the file using the current
294  * PATH environment variable as a search path.
295  *
296  * @param binary the name of the file to check
297  *
298  * @return GNUNET_YES if the binary is found and
299  *         can be run properly, GNUNET_NO otherwise
300  */
301 static int
302 check_gnunet_nat_binary (char *binary)
303 {
304   struct stat statbuf;
305   char *p;
306
307 #ifdef MINGW
308   SOCKET rawsock;
309 #endif
310
311 #ifdef MINGW
312   char *binaryexe;
313
314   GNUNET_asprintf (&binaryexe, "%s.exe", binary);
315   p = get_path_from_PATH (binaryexe);
316   free (binaryexe);
317 #else
318   p = get_path_from_PATH (binary);
319 #endif
320   if (p == NULL)
321   {
322     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
323                 _("Could not find binary `%s' in PATH!\n"), binary);
324     return GNUNET_NO;
325   }
326   if (0 != STAT (p, &statbuf))
327   {
328     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("stat (%s) failed: %s\n"), p,
329                 STRERROR (errno));
330     GNUNET_free (p);
331     return GNUNET_SYSERR;
332   }
333   GNUNET_free (p);
334 #ifndef MINGW
335   if ((0 != (statbuf.st_mode & S_ISUID)) && (statbuf.st_uid == 0))
336     return GNUNET_YES;
337   return GNUNET_NO;
338 #else
339   rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
340   if (INVALID_SOCKET == rawsock)
341   {
342     DWORD err = GetLastError ();
343
344     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
345                 "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) have failed! GLE = %d\n",
346                 err);
347     return GNUNET_NO;           /* not running as administrator */
348   }
349   closesocket (rawsock);
350   return GNUNET_YES;
351 #endif
352 }
353
354 int
355 main (int argc, char *argv[])
356 {
357   int ret;
358
359   GNUNET_log_setup ("test-transport-api",
360 #if VERBOSE
361                     "DEBUG",
362 #else
363                     "WARNING",
364 #endif
365                     NULL);
366
367   char *pch = strdup (argv[0]);
368   char *backup = pch;
369   char *filename = NULL;
370   char *dotexe;
371
372   /* get executable filename */
373   pch = strtok (pch, "/");
374   while (pch != NULL)
375   {
376     pch = strtok (NULL, "/");
377     if (pch != NULL)
378       filename = pch;
379   }
380   /* remove "lt-" */
381   filename = strstr (filename, "tes");
382   if (NULL != (dotexe = strstr (filename, ".exe")))
383     dotexe[0] = '\0';
384
385   /* create cfg filename */
386   GNUNET_asprintf (&cfg_file_p1, "%s_peer1.conf", filename);
387   GNUNET_asprintf (&cfg_file_p2, "%s_peer2.conf", filename);
388   GNUNET_free (backup);
389
390   if (strstr (argv[0], "tcp_nat") != NULL)
391   {
392     if (GNUNET_YES != check_gnunet_nat_binary ("gnunet-nat-server"))
393     {
394       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
395                   "`%s' not properly installed, cannot run NAT test!\n",
396                   "gnunet-nat-server");
397       return 0;
398     }
399   }
400   else if (strstr (argv[0], "udp_nat") != NULL)
401   {
402     if (GNUNET_YES != check_gnunet_nat_binary ("gnunet-nat-server"))
403     {
404       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
405                   "`%s' not properly installed, cannot run NAT test!\n",
406                   "gnunet-nat-server");
407       return 0;
408     }
409   }
410
411   ret = check ();
412
413   GNUNET_free (cfg_file_p1);
414   GNUNET_free (cfg_file_p2);
415
416   return ret;
417 }
418
419 /* end of test_transport_api.c */