finishing gnunet-nat-server
[oweals/gnunet.git] / src / nat / gnunet-nat-server.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 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/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  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_nat_lib.h"
30 #include "gnunet_protocols.h"
31 #include "nat.h"
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  * Try contacting the peer using autonomous
45  * 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 sa;
58
59   h = GNUNET_NAT_register (cfg,
60                            is_tcp,
61                            dport,
62                            0, NULL, NULL,
63                            NULL, NULL, NULL);
64   memset (&sa, 0, sizeof (sa));
65 #if HAVE_SOCKADDR_IN_SIN_LEN
66   sa.sin_len = sizeof (sa);
67 #endif
68   sa.sin_addr.s_addr = dst_ipv4; 
69   GNUNET_NAT_run_client (h, &sa);
70   GNUNET_NAT_unregister (h);
71 }
72
73
74 /**
75  * Closure for 'tcp_send'.
76  */
77 struct TcpContext 
78 {
79   /**
80    * TCP  socket.
81    */
82   struct GNUNET_NETWORK_Handle *s;
83
84   /** 
85    * Data to transmit.
86    */  
87   uint16_t data;
88 };
89
90
91 /**
92  * Task called by the scheduler once we can do the TCP send
93  * (or once we failed to connect...).
94  *
95  * @param ctx the 'struct TcpContext'
96  * @param tc scheduler context
97  */
98 static void
99 tcp_send (void *cls,
100           const struct GNUNET_SCHEDULER_TaskContext *tc)
101 {
102   struct TcpContext *ctx = cls;
103
104   if ( (NULL != tc->write_ready) &&
105        (GNUNET_NETWORK_fdset_isset (tc->write_ready, 
106                                     ctx->s)) )
107     {
108       if (-1 == GNUNET_NETWORK_socket_send (ctx->s, &ctx->data, sizeof (ctx->data)))
109         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send");
110       GNUNET_NETWORK_socket_shutdown (ctx->s, SHUT_RDWR);
111     }
112   GNUNET_NETWORK_socket_close (ctx->s);
113   GNUNET_free (ctx);
114 }
115
116
117 /**
118  * Try to send 'data' to the
119  * IP 'dst_ipv4' at port 'dport' via TCP.
120  * 
121  * @param dst_ivp4 target IP
122  * @param dport target port
123  * @param data data to send
124  */
125 static void
126 try_send_tcp (uint32_t dst_ipv4,
127               uint16_t dport,
128               uint16_t data)
129 {
130   struct GNUNET_NETWORK_Handle *s;
131   struct sockaddr_in sa;
132   struct TcpContext *ctx;
133
134   s = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0);
135   if (NULL == s)
136     {
137       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
138       return;
139     }
140   memset (&sa, 0, sizeof (sa));
141 #if HAVE_SOCKADDR_IN_SIN_LEN
142   sa.sin_len = sizeof (sa);
143 #endif
144   sa.sin_addr.s_addr = dst_ipv4; 
145   sa.sin_port = htons (dport);
146   if ( (GNUNET_OK != 
147         GNUNET_NETWORK_socket_connect (s, 
148                                        (const struct sockaddr*) &sa, sizeof (sa))) &&
149        (errno != EINPROGRESS) )
150     {
151       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
152       GNUNET_NETWORK_socket_close (s);
153       return;
154     }
155   ctx = GNUNET_malloc (sizeof (struct TcpContext));
156   ctx->s = s;
157   ctx->data = data;
158   GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_SECONDS,
159                                   s,
160                                   &tcp_send, ctx);
161 }
162
163
164 /**
165  * Try to send 'data' to the
166  * IP 'dst_ipv4' at port 'dport' via UDP.
167  * 
168  * @param dst_ivp4 target IP
169  * @param dport target port
170  * @param data data to send
171  */
172 static void
173 try_send_udp (uint32_t dst_ipv4,
174               uint16_t dport,
175               uint16_t data)
176 {
177   struct GNUNET_NETWORK_Handle *s;
178   struct sockaddr_in sa;
179
180   s = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_DGRAM, 0);
181   if (NULL == s)
182     {
183       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
184       return;
185     }
186   memset (&sa, 0, sizeof (sa));
187 #if HAVE_SOCKADDR_IN_SIN_LEN
188   sa.sin_len = sizeof (sa);
189 #endif
190   sa.sin_addr.s_addr = dst_ipv4; 
191   sa.sin_port = htons (dport);
192   if (-1 == GNUNET_NETWORK_socket_sendto (s, &data, sizeof(data), (const struct sockaddr*) &sa, sizeof (sa)))
193     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "sendto");
194   GNUNET_NETWORK_socket_close (s);
195 }
196
197
198 /**
199  * We've received a request to probe a NAT
200  * traversal. Do it.
201  * 
202  * @param cls unused
203  * @param client handle to client (we always close)
204  * @param msg message with details about what to test
205  */
206 static void
207 test (void *cls,
208       struct GNUNET_SERVER_Client *client,
209       const struct GNUNET_MessageHeader *msg)
210 {
211   const struct GNUNET_NAT_TestMessage *tm;
212   uint16_t dport;
213
214   tm = (const struct GNUNET_NAT_TestMessage*) msg;
215   dport = ntohs (tm->dport);
216   if (0 == dport)
217     try_anat (tm->dst_ipv4,
218               ntohs (tm->data),
219               (int) ntohl (tm->is_tcp));
220   else if (GNUNET_YES == ntohl (tm->is_tcp))
221     try_send_tcp (tm->dst_ipv4, dport, tm->data);
222   else
223     try_send_udp (tm->dst_ipv4, dport, tm->data);
224   GNUNET_SERVER_receive_done (client,
225                               GNUNET_NO);
226 }
227
228
229 /**
230  * Task run during shutdown.
231  *
232  * @param ctx unused
233  * @param tc scheduler context
234  */
235 static void
236 shutdown_task (void *cls,
237                const struct GNUNET_SCHEDULER_TaskContext *tc)
238 {
239   GNUNET_SERVER_destroy (server);
240   server = NULL;
241 }
242
243
244 /**
245  * Main function that will be run.
246  *
247  * @param cls closure
248  * @param args remaining command-line arguments
249  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
250  * @param c configuration
251  */
252 static void
253 run (void *cls,
254      char *const *args,
255      const char *cfgfile,
256      const struct GNUNET_CONFIGURATION_Handle *c)
257 {
258   static const struct GNUNET_SERVER_MessageHandler handlers[] =
259       {
260         { &test, NULL, GNUNET_MESSAGE_TYPE_NAT_TEST, sizeof (struct GNUNET_NAT_TestMessage) },
261         { NULL, NULL, 0, 0 }
262       };
263   unsigned int port;
264   struct sockaddr_in in4;
265   struct sockaddr_in6 in6;
266   socklen_t slen[] =
267     {
268       sizeof (in4),
269       sizeof (in6), 
270       0
271     };
272   struct sockaddr *sa[] =
273     {
274       (struct sockaddr*) &in4,
275       (struct sockaddr*) &in6,
276       NULL
277     };
278
279   cfg = c;
280   if ( (args[0] == NULL) || 
281        (1 != SSCANF (args[0], "%u", &port)) ||
282        (0 == port) ||
283        (65536 >= port) )
284     {
285       fprintf (stderr,
286                _("Please pass valid port number as the first argument!\n"));
287       return;
288     }
289   memset (&in4, 0, sizeof (in4)); 
290   memset (&in6, 0, sizeof (in6)); 
291   in4.sin_port = htons ((uint16_t) port);
292   in6.sin6_port = htons ((uint16_t) port);
293 #if HAVE_SOCKADDR_IN_SIN_LEN
294   in4.sin_len = sizeof (in);
295   in6.sin6_len = sizeof (in6);
296 #endif
297   server = GNUNET_SERVER_create (NULL, NULL,
298                                  (struct sockaddr*const*) sa,
299                                  slen,
300                                  GNUNET_TIME_UNIT_SECONDS,
301                                  GNUNET_YES);
302   GNUNET_SERVER_add_handlers (server,
303                               handlers);
304   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
305                                 &shutdown_task,
306                                 NULL);
307 }
308
309
310 /**
311  * Main function of gnunet-nat-server.
312  *
313  * @param argc number of command-line arguments
314  * @param argv command line
315  * @return 0 on success, -1 on error
316  */
317 int
318 main (int argc, char *const argv[])
319 {
320   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
321     GNUNET_GETOPT_OPTION_END
322   };
323
324   if (GNUNET_OK !=
325       GNUNET_PROGRAM_run (argc, argv, 
326                           "gnunet-nat-server [options] PORT", 
327                           _("GNUnet NAT traversal test helper daemon"), 
328                           options,
329                           &run, NULL))
330     return 1;
331   return 0;
332 }
333
334
335 /* end of gnunet-nat-server.c */