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