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