2 This file is part of GNUnet.
3 Copyright (C) 2011, 2017 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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
27 #include "gnunet_util_lib.h"
28 #include "gnunet_nat_service.h"
29 #include "gnunet_protocols.h"
34 * Information we track per client.
40 struct GNUNET_SCHEDULER_Task *tt;
45 struct GNUNET_SERVICE_Client *client;
52 static const struct GNUNET_CONFIGURATION_Handle *cfg;
56 * Try contacting the peer using autonomous NAT traveral method.
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)
63 try_anat(uint32_t dst_ipv4,
67 struct GNUNET_NAT_Handle *h;
68 struct sockaddr_in lsa;
69 struct sockaddr_in rsa;
70 const struct sockaddr *sa;
73 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
74 "Asking for connection reversal with %x and code %u\n",
75 (unsigned int)dst_ipv4,
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);
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);
89 rsa.sin_addr.s_addr = dst_ipv4;
90 rsa.sin_port = htons(dport);
92 sa = (const struct sockaddr *)&lsa;
93 h = GNUNET_NAT_register(cfg,
95 is_tcp ? IPPROTO_TCP : IPPROTO_UDP,
100 GNUNET_NAT_request_reversal(h,
103 GNUNET_NAT_unregister(h);
108 * Closure for #tcp_send.
114 struct GNUNET_NETWORK_Handle *s;
124 * Task called by the scheduler once we can do the TCP send
125 * (or once we failed to connect...).
127 * @param cls the `struct TcpContext`
132 struct TcpContext *ctx = cls;
133 const struct GNUNET_SCHEDULER_TaskContext *tc;
135 tc = GNUNET_SCHEDULER_get_task_context();
136 if ((NULL != tc->write_ready) &&
137 (GNUNET_NETWORK_fdset_isset(tc->write_ready, ctx->s)))
140 GNUNET_NETWORK_socket_send(ctx->s, &ctx->data, sizeof(ctx->data)))
142 GNUNET_log_strerror(GNUNET_ERROR_TYPE_DEBUG, "send");
144 GNUNET_NETWORK_socket_shutdown(ctx->s, SHUT_RDWR);
146 GNUNET_NETWORK_socket_close(ctx->s);
152 * Try to send @a data to the
153 * IP @a dst_ipv4' at port @a dport via TCP.
155 * @param dst_ipv4 target IP
156 * @param dport target port
157 * @param data data to send
160 try_send_tcp(uint32_t dst_ipv4,
164 struct GNUNET_NETWORK_Handle *s;
165 struct sockaddr_in sa;
166 struct TcpContext *ctx;
168 s = GNUNET_NETWORK_socket_create(AF_INET,
173 GNUNET_log_strerror(GNUNET_ERROR_TYPE_WARNING,
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);
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,
189 GNUNET_NETWORK_socket_connect(s,
190 (const struct sockaddr *)&sa,
192 (errno != EINPROGRESS))
194 GNUNET_log_strerror(GNUNET_ERROR_TYPE_WARNING,
196 GNUNET_NETWORK_socket_close(s);
199 ctx = GNUNET_new(struct TcpContext);
202 GNUNET_SCHEDULER_add_write_net(GNUNET_TIME_UNIT_SECONDS,
210 * Try to send @a data to the
211 * IP @a dst_ipv4 at port @a dport via UDP.
213 * @param dst_ipv4 target IP
214 * @param dport target port
215 * @param data data to send
218 try_send_udp(uint32_t dst_ipv4,
222 struct GNUNET_NETWORK_Handle *s;
223 struct sockaddr_in sa;
225 s = GNUNET_NETWORK_socket_create(AF_INET,
230 GNUNET_log_strerror(GNUNET_ERROR_TYPE_WARNING,
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);
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,
246 GNUNET_NETWORK_socket_sendto(s,
249 (const struct sockaddr *)&sa,
251 GNUNET_log_strerror(GNUNET_ERROR_TYPE_WARNING,
253 GNUNET_NETWORK_socket_close(s);
258 * We've received a request to probe a NAT
261 * @param cls handle to client (we always close)
262 * @param msg message with details about what to test
265 handle_test(void *cls,
266 const struct GNUNET_NAT_AUTO_TestMessage *tm)
268 struct ClientData *cd = cls;
271 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
272 "Received test request\n");
273 dport = ntohs(tm->dport);
275 try_anat(tm->dst_ipv4,
277 (int)ntohl(tm->is_tcp));
278 else if (GNUNET_YES == ntohl(tm->is_tcp))
279 try_send_tcp(tm->dst_ipv4,
283 try_send_udp(tm->dst_ipv4,
286 GNUNET_SERVICE_client_drop(cd->client);
291 * Main function that will be run.
294 * @param c configuration
295 * @param srv service handle
299 const struct GNUNET_CONFIGURATION_Handle *c,
300 struct GNUNET_SERVICE_Handle *srv)
307 * Forcefully drops client after 1s.
309 * @param cls our `struct ClientData` of a client to drop
312 force_timeout(void *cls)
314 struct ClientData *cd = cls;
317 GNUNET_SERVICE_client_drop(cd->client);
323 * Callback called when a client connects to the service.
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`
331 client_connect_cb(void *cls,
332 struct GNUNET_SERVICE_Client *c,
333 struct GNUNET_MQ_Handle *mq)
335 struct ClientData *cd;
337 cd = GNUNET_new(struct ClientData);
339 cd->tt = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS,
347 * Callback called when a client disconnected from the service
349 * @param cls closure for the service
350 * @param c the client that disconnected
351 * @param internal_cls our `struct ClientData`
354 client_disconnect_cb(void *cls,
355 struct GNUNET_SERVICE_Client *c,
358 struct ClientData *cd = internal_cls;
361 GNUNET_SCHEDULER_cancel(cd->tt);
367 * Define "main" method using service macro.
371 GNUNET_SERVICE_OPTION_NONE,
374 &client_disconnect_cb,
376 GNUNET_MQ_hd_fixed_size(test,
377 GNUNET_MESSAGE_TYPE_NAT_TEST,
378 struct GNUNET_NAT_AUTO_TestMessage,
380 GNUNET_MQ_handler_end());
383 #if defined(LINUX) && defined(__GLIBC__)
387 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
389 void __attribute__ ((constructor))
390 GNUNET_ARM_memory_init()
392 mallopt(M_TRIM_THRESHOLD, 4 * 1024);
393 mallopt(M_TOP_PAD, 1 * 1024);
401 /* end of gnunet-nat-server.c */