uncrustify as demanded.
[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      SPDX-License-Identifier: AGPL3.0-or-later
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    * Timeout task.
39    */
40   struct GNUNET_SCHEDULER_Task *tt;
41
42   /**
43    * Client handle.
44    */
45   struct GNUNET_SERVICE_Client *client;
46 };
47
48
49 /**
50  * Our configuration.
51  */
52 static const struct GNUNET_CONFIGURATION_Handle *cfg;
53
54
55 /**
56  * Try contacting the peer using autonomous NAT traveral method.
57  *
58  * @param dst_ipv4 IPv4 address to send the fake ICMP message
59  * @param dport destination port to include in ICMP message
60  * @param is_tcp mark for TCP (#GNUNET_YES)  or UDP (#GNUNET_NO)
61  */
62 static void
63 try_anat(uint32_t dst_ipv4,
64          uint16_t dport,
65          int is_tcp)
66 {
67   struct GNUNET_NAT_Handle *h;
68   struct sockaddr_in lsa;
69   struct sockaddr_in rsa;
70   const struct sockaddr *sa;
71   socklen_t sa_len;
72
73   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
74              "Asking for connection reversal with %x and code %u\n",
75              (unsigned int)dst_ipv4,
76              (unsigned int)dport);
77   memset(&lsa, 0, sizeof(lsa));
78   lsa.sin_family = AF_INET;
79 #if HAVE_SOCKADDR_IN_SIN_LEN
80   lsa.sin_len = sizeof(sa);
81 #endif
82   lsa.sin_addr.s_addr = 0;
83   lsa.sin_port = htons(dport);
84   memset(&rsa, 0, sizeof(rsa));
85   rsa.sin_family = AF_INET;
86 #if HAVE_SOCKADDR_IN_SIN_LEN
87   rsa.sin_len = sizeof(sa);
88 #endif
89   rsa.sin_addr.s_addr = dst_ipv4;
90   rsa.sin_port = htons(dport);
91   sa_len = sizeof(lsa);
92   sa = (const struct sockaddr *)&lsa;
93   h = GNUNET_NAT_register(cfg,
94                           "none",
95                           is_tcp ? IPPROTO_TCP : IPPROTO_UDP,
96                           1,
97                           &sa,
98                           &sa_len,
99                           NULL, NULL, NULL);
100   GNUNET_NAT_request_reversal(h,
101                               &lsa,
102                               &rsa);
103   GNUNET_NAT_unregister(h);
104 }
105
106
107 /**
108  * Closure for #tcp_send.
109  */
110 struct TcpContext {
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 */