missing function reference
[oweals/gnunet.git] / src / transport / gnunet-nat-client-udp.c
1 /*
2      This file is part of GNUnet.
3      (C) 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 /**
22  * @file src/transport/client-test.c
23  * @brief Test for NAT traversal using ICMP method.
24  * @author Christian Grothoff
25  */
26 #include <sys/types.h> 
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <stdint.h>
36 #include <netinet/in.h> 
37 #include <time.h>
38
39 /**
40  * How often do we send our UDP messages to keep ports open (and to
41  * try to connect, of course).  Use small value since we are the
42  * initiator and should hence be rather aggressive.
43  */
44 #define UDP_SEND_FREQUENCY_MS 5
45
46 /**
47  * Port we always try to use.
48  */
49 #define NAT_TRAV_PORT 22223
50
51 /**
52  * Number of UDP ports to keep open at the same time (typically >= 256).
53  * Should be less than FD_SETSIZE.
54  */
55 #define NUM_UDP_PORTS 1000
56
57 /**
58  * How often do we retry to open and bind a UDP socket before giving up?
59  */
60 #define MAX_BIND_TRIES 10
61
62 /**
63  * How often do we try at most?  We expect to need (for the worst kind
64  * of NAT) on average 64512 / 512 = 126 attempts to have the right
65  * destination port and we then need to also (in the worst case) have
66  * the right source port (so 126 * 64512 = 8128512 packets on
67  * average!).  That's obviously a bit much, so we give up earlier.  The
68  * given value corresponds to about 1 minute of runtime (for a send
69  * frequency of one packet per ms).
70  *
71  * NOW: if the *server* would listen for Linux-generated ICMP 
72  * "Destination unreachables" we *might* increase our chances since
73  * maybe the firewall has some older/other UDP rules (this was
74  * the case during testing for me), but obviously that would mean
75  * more SUID'ed code. Yuck.
76  */
77 #define MAX_TRIES 62500
78
79 #define LOW_PORT 32768
80
81 /**
82  * create a random port number that is not totally
83  * unlikely to be chosen by the nat box.
84  */
85 static uint16_t 
86 make_port ()
87 {
88   return LOW_PORT + ( (unsigned int)rand ()) % (64 * 1024 - LOW_PORT);
89 }
90
91
92 /**
93  * create a fresh udp socket bound to a random local port,
94  * or, if the argument is zero, to the NAT_TRAV_PORT.
95  *
96  * @param i counter
97  * @return -1 on error
98  */
99 static int
100 make_udp_socket (int i)
101 {
102   int ret;
103   int tries;
104   struct sockaddr_in src;
105
106   for (tries=0;tries<MAX_BIND_TRIES;tries++)
107     {
108       ret = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
109       if (-1 == ret)
110         {
111           fprintf (stderr,
112                    "Error opening udp socket: %s\n",
113                    strerror (errno));
114           return -1;
115         }
116       if (ret >= FD_SETSIZE)
117         {
118           fprintf (stderr,
119                    "Socket number too large (%d > %u)\n",
120                    ret,
121                    (unsigned int) FD_SETSIZE);
122           close (ret);
123           return -1;
124         }
125       memset (&src, 0, sizeof (src));
126       src.sin_family = AF_INET;
127       if (i == 0)
128         src.sin_port = htons (NAT_TRAV_PORT);
129       else
130         src.sin_port = htons (make_port ());
131       if (0 != bind (ret, (struct sockaddr*) &src, sizeof (src)))
132         {
133           close (ret);
134           continue;
135         }
136       return ret;
137     }
138   fprintf (stderr,
139            "Error binding udp socket: %s\n",
140            strerror (errno));
141   return -1;
142 }
143
144
145
146
147 int
148 main (int argc, char *const *argv)
149 {
150   int udpsocks[NUM_UDP_PORTS];
151   char command[512];
152   struct in_addr external;
153   struct in_addr target;
154   int ret;
155   unsigned int pos;
156   int i;
157   int max;
158   struct sockaddr_in dst;
159   struct sockaddr_in src;
160   int first_round = 1;
161   char dummybuf[65536];
162   unsigned int tries;
163   struct timeval tv;
164   socklen_t slen;
165   fd_set rs;
166  
167   if (argc != 3)
168     {
169       fprintf (stderr,
170                "This program must be started with our IP and the targets external IP as arguments.\n");
171       return 1;
172     }
173   if ( (1 != inet_pton (AF_INET, argv[1], &external)) ||
174        (1 != inet_pton (AF_INET, argv[2], &target)) )
175     {
176       fprintf (stderr,
177                "Error parsing IPv4 address: %s\n",
178                strerror (errno));
179       return 1;
180     }
181   snprintf (command, 
182             sizeof (command),
183             "gnunet-nat-client %s %s",
184             argv[1],
185             argv[2]);
186   if (0 != (ret = system (command)))
187     {
188       if (ret == -1)
189         fprintf (stderr,
190                  "Error running `%s': %s\n",
191                  command,
192                  strerror (errno));
193       return 1;
194     }
195   fprintf (stderr,
196            "Trying to connect to `%s'\n",
197            argv[2]);
198   srand (time(NULL));
199   for (i=0;i<NUM_UDP_PORTS;i++)
200     udpsocks[i] = make_udp_socket (i); 
201   memset (&dst, 0, sizeof (dst));
202   dst.sin_family = AF_INET;
203   dst.sin_addr = target;
204   pos = 0;
205   tries = 0;
206   while (MAX_TRIES > tries++)
207     {
208       FD_ZERO (&rs);
209       for (i=0;i<NUM_UDP_PORTS;i++)
210         {
211           if (udpsocks[i] != -1)
212             FD_SET (udpsocks[i], &rs);
213           if (udpsocks[i] > max)
214             max = udpsocks[i];
215         }
216       tv.tv_sec = 0;
217       tv.tv_usec = UDP_SEND_FREQUENCY_MS * 1000;
218       select (max + 1, &rs, NULL, NULL, &tv);
219       for (i=0;i<NUM_UDP_PORTS;i++)
220         {
221           if (udpsocks[i] == -1)
222             continue;
223           if (! FD_ISSET (udpsocks[i], &rs))
224             continue;
225           slen = sizeof (src);
226           recvfrom (udpsocks[i], 
227                     dummybuf, sizeof (dummybuf), 0,
228                     (struct sockaddr*) &src,
229                     &slen);
230           if (slen != sizeof (src))
231             {
232               fprintf (stderr,
233                        "Unexpected size of address.\n");
234               continue;
235             }
236           if (0 != memcmp (&src.sin_addr,
237                            &target,
238                            sizeof (external)))
239             {
240               fprintf (stderr,
241                        "Unexpected sender IP\n");
242               continue;
243             }
244           /* discovered port! */
245           fprintf (stdout,
246                    "%s:%u\n",
247                    argv[2],
248                    ntohs (src.sin_port));
249           dst.sin_port = src.sin_port;
250           if (-1 == sendto (udpsocks[i],
251                             NULL, 0, 0,
252                             (struct sockaddr*) &dst, sizeof (dst)))
253             {
254               fprintf (stderr,
255                        "sendto failed: %s\n",
256                        strerror (errno));             
257               return 2; /* oops */
258             }     
259           /* success! */
260           fprintf (stderr,
261                    "Succeeded after %u packets.\n",
262                    tries);
263           return 0;
264         }
265       if (udpsocks[pos] == -1)
266         {
267           udpsocks[pos] = make_udp_socket (pos);
268           continue;
269         }
270       if ( (0 == ((unsigned int)rand() % NUM_UDP_PORTS)) ||
271            (1 == first_round) )
272         dst.sin_port = htons (NAT_TRAV_PORT);
273       else
274         dst.sin_port = htons (make_port ());
275       fprintf (stderr,
276                "Sending UDP packet to `%s:%u'\n",
277                argv[2],
278                ntohs (dst.sin_port));
279       first_round = 0;
280       if (-1 == sendto (udpsocks[pos],
281                         NULL, 0, 0,
282                         (struct sockaddr*) &dst, sizeof (dst)))
283         {
284           fprintf (stderr,
285                    "sendto failed: %s\n",
286                    strerror (errno));
287           close (udpsocks[pos]);
288           udpsocks[pos] = make_udp_socket (pos);
289         }
290       pos = (pos+1) % NUM_UDP_PORTS;
291     }
292   fprintf (stderr,
293            "Giving up after %u tries.\n",
294            tries);
295   return 3;
296 }
297
298 /* end of client-test.c */