fix #3284: support lib/MULTIARCH/ paths in installation, use GNUNET_PREFIX=@libdir...
[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 /**
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
46  * NAT traveral method.
47  *
48  * @param dst_ipv4 IPv4 address to send the fake ICMP message
49  * @param dport destination port to include in ICMP message
50  * @param is_tcp mark for TCP (GNUNET_YES)  or UDP (GNUNET_NO)
51  */
52 static void
53 try_anat (uint32_t dst_ipv4, uint16_t dport, int is_tcp)
54 {
55   struct GNUNET_NAT_Handle *h;
56   struct sockaddr_in sa;
57
58   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
59               "Asking for connection reversal with %x and code %u\n",
60               (unsigned int) dst_ipv4, (unsigned int) dport);
61   h = GNUNET_NAT_register (cfg, is_tcp, dport, 0, NULL, NULL, NULL, NULL, NULL);
62   memset (&sa, 0, sizeof (sa));
63   sa.sin_family = AF_INET;
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 cls the 'struct TcpContext'
95  * @param tc scheduler context
96  */
97 static void
98 tcp_send (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
99 {
100   struct TcpContext *ctx = cls;
101
102   if ((NULL != tc->write_ready) &&
103       (GNUNET_NETWORK_fdset_isset (tc->write_ready, ctx->s)))
104   {
105     if (-1 ==
106         GNUNET_NETWORK_socket_send (ctx->s, &ctx->data, sizeof (ctx->data)))
107     {
108       GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send");
109     }
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_ipv4 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, uint16_t dport, uint16_t data)
127 {
128   struct GNUNET_NETWORK_Handle *s;
129   struct sockaddr_in sa;
130   struct TcpContext *ctx;
131
132   s = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
133   if (NULL == s)
134   {
135     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
136     return;
137   }
138   memset (&sa, 0, sizeof (sa));
139   sa.sin_family = AF_INET;
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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending TCP message to `%s'\n",
146               GNUNET_a2s ((struct sockaddr *) &sa, sizeof (sa)));
147   if ((GNUNET_OK !=
148        GNUNET_NETWORK_socket_connect (s, (const struct sockaddr *) &sa,
149                                       sizeof (sa))) && (errno != EINPROGRESS))
150   {
151     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
152     GNUNET_NETWORK_socket_close (s);
153     return;
154   }
155   ctx = GNUNET_new (struct TcpContext);
156   ctx->s = s;
157   ctx->data = data;
158   GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_SECONDS, s, &tcp_send, ctx);
159 }
160
161
162 /**
163  * Try to send 'data' to the
164  * IP 'dst_ipv4' at port 'dport' via UDP.
165  *
166  * @param dst_ipv4 target IP
167  * @param dport target port
168  * @param data data to send
169  */
170 static void
171 try_send_udp (uint32_t dst_ipv4, uint16_t dport, uint16_t data)
172 {
173   struct GNUNET_NETWORK_Handle *s;
174   struct sockaddr_in sa;
175
176   s = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0);
177   if (NULL == s)
178   {
179     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
180     return;
181   }
182   memset (&sa, 0, sizeof (sa));
183   sa.sin_family = AF_INET;
184 #if HAVE_SOCKADDR_IN_SIN_LEN
185   sa.sin_len = sizeof (sa);
186 #endif
187   sa.sin_addr.s_addr = dst_ipv4;
188   sa.sin_port = htons (dport);
189   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending UDP packet to `%s'\n",
190               GNUNET_a2s ((struct sockaddr *) &sa, sizeof (sa)));
191   if (-1 ==
192       GNUNET_NETWORK_socket_sendto (s, &data, sizeof (data),
193                                     (const struct sockaddr *) &sa, sizeof (sa)))
194     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "sendto");
195   GNUNET_NETWORK_socket_close (s);
196 }
197
198
199 /**
200  * We've received a request to probe a NAT
201  * traversal. Do it.
202  *
203  * @param cls unused
204  * @param client handle to client (we always close)
205  * @param msg message with details about what to test
206  */
207 static void
208 test (void *cls, 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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received test request\n");
215   tm = (const struct GNUNET_NAT_TestMessage *) msg;
216   dport = ntohs (tm->dport);
217   if (0 == dport)
218     try_anat (tm->dst_ipv4, ntohs (tm->data), (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, GNUNET_NO);
224 }
225
226
227 /**
228  * Task run during shutdown.
229  *
230  * @param cls unused
231  * @param tc scheduler context
232  */
233 static void
234 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
235 {
236   GNUNET_SERVER_destroy (server);
237   server = NULL;
238 }
239
240
241 /**
242  * Main function that will be run.
243  *
244  * @param cls closure
245  * @param args remaining command-line arguments
246  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
247  * @param c configuration
248  */
249 static void
250 run (void *cls, char *const *args, const char *cfgfile,
251      const struct GNUNET_CONFIGURATION_Handle *c)
252 {
253   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
254     {&test, NULL, GNUNET_MESSAGE_TYPE_NAT_TEST,
255      sizeof (struct GNUNET_NAT_TestMessage)},
256     {NULL, NULL, 0, 0}
257   };
258   unsigned int port;
259   struct sockaddr_in in4;
260   struct sockaddr_in6 in6;
261
262   socklen_t slen[] = {
263     sizeof (in4),
264     sizeof (in6),
265     0
266   };
267   struct sockaddr *sa[] = {
268     (struct sockaddr *) &in4,
269     (struct sockaddr *) &in6,
270     NULL
271   };
272
273   cfg = c;
274   if ((args[0] == NULL) || (1 != SSCANF (args[0], "%u", &port)) || (0 == port)
275       || (65536 <= port))
276   {
277     FPRINTF (stderr,
278              _
279              ("Please pass valid port number as the first argument! (got `%s')\n"),
280              args[0]);
281     return;
282   }
283   memset (&in4, 0, sizeof (in4));
284   memset (&in6, 0, sizeof (in6));
285   in4.sin_family = AF_INET;
286   in4.sin_port = htons ((uint16_t) port);
287   in6.sin6_family = AF_INET6;
288   in6.sin6_port = htons ((uint16_t) port);
289 #if HAVE_SOCKADDR_IN_SIN_LEN
290   in4.sin_len = sizeof (in4);
291   in6.sin6_len = sizeof (in6);
292 #endif
293   server =
294       GNUNET_SERVER_create (NULL, NULL, (struct sockaddr * const *) sa, slen,
295                             GNUNET_TIME_UNIT_SECONDS, GNUNET_YES);
296   GNUNET_SERVER_add_handlers (server, handlers);
297   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
298                                 NULL);
299 }
300
301
302 /**
303  * Main function of gnunet-nat-server.
304  *
305  * @param argc number of command-line arguments
306  * @param argv command line
307  * @return 0 on success, -1 on error
308  */
309 int
310 main (int argc, char *const argv[])
311 {
312   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
313     GNUNET_GETOPT_OPTION_END
314   };
315
316   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
317     return 2;
318
319   if (GNUNET_OK !=
320       GNUNET_PROGRAM_run (argc, argv, "gnunet-nat-server [options] PORT",
321                           _("GNUnet NAT traversal test helper daemon"), options,
322                           &run, NULL))
323   {
324     GNUNET_free ((void*) argv);
325     return 1;
326   }
327   GNUNET_free ((void*) argv);
328   return 0;
329 }
330
331
332 /* end of gnunet-nat-server.c */