7fd3ec37177ba452ac04ef0dff8ed7b78c64e6d3
[oweals/gnunet.git] / src / nat-auto / gnunet-nat-server.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011, 2017 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file src/nat/gnunet-nat-server.c
21  * @brief Daemon to run on 'gnunet.org' to help test NAT traversal code
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
25 #include "gnunet_util_lib.h"
26 #include "gnunet_nat_service.h"
27 #include "gnunet_protocols.h"
28 #include "nat-auto.h"
29
30
31 /**
32  * Information we track per client.
33  */
34 struct ClientData
35 {
36   /**
37    * Timeout task.
38    */
39   struct GNUNET_SCHEDULER_Task *tt;
40
41   /**
42    * Client handle.
43    */
44   struct GNUNET_SERVICE_Client *client;
45 };
46
47
48 /**
49  * Our configuration.
50  */
51 static const struct GNUNET_CONFIGURATION_Handle *cfg;
52
53
54 /**
55  * Try contacting the peer using autonomous NAT traveral method.
56  *
57  * @param dst_ipv4 IPv4 address to send the fake ICMP message
58  * @param dport destination port to include in ICMP message
59  * @param is_tcp mark for TCP (#GNUNET_YES)  or UDP (#GNUNET_NO)
60  */
61 static void
62 try_anat (uint32_t dst_ipv4,
63           uint16_t dport,
64           int is_tcp)
65 {
66   struct GNUNET_NAT_Handle *h;
67   struct sockaddr_in lsa;
68   struct sockaddr_in rsa;
69   const struct sockaddr *sa;
70   socklen_t sa_len;
71
72   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
73               "Asking for connection reversal with %x and code %u\n",
74               (unsigned int) dst_ipv4,
75               (unsigned int) dport);
76   memset (&lsa, 0, sizeof (lsa));
77   lsa.sin_family = AF_INET;
78 #if HAVE_SOCKADDR_IN_SIN_LEN
79   lsa.sin_len = sizeof (sa);
80 #endif
81   lsa.sin_addr.s_addr = 0;
82   lsa.sin_port = htons (dport);
83   memset (&rsa, 0, sizeof (rsa));
84   rsa.sin_family = AF_INET;
85 #if HAVE_SOCKADDR_IN_SIN_LEN
86   rsa.sin_len = sizeof (sa);
87 #endif
88   rsa.sin_addr.s_addr = dst_ipv4;
89   rsa.sin_port = htons (dport);
90   sa_len = sizeof (lsa);
91   sa = (const struct sockaddr *) &lsa;
92   h = GNUNET_NAT_register (cfg,
93                            "none",
94                            is_tcp ? IPPROTO_TCP : IPPROTO_UDP,
95                            1,
96                            &sa,
97                            &sa_len,
98                            NULL, NULL, NULL);
99   GNUNET_NAT_request_reversal (h,
100                                &lsa,
101                                &rsa);
102   GNUNET_NAT_unregister (h);
103 }
104
105
106 /**
107  * Closure for #tcp_send.
108  */
109 struct TcpContext
110 {
111   /**
112    * TCP  socket.
113    */
114   struct GNUNET_NETWORK_Handle *s;
115
116   /**
117    * Data to transmit.
118    */
119   uint16_t data;
120 };
121
122
123 /**
124  * Task called by the scheduler once we can do the TCP send
125  * (or once we failed to connect...).
126  *
127  * @param cls the `struct TcpContext`
128  */
129 static void
130 tcp_send (void *cls)
131 {
132   struct TcpContext *ctx = cls;
133   const struct GNUNET_SCHEDULER_TaskContext *tc;
134
135   tc = GNUNET_SCHEDULER_get_task_context ();
136   if ((NULL != tc->write_ready) &&
137       (GNUNET_NETWORK_fdset_isset (tc->write_ready, ctx->s)))
138   {
139     if (-1 ==
140         GNUNET_NETWORK_socket_send (ctx->s, &ctx->data, sizeof (ctx->data)))
141     {
142       GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send");
143     }
144     GNUNET_NETWORK_socket_shutdown (ctx->s, SHUT_RDWR);
145   }
146   GNUNET_NETWORK_socket_close (ctx->s);
147   GNUNET_free (ctx);
148 }
149
150
151 /**
152  * Try to send @a data to the
153  * IP @a dst_ipv4' at port @a dport via TCP.
154  *
155  * @param dst_ipv4 target IP
156  * @param dport target port
157  * @param data data to send
158  */
159 static void
160 try_send_tcp (uint32_t dst_ipv4,
161               uint16_t dport,
162               uint16_t data)
163 {
164   struct GNUNET_NETWORK_Handle *s;
165   struct sockaddr_in sa;
166   struct TcpContext *ctx;
167
168   s = GNUNET_NETWORK_socket_create (AF_INET,
169                                     SOCK_STREAM,
170                                     0);
171   if (NULL == s)
172   {
173     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
174                          "socket");
175     return;
176   }
177   memset (&sa, 0, sizeof (sa));
178   sa.sin_family = AF_INET;
179 #if HAVE_SOCKADDR_IN_SIN_LEN
180   sa.sin_len = sizeof (sa);
181 #endif
182   sa.sin_addr.s_addr = dst_ipv4;
183   sa.sin_port = htons (dport);
184   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
185               "Sending TCP message to `%s'\n",
186               GNUNET_a2s ((struct sockaddr *) &sa,
187                           sizeof (sa)));
188   if ( (GNUNET_OK !=
189         GNUNET_NETWORK_socket_connect (s,
190                                        (const struct sockaddr *) &sa,
191                                        sizeof (sa))) &&
192        (errno != EINPROGRESS) )
193   {
194     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
195                          "connect");
196     GNUNET_NETWORK_socket_close (s);
197     return;
198   }
199   ctx = GNUNET_new (struct TcpContext);
200   ctx->s = s;
201   ctx->data = data;
202   GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_SECONDS,
203                                   s,
204                                   &tcp_send,
205                                   ctx);
206 }
207
208
209 /**
210  * Try to send @a data to the
211  * IP @a dst_ipv4 at port @a dport via UDP.
212  *
213  * @param dst_ipv4 target IP
214  * @param dport target port
215  * @param data data to send
216  */
217 static void
218 try_send_udp (uint32_t dst_ipv4,
219               uint16_t dport,
220               uint16_t data)
221 {
222   struct GNUNET_NETWORK_Handle *s;
223   struct sockaddr_in sa;
224
225   s = GNUNET_NETWORK_socket_create (AF_INET,
226                                     SOCK_DGRAM,
227                                     0);
228   if (NULL == s)
229   {
230     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
231                          "socket");
232     return;
233   }
234   memset (&sa, 0, sizeof (sa));
235   sa.sin_family = AF_INET;
236 #if HAVE_SOCKADDR_IN_SIN_LEN
237   sa.sin_len = sizeof (sa);
238 #endif
239   sa.sin_addr.s_addr = dst_ipv4;
240   sa.sin_port = htons (dport);
241   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
242               "Sending UDP packet to `%s'\n",
243               GNUNET_a2s ((struct sockaddr *) &sa,
244                           sizeof (sa)));
245   if (-1 ==
246       GNUNET_NETWORK_socket_sendto (s,
247                                     &data,
248                                     sizeof (data),
249                                     (const struct sockaddr *) &sa,
250                                     sizeof (sa)))
251     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
252                          "sendto");
253   GNUNET_NETWORK_socket_close (s);
254 }
255
256
257 /**
258  * We've received a request to probe a NAT
259  * traversal. Do it.
260  *
261  * @param cls handle to client (we always close)
262  * @param msg message with details about what to test
263  */
264 static void
265 handle_test (void *cls,
266              const struct GNUNET_NAT_AUTO_TestMessage *tm)
267 {
268   struct ClientData *cd = cls;
269   uint16_t dport;
270
271   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
272               "Received test request\n");
273   dport = ntohs (tm->dport);
274   if (0 == dport)
275     try_anat (tm->dst_ipv4,
276               ntohs (tm->data),
277               (int) ntohl (tm->is_tcp));
278   else if (GNUNET_YES == ntohl (tm->is_tcp))
279     try_send_tcp (tm->dst_ipv4,
280                   dport,
281                   tm->data);
282   else
283     try_send_udp (tm->dst_ipv4,
284                   dport,
285                   tm->data);
286   GNUNET_SERVICE_client_drop (cd->client);
287 }
288
289
290 /**
291  * Main function that will be run.
292  *
293  * @param cls closure
294  * @param c configuration
295  * @param srv service handle
296  */
297 static void
298 run (void *cls,
299      const struct GNUNET_CONFIGURATION_Handle *c,
300      struct GNUNET_SERVICE_Handle *srv)
301 {
302   cfg = c;
303 }
304
305
306 /**
307  * Forcefully drops client after 1s.
308  *
309  * @param cls our `struct ClientData` of a client to drop
310  */
311 static void
312 force_timeout (void *cls)
313 {
314   struct ClientData *cd = cls;
315
316   cd->tt = NULL;
317   GNUNET_SERVICE_client_drop (cd->client);
318 }
319
320
321
322 /**
323  * Callback called when a client connects to the service.
324  *
325  * @param cls closure for the service
326  * @param c the new client that connected to the service
327  * @param mq the message queue used to send messages to the client
328  * @return our `struct ClientData`
329  */
330 static void *
331 client_connect_cb (void *cls,
332                    struct GNUNET_SERVICE_Client *c,
333                    struct GNUNET_MQ_Handle *mq)
334 {
335   struct ClientData *cd;
336
337   cd = GNUNET_new (struct ClientData);
338   cd->client = c;
339   cd->tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
340                                          &force_timeout,
341                                          cd);
342   return cd;
343 }
344
345
346 /**
347  * Callback called when a client disconnected from the service
348  *
349  * @param cls closure for the service
350  * @param c the client that disconnected
351  * @param internal_cls our `struct ClientData`
352  */
353 static void
354 client_disconnect_cb (void *cls,
355                       struct GNUNET_SERVICE_Client *c,
356                       void *internal_cls)
357 {
358   struct ClientData *cd = internal_cls;
359
360   if (NULL != cd->tt)
361     GNUNET_SCHEDULER_cancel (cd->tt);
362   GNUNET_free (cd);
363 }
364
365
366 /**
367  * Define "main" method using service macro.
368  */
369 GNUNET_SERVICE_MAIN
370 ("nat-server",
371  GNUNET_SERVICE_OPTION_NONE,
372  &run,
373  &client_connect_cb,
374  &client_disconnect_cb,
375  NULL,
376  GNUNET_MQ_hd_fixed_size (test,
377                           GNUNET_MESSAGE_TYPE_NAT_TEST,
378                           struct GNUNET_NAT_AUTO_TestMessage,
379                           NULL),
380  GNUNET_MQ_handler_end ());
381
382
383 #if defined(LINUX) && defined(__GLIBC__)
384 #include <malloc.h>
385
386 /**
387  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
388  */
389 void __attribute__ ((constructor))
390 GNUNET_ARM_memory_init ()
391 {
392   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
393   mallopt (M_TOP_PAD, 1 * 1024);
394   malloc_trim (0);
395 }
396 #endif
397
398
399
400
401 /* end of gnunet-nat-server.c */