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