towards having sending in transport-testing API (not yet flexible enough)
[oweals/gnunet.git] / src / transport / gnunet-helper-transport-wlan-dummy.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2010, 2012 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19  */
20 /**
21  * @file transport/gnunet-helper-transport-wlan-dummy.c
22  * @brief helper for the testcases for plugin_transport_wlan.c
23  * @author David Brodski
24  */
25 #include "platform.h"
26 #include "gnunet_protocols.h"
27 #include "gnunet_util_lib.h"
28 #include "plugin_transport_wlan.h"
29
30 /**
31  * Name of the fifo to use for IPC with the other dummy process.
32  */
33 #define FIFO_FILE1 "/tmp/test-transport/api-wlan-p1/WLAN_FIFO_in"
34
35 /**
36  * Name of the fifo to use for IPC with the other dummy process.
37  */
38 #define FIFO_FILE2 "/tmp/test-transport/api-wlan-p1/WLAN_FIFO_out"
39
40 /**
41  * Maximum size of a message allowed in either direction
42  * (used for our receive and sent buffers).
43  */
44 #define MAXLINE 4096
45
46
47 /**
48  * IO buffer used for buffering data in transit.
49  */
50 struct SendBuffer
51 {
52
53   /**
54    * How many bytes that were stored in 'buf' did we already write to the
55    * destination?  Always smaller than 'size'.
56    */
57   size_t pos;
58
59   /**
60    * How many bytes of data are stored in 'buf' for transmission right now?
61    * Data always starts at offset 0 and extends to 'size'.
62    */
63   size_t size;
64
65   /**
66    * Buffered data; twice the maximum allowed message size as we add some
67    * headers.
68    */
69   char buf[MAXLINE * 2];
70 };
71
72
73 /**
74  * Flag set to 1 if we are to terminate, otherwise 0.
75  */
76 static int closeprog;
77
78
79 /**
80  * We're being killed, clean up.
81  *
82  * @param sig killing signal
83  */
84 static void
85 sigfunc (int sig)
86 {
87   closeprog = 1;
88   (void) unlink (FIFO_FILE1);
89   (void) unlink (FIFO_FILE2);
90 }
91
92
93 /**
94  * Create control message for plugin
95  *
96  * @param buffer pointer to buffer for the message
97  * @param mac pointer to the mac address
98  * @return number of bytes written
99  */
100 static int
101 send_mac_to_plugin (char *buffer, struct GNUNET_TRANSPORT_WLAN_MacAddress *mac)
102 {
103
104   struct GNUNET_TRANSPORT_WLAN_HelperControlMessage macmsg;
105
106   GNUNET_memcpy (&macmsg.mac,
107           (char *) mac,
108           sizeof (struct GNUNET_TRANSPORT_WLAN_MacAddress));
109   macmsg.hdr.size = htons (sizeof (struct GNUNET_TRANSPORT_WLAN_HelperControlMessage));
110   macmsg.hdr.type = htons (GNUNET_MESSAGE_TYPE_WLAN_HELPER_CONTROL);
111   GNUNET_memcpy (buffer,
112           &macmsg,
113           sizeof (struct GNUNET_TRANSPORT_WLAN_HelperControlMessage));
114   return sizeof (struct GNUNET_TRANSPORT_WLAN_HelperControlMessage);
115 }
116
117
118 /**
119  * We got a message from the FIFO, check it, convert the message
120  * type to the output forward and copy it to the buffer for stdout.
121  *
122  * @param cls the 'struct SendBuffer' to copy the converted message to
123  * @param client unused
124  * @param hdr inbound message from the FIFO
125  */
126 static int
127 stdin_send (void *cls, void *client, const struct GNUNET_MessageHeader *hdr)
128 {
129   struct SendBuffer *write_pout = cls;
130   const struct GNUNET_TRANSPORT_WLAN_RadiotapSendMessage *in;
131   size_t payload_size;
132   struct GNUNET_TRANSPORT_WLAN_RadiotapReceiveMessage newheader;
133   uint16_t sendsize;
134
135   sendsize = ntohs (hdr->size);
136   in = (const struct GNUNET_TRANSPORT_WLAN_RadiotapSendMessage *) hdr;
137   if ( (GNUNET_MESSAGE_TYPE_WLAN_DATA_TO_HELPER != ntohs (hdr->type)) ||
138        (sizeof (struct GNUNET_TRANSPORT_WLAN_RadiotapSendMessage) > sendsize) )
139   {
140     FPRINTF (stderr, "%s", "Received malformed message\n");
141     exit (1);
142   }
143   payload_size = sendsize - sizeof (struct GNUNET_TRANSPORT_WLAN_RadiotapSendMessage);
144   if ((payload_size + sizeof (struct GNUNET_TRANSPORT_WLAN_RadiotapReceiveMessage) + write_pout->size) > MAXLINE * 2)
145   {
146     FPRINTF (stderr, "%s",  "Packet too big for buffer\n");
147     exit (1);
148   }
149   memset (&newheader, 0, sizeof (newheader));
150   newheader.header.size = htons (payload_size + sizeof (newheader));
151   newheader.header.type = htons (GNUNET_MESSAGE_TYPE_WLAN_DATA_FROM_HELPER);
152   newheader.frame = in->frame;
153   GNUNET_memcpy (write_pout->buf + write_pout->size,
154           &newheader,
155           sizeof (newheader));
156   write_pout->size += sizeof (newheader);
157   GNUNET_memcpy (write_pout->buf + write_pout->size,
158           &in[1],
159           payload_size);
160   write_pout->size += payload_size;
161   return GNUNET_OK;
162 }
163
164
165 /**
166  * We read a full message from stdin.  Copy it to our send buffer.
167  *
168  * @param cls the 'struct SendBuffer' to copy to
169  * @param client unused
170  * @param hdr the message we received to copy to the buffer
171  */
172 static int
173 file_in_send (void *cls, void *client, const struct GNUNET_MessageHeader *hdr)
174 {
175   struct SendBuffer *write_std = cls;
176   uint16_t sendsize;
177
178   sendsize = ntohs (hdr->size);
179   if ((sendsize + write_std->size) > MAXLINE * 2)
180   {
181     FPRINTF (stderr, "%s", "Packet too big for buffer\n");
182     exit (1);
183   }
184   GNUNET_memcpy (write_std->buf + write_std->size, hdr, sendsize);
185   write_std->size += sendsize;
186   return GNUNET_OK;
187 }
188
189
190 /**
191  * Main function of a program that pretends to be a WLAN card.
192  *
193  * @param argc should be 2
194  * @param argv either '1' or '2', depending on which of the two cards this dummy is to emulate
195  * @return 1 on error, 0 if terminated normally via signal
196  */
197 int
198 main (int argc, char *argv[])
199 {
200   struct stat st;
201   int erg;
202   FILE *fpin = NULL;
203   FILE *fpout = NULL;
204   int fdpin;
205   int fdpout;
206   char readbuf[MAXLINE];
207   int readsize;
208   struct SendBuffer write_std;
209   struct SendBuffer write_pout;
210   int ret;
211   int maxfd;
212   fd_set rfds;
213   fd_set wfds;
214   struct timeval tv;
215   int retval;
216   struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst = NULL;
217   struct GNUNET_SERVER_MessageStreamTokenizer *file_in_mst = NULL;
218   struct GNUNET_TRANSPORT_WLAN_MacAddress macaddr;
219   int first;
220
221   if ( (2 != argc) ||
222        ((0 != strcmp (argv[1], "1")) && (0 != strcmp (argv[1], "2"))) )
223   {
224     FPRINTF (stderr,
225              "%s",
226              "This program must be started with the operating mode (1 or 2) as the only argument.\n");
227     return 1;
228   }
229
230   /* make the fifos if needed */
231   umask (0);
232   if ( (GNUNET_OK != GNUNET_DISK_directory_create_for_file (FIFO_FILE1)) ||
233        (GNUNET_OK != GNUNET_DISK_directory_create_for_file (FIFO_FILE2)) )
234   {
235     FPRINTF (stderr,
236              "Failed to create directory for file `%s'\n",
237              FIFO_FILE1);
238     return 1;
239   }
240   if (0 == strcmp (argv[1], "1") )
241   {
242     if (0 != stat (FIFO_FILE1, &st))
243     {
244       erg = mkfifo (FIFO_FILE1, 0666);
245       if ( (0 != erg) && (EEXIST != errno) )
246         FPRINTF (stderr, "Error in mkfifo(%s): %s\n", FIFO_FILE1,
247                  strerror (errno));
248     }
249   }
250   else
251   {
252     if (0 != stat (FIFO_FILE2, &st))
253     {
254         GNUNET_break (0 == (erg = mkfifo (FIFO_FILE2, 0666)));
255       if ( (0 != erg) && (EEXIST != errno) )
256         FPRINTF (stderr,
257                  "Error in mkfifo(%s): %s\n",
258                  FIFO_FILE2,
259                  strerror (errno));
260     }
261   }
262
263   if (0 == strcmp (argv[1], "1"))
264   {
265     first = 1;
266     fpin = fopen (FIFO_FILE1, "r");
267     if (NULL == fpin)
268     {
269       FPRINTF (stderr,
270                "fopen of read FIFO_FILE1 failed: %s\n",
271                STRERROR (errno));
272       goto end;
273     }
274     if (NULL == (fpout = fopen (FIFO_FILE2, "w")))
275     {
276       GNUNET_break (0 == mkfifo (FIFO_FILE2, 0666));
277       fpout = fopen (FIFO_FILE2, "w");
278     }
279     if (NULL == fpout)
280     {
281       FPRINTF (stderr,
282                "fopen of write FIFO_FILE2 failed: %s\n",
283                STRERROR (errno));
284       goto end;
285     }
286   }
287   else
288   {
289     first = 0;
290     if (NULL == (fpout = fopen (FIFO_FILE1, "w")))
291     {
292         GNUNET_break (0 == mkfifo (FIFO_FILE1, 0666));
293       fpout = fopen (FIFO_FILE1, "w");
294     }
295     if (NULL == fpout)
296     {
297       FPRINTF (stderr,
298                "fopen of write FIFO_FILE1 failed: %s\n",
299                STRERROR (errno));
300       goto end;
301     }
302     fpin = fopen (FIFO_FILE2, "r");
303     if (NULL == fpin)
304     {
305       FPRINTF (stderr,
306                "fopen of read FIFO_FILE2 failed: %s\n",
307                STRERROR (errno));
308       goto end;
309     }
310   }
311
312   fdpin = fileno (fpin);
313   GNUNET_assert (fpin >= 0);
314   if (fdpin >= FD_SETSIZE)
315   {
316     FPRINTF (stderr,
317              "File fdpin number too large (%d > %u)\n",
318              fdpin,
319              (unsigned int) FD_SETSIZE);
320     goto end;
321   }
322
323   fdpout = fileno (fpout);
324   GNUNET_assert (fdpout >= 0);
325
326   if (fdpout >= FD_SETSIZE)
327   {
328     FPRINTF (stderr,
329              "File fdpout number too large (%d > %u)\n",
330              fdpout,
331              (unsigned int) FD_SETSIZE);
332     goto end;
333   }
334
335   signal (SIGINT, &sigfunc);
336   signal (SIGTERM, &sigfunc);
337   signal (GNUNET_TERM_SIG, &sigfunc);
338
339   write_std.size = 0;
340   write_std.pos = 0;
341   write_pout.size = 0;
342   write_pout.pos = 0;
343   stdin_mst = GNUNET_SERVER_mst_create (&stdin_send, &write_pout);
344   file_in_mst = GNUNET_SERVER_mst_create (&file_in_send, &write_std);
345
346   /* Send 'random' mac address */
347   macaddr.mac[0] = 0x13;
348   macaddr.mac[1] = 0x22;
349   macaddr.mac[2] = 0x33;
350   macaddr.mac[3] = 0x44;
351   macaddr.mac[4] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, 256);
352   macaddr.mac[5] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, 256);
353   write_std.size = send_mac_to_plugin (write_std.buf, &macaddr);
354
355   while (0 == closeprog)
356   {
357     maxfd = -1;
358     tv.tv_sec = 5;
359     tv.tv_usec = 0;
360
361     FD_ZERO (&rfds);
362     FD_ZERO (&wfds);
363     /* if output queue is empty, read */
364     if (0 == write_pout.size)
365     {
366       FD_SET (STDIN_FILENO, &rfds);
367       maxfd = MAX (STDIN_FILENO, maxfd);
368     }
369     if (0 == write_std.size)
370     {
371       FD_SET (fdpin, &rfds);
372       maxfd = MAX (fdpin, maxfd);
373     }
374
375     /* if there is something to write, try to write */
376     if (0 < write_std.size)
377     {
378       FD_SET (STDOUT_FILENO, &wfds);
379       maxfd = MAX (maxfd, STDOUT_FILENO);
380     }
381     if (0 < write_pout.size)
382     {
383       FD_SET (fdpout, &wfds);
384       maxfd = MAX (maxfd, fdpout);
385     }
386
387     retval = select (maxfd + 1, &rfds, &wfds, NULL, &tv);
388     if ((-1 == retval) && (EINTR == errno))
389       continue;
390     if (0 > retval)
391     {
392       FPRINTF (stderr, "select failed: %s\n", STRERROR (errno));
393       closeprog = 1;
394       break;
395     }
396
397     if (FD_ISSET (STDOUT_FILENO, &wfds))
398     {
399       ret =
400           write (STDOUT_FILENO, write_std.buf + write_std.pos,
401                  write_std.size - write_std.pos);
402       if (0 > ret)
403       {
404         closeprog = 1;
405         FPRINTF (stderr, "Write ERROR to STDOUT_FILENO: %s\n",
406                  STRERROR (errno));
407         break;
408       }
409       else
410       {
411         write_std.pos += ret;
412         /* check if finished writing */
413         if (write_std.pos == write_std.size)
414         {
415           write_std.pos = 0;
416           write_std.size = 0;
417         }
418       }
419     }
420
421     if (FD_ISSET (fdpout, &wfds))
422     {
423       ret =
424           write (fdpout, write_pout.buf + write_pout.pos,
425                  write_pout.size - write_pout.pos);
426
427       if (0 > ret)
428       {
429         closeprog = 1;
430         FPRINTF (stderr, "Write ERROR to fdpout failed: %s\n", STRERROR (errno));
431       }
432       else
433       {
434         write_pout.pos += ret;
435         /* check if finished writing */
436         if (write_pout.pos == write_pout.size)
437         {
438           write_pout.pos = 0;
439           write_pout.size = 0;
440         }
441       }
442     }
443
444     if (FD_ISSET (STDIN_FILENO, &rfds))
445     {
446       readsize = read (STDIN_FILENO, readbuf, sizeof (readbuf));
447
448       if (0 > readsize)
449       {
450         closeprog = 1;
451         FPRINTF (stderr, "Error reading from STDIN_FILENO: %s\n",
452                  STRERROR (errno));
453       }
454       else if (0 < readsize)
455       {
456         GNUNET_SERVER_mst_receive (stdin_mst, NULL, readbuf, readsize,
457                                    GNUNET_NO, GNUNET_NO);
458
459       }
460       else
461       {
462         /* eof */
463         closeprog = 1;
464       }
465     }
466
467     if (FD_ISSET (fdpin, &rfds))
468     {
469       readsize = read (fdpin, readbuf, sizeof (readbuf));
470       if (0 > readsize)
471       {
472         closeprog = 1;
473         FPRINTF (stderr, "Error reading from fdpin: %s\n", STRERROR (errno));
474         break;
475       }
476       else if (0 < readsize)
477       {
478         GNUNET_SERVER_mst_receive (file_in_mst, NULL, readbuf, readsize,
479                                    GNUNET_NO, GNUNET_NO);
480       }
481       else
482       {
483         /* eof */
484         closeprog = 1;
485       }
486     }
487   }
488
489 end:
490   /* clean up */
491   if (NULL != stdin_mst)
492     GNUNET_SERVER_mst_destroy (stdin_mst);
493   if (NULL != file_in_mst)
494     GNUNET_SERVER_mst_destroy (file_in_mst);
495
496   if (NULL != fpout)
497     fclose (fpout);
498   if (NULL != fpin)
499     fclose (fpin);
500   if (1 == first)
501   {
502     (void) unlink (FIFO_FILE1);
503     (void) unlink (FIFO_FILE2);
504   }
505   return 0;
506 }