another special case for loopback
[oweals/gnunet.git] / src / nat-auto / gnunet-nat-server.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011 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 /**
22  * @file src/nat/gnunet-nat-server.c
23  * @brief Daemon to run on 'gnunet.org' to help test NAT traversal code
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_nat_service.h"
29 #include "gnunet_protocols.h"
30 #include "nat-auto.h"
31
32
33 /**
34  * Our server.
35  */
36 static struct GNUNET_SERVER_Handle *server;
37
38 /**
39  * Our configuration.
40  */
41 static const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43
44 /**
45  * Try contacting the peer using autonomous NAT traveral method.
46  *
47  * @param dst_ipv4 IPv4 address to send the fake ICMP message
48  * @param dport destination port to include in ICMP message
49  * @param is_tcp mark for TCP (#GNUNET_YES)  or UDP (#GNUNET_NO)
50  */
51 static void
52 try_anat (uint32_t dst_ipv4,
53           uint16_t dport,
54           int is_tcp)
55 {
56   struct GNUNET_NAT_Handle *h;
57   struct sockaddr_in lsa;
58   struct sockaddr_in rsa;
59   socklen_t sa_len;
60
61   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
62               "Asking for connection reversal with %x and code %u\n",
63               (unsigned int) dst_ipv4,
64               (unsigned int) dport);
65   memset (&lsa, 0, sizeof (lsa));
66   lsa.sin_family = AF_INET;
67 #if HAVE_SOCKADDR_IN_SIN_LEN
68   lsa.sin_len = sizeof (sa);
69 #endif
70   lsa.sin_addr.s_addr = 0;
71   lsa.sin_port = htons (dport);
72   memset (&rsa, 0, sizeof (rsa));
73   rsa.sin_family = AF_INET;
74 #if HAVE_SOCKADDR_IN_SIN_LEN
75   rsa.sin_len = sizeof (sa);
76 #endif
77   rsa.sin_addr.s_addr = dst_ipv4;
78   rsa.sin_port = htons (dport);
79   sa_len = sizeof (lsa);
80   h = GNUNET_NAT_register (cfg,
81                            "none",
82                            is_tcp ? IPPROTO_TCP : IPPROTO_UDP,
83                            1,
84                            (const struct sockaddr **) &lsa,
85                            &sa_len,
86                            NULL, NULL, NULL);
87   GNUNET_NAT_request_reversal (h,
88                                &lsa,
89                                &rsa);
90   GNUNET_NAT_unregister (h);
91 }
92
93
94 /**
95  * Closure for #tcp_send.
96  */
97 struct TcpContext
98 {
99   /**
100    * TCP  socket.
101    */
102   struct GNUNET_NETWORK_Handle *s;
103
104   /**
105    * Data to transmit.
106    */
107   uint16_t data;
108 };
109
110
111 /**
112  * Task called by the scheduler once we can do the TCP send
113  * (or once we failed to connect...).
114  *
115  * @param cls the `struct TcpContext`
116  */
117 static void
118 tcp_send (void *cls)
119 {
120   struct TcpContext *ctx = cls;
121   const struct GNUNET_SCHEDULER_TaskContext *tc;
122
123   tc = GNUNET_SCHEDULER_get_task_context ();
124   if ((NULL != tc->write_ready) &&
125       (GNUNET_NETWORK_fdset_isset (tc->write_ready, ctx->s)))
126   {
127     if (-1 ==
128         GNUNET_NETWORK_socket_send (ctx->s, &ctx->data, sizeof (ctx->data)))
129     {
130       GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send");
131     }
132     GNUNET_NETWORK_socket_shutdown (ctx->s, SHUT_RDWR);
133   }
134   GNUNET_NETWORK_socket_close (ctx->s);
135   GNUNET_free (ctx);
136 }
137
138
139 /**
140  * Try to send @a data to the
141  * IP @a dst_ipv4' at port @a dport via TCP.
142  *
143  * @param dst_ipv4 target IP
144  * @param dport target port
145  * @param data data to send
146  */
147 static void
148 try_send_tcp (uint32_t dst_ipv4,
149               uint16_t dport,
150               uint16_t data)
151 {
152   struct GNUNET_NETWORK_Handle *s;
153   struct sockaddr_in sa;
154   struct TcpContext *ctx;
155
156   s = GNUNET_NETWORK_socket_create (AF_INET,
157                                     SOCK_STREAM,
158                                     0);
159   if (NULL == s)
160   {
161     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
162                          "socket");
163     return;
164   }
165   memset (&sa, 0, sizeof (sa));
166   sa.sin_family = AF_INET;
167 #if HAVE_SOCKADDR_IN_SIN_LEN
168   sa.sin_len = sizeof (sa);
169 #endif
170   sa.sin_addr.s_addr = dst_ipv4;
171   sa.sin_port = htons (dport);
172   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173               "Sending TCP message to `%s'\n",
174               GNUNET_a2s ((struct sockaddr *) &sa,
175                           sizeof (sa)));
176   if ( (GNUNET_OK !=
177         GNUNET_NETWORK_socket_connect (s,
178                                        (const struct sockaddr *) &sa,
179                                        sizeof (sa))) &&
180        (errno != EINPROGRESS) )
181   {
182     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
183                          "connect");
184     GNUNET_NETWORK_socket_close (s);
185     return;
186   }
187   ctx = GNUNET_new (struct TcpContext);
188   ctx->s = s;
189   ctx->data = data;
190   GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_SECONDS,
191                                   s,
192                                   &tcp_send,
193                                   ctx);
194 }
195
196
197 /**
198  * Try to send @a data to the
199  * IP @a dst_ipv4 at port @a dport via UDP.
200  *
201  * @param dst_ipv4 target IP
202  * @param dport target port
203  * @param data data to send
204  */
205 static void
206 try_send_udp (uint32_t dst_ipv4,
207               uint16_t dport,
208               uint16_t data)
209 {
210   struct GNUNET_NETWORK_Handle *s;
211   struct sockaddr_in sa;
212
213   s = GNUNET_NETWORK_socket_create (AF_INET,
214                                     SOCK_DGRAM,
215                                     0);
216   if (NULL == s)
217   {
218     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
219                          "socket");
220     return;
221   }
222   memset (&sa, 0, sizeof (sa));
223   sa.sin_family = AF_INET;
224 #if HAVE_SOCKADDR_IN_SIN_LEN
225   sa.sin_len = sizeof (sa);
226 #endif
227   sa.sin_addr.s_addr = dst_ipv4;
228   sa.sin_port = htons (dport);
229   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
230               "Sending UDP packet to `%s'\n",
231               GNUNET_a2s ((struct sockaddr *) &sa,
232                           sizeof (sa)));
233   if (-1 ==
234       GNUNET_NETWORK_socket_sendto (s,
235                                     &data,
236                                     sizeof (data),
237                                     (const struct sockaddr *) &sa,
238                                     sizeof (sa)))
239     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
240                          "sendto");
241   GNUNET_NETWORK_socket_close (s);
242 }
243
244
245 /**
246  * We've received a request to probe a NAT
247  * traversal. Do it.
248  *
249  * @param cls unused
250  * @param client handle to client (we always close)
251  * @param msg message with details about what to test
252  */
253 static void
254 test (void *cls,
255       struct GNUNET_SERVER_Client *client,
256       const struct GNUNET_MessageHeader *msg)
257 {
258   const struct GNUNET_NAT_AUTO_TestMessage *tm;
259   uint16_t dport;
260
261   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
262               "Received test request\n");
263   tm = (const struct GNUNET_NAT_AUTO_TestMessage *) msg;
264   dport = ntohs (tm->dport);
265   if (0 == dport)
266     try_anat (tm->dst_ipv4,
267               ntohs (tm->data),
268               (int) ntohl (tm->is_tcp));
269   else if (GNUNET_YES == ntohl (tm->is_tcp))
270     try_send_tcp (tm->dst_ipv4,
271                   dport,
272                   tm->data);
273   else
274     try_send_udp (tm->dst_ipv4,
275                   dport,
276                   tm->data);
277   GNUNET_SERVER_receive_done (client,
278                               GNUNET_NO);
279 }
280
281
282 /**
283  * Task run during shutdown.
284  *
285  * @param cls unused
286  */
287 static void
288 shutdown_task (void *cls)
289 {
290   GNUNET_SERVER_destroy (server);
291   server = NULL;
292 }
293
294
295 /**
296  * Main function that will be run.
297  *
298  * @param cls closure
299  * @param args remaining command-line arguments
300  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
301  * @param c configuration
302  */
303 static void
304 run (void *cls,
305      char *const *args,
306      const char *cfgfile,
307      const struct GNUNET_CONFIGURATION_Handle *c)
308 {
309   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
310     {&test, NULL, GNUNET_MESSAGE_TYPE_NAT_TEST,
311      sizeof (struct GNUNET_NAT_AUTO_TestMessage)},
312     {NULL, NULL, 0, 0}
313   };
314   unsigned int port;
315   struct sockaddr_in in4;
316   struct sockaddr_in6 in6;
317
318   socklen_t slen[] = {
319     sizeof (in4),
320     sizeof (in6),
321     0
322   };
323   struct sockaddr *sa[] = {
324     (struct sockaddr *) &in4,
325     (struct sockaddr *) &in6,
326     NULL
327   };
328
329   cfg = c;
330   if ( (NULL == args[0]) ||
331        (1 != SSCANF (args[0], "%u", &port)) ||
332        (0 == port) ||
333        (65536 <= port) )
334   {
335     FPRINTF (stderr,
336              _("Please pass valid port number as the first argument! (got `%s')\n"),
337              args[0]);
338     return;
339   }
340   memset (&in4, 0, sizeof (in4));
341   memset (&in6, 0, sizeof (in6));
342   in4.sin_family = AF_INET;
343   in4.sin_port = htons ((uint16_t) port);
344   in6.sin6_family = AF_INET6;
345   in6.sin6_port = htons ((uint16_t) port);
346 #if HAVE_SOCKADDR_IN_SIN_LEN
347   in4.sin_len = sizeof (in4);
348   in6.sin6_len = sizeof (in6);
349 #endif
350   server = GNUNET_SERVER_create (NULL,
351                                  NULL,
352                                  (struct sockaddr * const *) sa,
353                                  slen,
354                                  GNUNET_TIME_UNIT_SECONDS,
355                                  GNUNET_YES);
356   GNUNET_SERVER_add_handlers (server,
357                               handlers);
358   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
359                                  NULL);
360 }
361
362
363 /**
364  * Main function of gnunet-nat-server.
365  *
366  * @param argc number of command-line arguments
367  * @param argv command line
368  * @return 0 on success, -1 on error
369  */
370 int
371 main (int argc, char *const argv[])
372 {
373   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
374     GNUNET_GETOPT_OPTION_END
375   };
376
377   if (GNUNET_OK !=
378       GNUNET_STRINGS_get_utf8_args (argc, argv,
379                                     &argc, &argv))
380     return 2;
381
382   if (GNUNET_OK !=
383       GNUNET_PROGRAM_run (argc,
384                           argv,
385                           "gnunet-nat-server [options] PORT",
386                           _("GNUnet NAT traversal test helper daemon"),
387                           options,
388                           &run,
389                           NULL))
390   {
391     GNUNET_free ((void*) argv);
392     return 1;
393   }
394   GNUNET_free ((void*) argv);
395   return 0;
396 }
397
398
399 /* end of gnunet-nat-server.c */