diff options
Diffstat (limited to 'src/nat-auto/gnunet-service-nat-auto.c')
-rw-r--r-- | src/nat-auto/gnunet-service-nat-auto.c | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/src/nat-auto/gnunet-service-nat-auto.c b/src/nat-auto/gnunet-service-nat-auto.c new file mode 100644 index 000000000..897d6feb2 --- /dev/null +++ b/src/nat-auto/gnunet-service-nat-auto.c | |||
@@ -0,0 +1,481 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2016, 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 nat-auto/gnunet-service-nat-auto.c | ||
23 | * @brief NAT autoconfiguration service | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * TODO: | ||
27 | * - merge client handle and autoconfig context | ||
28 | * - implement "more" autoconfig: | ||
29 | * + re-work gnunet-nat-server & integrate! | ||
30 | * + test manually punched NAT (how?) | ||
31 | */ | ||
32 | #include "platform.h" | ||
33 | #include <math.h> | ||
34 | #include "gnunet_util_lib.h" | ||
35 | #include "gnunet_protocols.h" | ||
36 | #include "gnunet_signatures.h" | ||
37 | #include "gnunet_nat_service.h" | ||
38 | #include "gnunet_statistics_service.h" | ||
39 | #include "gnunet_resolver_service.h" | ||
40 | #include "nat-auto.h" | ||
41 | #include <gcrypt.h> | ||
42 | |||
43 | |||
44 | /** | ||
45 | * How long do we wait until we forcefully terminate autoconfiguration? | ||
46 | */ | ||
47 | #define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) | ||
48 | |||
49 | |||
50 | /** | ||
51 | * Internal data structure we track for each of our clients. | ||
52 | */ | ||
53 | struct ClientHandle | ||
54 | { | ||
55 | |||
56 | /** | ||
57 | * Kept in a DLL. | ||
58 | */ | ||
59 | struct ClientHandle *next; | ||
60 | |||
61 | /** | ||
62 | * Kept in a DLL. | ||
63 | */ | ||
64 | struct ClientHandle *prev; | ||
65 | |||
66 | /** | ||
67 | * Underlying handle for this client with the service. | ||
68 | */ | ||
69 | struct GNUNET_SERVICE_Client *client; | ||
70 | |||
71 | /** | ||
72 | * Message queue for communicating with the client. | ||
73 | */ | ||
74 | struct GNUNET_MQ_Handle *mq; | ||
75 | }; | ||
76 | |||
77 | |||
78 | /** | ||
79 | * Context for autoconfiguration operations. | ||
80 | */ | ||
81 | struct AutoconfigContext | ||
82 | { | ||
83 | /** | ||
84 | * Kept in a DLL. | ||
85 | */ | ||
86 | struct AutoconfigContext *prev; | ||
87 | |||
88 | /** | ||
89 | * Kept in a DLL. | ||
90 | */ | ||
91 | struct AutoconfigContext *next; | ||
92 | |||
93 | /** | ||
94 | * Which client asked the question. | ||
95 | */ | ||
96 | struct ClientHandle *ch; | ||
97 | |||
98 | /** | ||
99 | * Configuration we are creating. | ||
100 | */ | ||
101 | struct GNUNET_CONFIGURATION_Handle *c; | ||
102 | |||
103 | /** | ||
104 | * Original configuration (for diffing). | ||
105 | */ | ||
106 | struct GNUNET_CONFIGURATION_Handle *orig; | ||
107 | |||
108 | /** | ||
109 | * Timeout task to force termination. | ||
110 | */ | ||
111 | struct GNUNET_SCHEDULER_Task *timeout_task; | ||
112 | |||
113 | /** | ||
114 | * #GNUNET_YES if upnpc should be used, | ||
115 | * #GNUNET_NO if upnpc should not be used, | ||
116 | * #GNUNET_SYSERR if we should simply not change the option. | ||
117 | */ | ||
118 | int enable_upnpc; | ||
119 | |||
120 | /** | ||
121 | * Status code to return to the client. | ||
122 | */ | ||
123 | enum GNUNET_NAT_StatusCode status_code; | ||
124 | |||
125 | /** | ||
126 | * NAT type to return to the client. | ||
127 | */ | ||
128 | enum GNUNET_NAT_Type type; | ||
129 | }; | ||
130 | |||
131 | |||
132 | /** | ||
133 | * Head of client DLL. | ||
134 | */ | ||
135 | static struct ClientHandle *ch_head; | ||
136 | |||
137 | /** | ||
138 | * Tail of client DLL. | ||
139 | */ | ||
140 | static struct ClientHandle *ch_tail; | ||
141 | |||
142 | /** | ||
143 | * DLL of our autoconfiguration operations. | ||
144 | */ | ||
145 | static struct AutoconfigContext *ac_head; | ||
146 | |||
147 | /** | ||
148 | * DLL of our autoconfiguration operations. | ||
149 | */ | ||
150 | static struct AutoconfigContext *ac_tail; | ||
151 | |||
152 | /** | ||
153 | * Handle to our current configuration. | ||
154 | */ | ||
155 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
156 | |||
157 | /** | ||
158 | * Handle to the statistics service. | ||
159 | */ | ||
160 | static struct GNUNET_STATISTICS_Handle *stats; | ||
161 | |||
162 | |||
163 | /** | ||
164 | * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message | ||
165 | * from client. | ||
166 | * | ||
167 | * @param cls client who sent the message | ||
168 | * @param message the message received | ||
169 | * @return #GNUNET_OK if message is well-formed | ||
170 | */ | ||
171 | static int | ||
172 | check_autoconfig_request (void *cls, | ||
173 | const struct GNUNET_NAT_AutoconfigRequestMessage *message) | ||
174 | { | ||
175 | return GNUNET_OK; /* checked later */ | ||
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * Stop all pending activities with respect to the @a ac | ||
181 | * | ||
182 | * @param ac autoconfiguration to terminate activities for | ||
183 | */ | ||
184 | static void | ||
185 | terminate_ac_activities (struct AutoconfigContext *ac) | ||
186 | { | ||
187 | if (NULL != ac->timeout_task) | ||
188 | { | ||
189 | GNUNET_SCHEDULER_cancel (ac->timeout_task); | ||
190 | ac->timeout_task = NULL; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | |||
195 | /** | ||
196 | * Finish handling the autoconfiguration request and send | ||
197 | * the response to the client. | ||
198 | * | ||
199 | * @param cls the `struct AutoconfigContext` to conclude | ||
200 | */ | ||
201 | static void | ||
202 | conclude_autoconfig_request (void *cls) | ||
203 | { | ||
204 | struct AutoconfigContext *ac = cls; | ||
205 | struct ClientHandle *ch = ac->ch; | ||
206 | struct GNUNET_NAT_AutoconfigResultMessage *arm; | ||
207 | struct GNUNET_MQ_Envelope *env; | ||
208 | size_t c_size; | ||
209 | char *buf; | ||
210 | struct GNUNET_CONFIGURATION_Handle *diff; | ||
211 | |||
212 | ac->timeout_task = NULL; | ||
213 | terminate_ac_activities (ac); | ||
214 | |||
215 | /* Send back response */ | ||
216 | diff = GNUNET_CONFIGURATION_get_diff (ac->orig, | ||
217 | ac->c); | ||
218 | buf = GNUNET_CONFIGURATION_serialize (diff, | ||
219 | &c_size); | ||
220 | GNUNET_CONFIGURATION_destroy (diff); | ||
221 | env = GNUNET_MQ_msg_extra (arm, | ||
222 | c_size, | ||
223 | GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT); | ||
224 | arm->status_code = htonl ((uint32_t) ac->status_code); | ||
225 | arm->type = htonl ((uint32_t) ac->type); | ||
226 | GNUNET_memcpy (&arm[1], | ||
227 | buf, | ||
228 | c_size); | ||
229 | GNUNET_free (buf); | ||
230 | GNUNET_MQ_send (ch->mq, | ||
231 | env); | ||
232 | |||
233 | /* clean up */ | ||
234 | GNUNET_CONFIGURATION_destroy (ac->orig); | ||
235 | GNUNET_CONFIGURATION_destroy (ac->c); | ||
236 | GNUNET_CONTAINER_DLL_remove (ac_head, | ||
237 | ac_tail, | ||
238 | ac); | ||
239 | GNUNET_free (ac); | ||
240 | GNUNET_SERVICE_client_continue (ch->client); | ||
241 | } | ||
242 | |||
243 | |||
244 | /** | ||
245 | * Check if all autoconfiguration operations have concluded, | ||
246 | * and if they have, send the result back to the client. | ||
247 | * | ||
248 | * @param ac autoconfiguation context to check | ||
249 | */ | ||
250 | static void | ||
251 | check_autoconfig_finished (struct AutoconfigContext *ac) | ||
252 | { | ||
253 | GNUNET_SCHEDULER_cancel (ac->timeout_task); | ||
254 | ac->timeout_task | ||
255 | = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request, | ||
256 | ac); | ||
257 | } | ||
258 | |||
259 | |||
260 | /** | ||
261 | * Update ENABLE_UPNPC configuration option. | ||
262 | * | ||
263 | * @param ac autoconfiguration to update | ||
264 | */ | ||
265 | static void | ||
266 | update_enable_upnpc_option (struct AutoconfigContext *ac) | ||
267 | { | ||
268 | switch (ac->enable_upnpc) | ||
269 | { | ||
270 | case GNUNET_YES: | ||
271 | GNUNET_CONFIGURATION_set_value_string (ac->c, | ||
272 | "NAT", | ||
273 | "ENABLE_UPNP", | ||
274 | "YES"); | ||
275 | break; | ||
276 | case GNUNET_NO: | ||
277 | GNUNET_CONFIGURATION_set_value_string (ac->c, | ||
278 | "NAT", | ||
279 | "ENABLE_UPNP", | ||
280 | "NO"); | ||
281 | break; | ||
282 | case GNUNET_SYSERR: | ||
283 | /* We are unsure, do not change option */ | ||
284 | break; | ||
285 | } | ||
286 | } | ||
287 | |||
288 | |||
289 | /** | ||
290 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from | ||
291 | * client. | ||
292 | * | ||
293 | * @param cls client who sent the message | ||
294 | * @param message the message received | ||
295 | */ | ||
296 | static void | ||
297 | handle_autoconfig_request (void *cls, | ||
298 | const struct GNUNET_NAT_AutoconfigRequestMessage *message) | ||
299 | { | ||
300 | struct ClientHandle *ch = cls; | ||
301 | size_t left = ntohs (message->header.size) - sizeof (*message); | ||
302 | struct AutoconfigContext *ac; | ||
303 | |||
304 | ac = GNUNET_new (struct AutoconfigContext); | ||
305 | ac->status_code = GNUNET_NAT_ERROR_SUCCESS; | ||
306 | ac->ch = ch; | ||
307 | ac->c = GNUNET_CONFIGURATION_create (); | ||
308 | if (GNUNET_OK != | ||
309 | GNUNET_CONFIGURATION_deserialize (ac->c, | ||
310 | (const char *) &message[1], | ||
311 | left, | ||
312 | GNUNET_NO)) | ||
313 | { | ||
314 | GNUNET_break (0); | ||
315 | GNUNET_SERVICE_client_drop (ch->client); | ||
316 | GNUNET_CONFIGURATION_destroy (ac->c); | ||
317 | GNUNET_free (ac); | ||
318 | return; | ||
319 | } | ||
320 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
321 | "Received REQUEST_AUTO_CONFIG message from client\n"); | ||
322 | |||
323 | GNUNET_CONTAINER_DLL_insert (ac_head, | ||
324 | ac_tail, | ||
325 | ac); | ||
326 | ac->orig | ||
327 | = GNUNET_CONFIGURATION_dup (ac->c); | ||
328 | ac->timeout_task | ||
329 | = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT, | ||
330 | &conclude_autoconfig_request, | ||
331 | ac); | ||
332 | ac->enable_upnpc = GNUNET_SYSERR; /* undecided */ | ||
333 | |||
334 | /* Probe for upnpc */ | ||
335 | if (GNUNET_SYSERR == | ||
336 | GNUNET_OS_check_helper_binary ("upnpc", | ||
337 | GNUNET_NO, | ||
338 | NULL)) | ||
339 | { | ||
340 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
341 | _("UPnP client `upnpc` command not found, disabling UPnP\n")); | ||
342 | ac->enable_upnpc = GNUNET_NO; | ||
343 | } | ||
344 | else | ||
345 | { | ||
346 | /* We might at some point be behind NAT, try upnpc */ | ||
347 | ac->enable_upnpc = GNUNET_YES; | ||
348 | } | ||
349 | update_enable_upnpc_option (ac); | ||
350 | |||
351 | /* Finally, check if we are already done */ | ||
352 | check_autoconfig_finished (ac); | ||
353 | } | ||
354 | |||
355 | |||
356 | /** | ||
357 | * Task run during shutdown. | ||
358 | * | ||
359 | * @param cls unused | ||
360 | */ | ||
361 | static void | ||
362 | shutdown_task (void *cls) | ||
363 | { | ||
364 | struct AutoconfigContext *ac; | ||
365 | |||
366 | while (NULL != (ac = ac_head)) | ||
367 | { | ||
368 | GNUNET_CONTAINER_DLL_remove (ac_head, | ||
369 | ac_tail, | ||
370 | ac); | ||
371 | terminate_ac_activities (ac); | ||
372 | GNUNET_free (ac); | ||
373 | } | ||
374 | if (NULL != stats) | ||
375 | { | ||
376 | GNUNET_STATISTICS_destroy (stats, | ||
377 | GNUNET_NO); | ||
378 | stats = NULL; | ||
379 | } | ||
380 | } | ||
381 | |||
382 | |||
383 | /** | ||
384 | * Setup NAT service. | ||
385 | * | ||
386 | * @param cls closure | ||
387 | * @param c configuration to use | ||
388 | * @param service the initialized service | ||
389 | */ | ||
390 | static void | ||
391 | run (void *cls, | ||
392 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
393 | struct GNUNET_SERVICE_Handle *service) | ||
394 | { | ||
395 | cfg = c; | ||
396 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
397 | NULL); | ||
398 | stats = GNUNET_STATISTICS_create ("nat-auto", | ||
399 | cfg); | ||
400 | } | ||
401 | |||
402 | |||
403 | /** | ||
404 | * Callback called when a client connects to the service. | ||
405 | * | ||
406 | * @param cls closure for the service | ||
407 | * @param c the new client that connected to the service | ||
408 | * @param mq the message queue used to send messages to the client | ||
409 | * @return a `struct ClientHandle` | ||
410 | */ | ||
411 | static void * | ||
412 | client_connect_cb (void *cls, | ||
413 | struct GNUNET_SERVICE_Client *c, | ||
414 | struct GNUNET_MQ_Handle *mq) | ||
415 | { | ||
416 | struct ClientHandle *ch; | ||
417 | |||
418 | ch = GNUNET_new (struct ClientHandle); | ||
419 | ch->mq = mq; | ||
420 | ch->client = c; | ||
421 | GNUNET_CONTAINER_DLL_insert (ch_head, | ||
422 | ch_tail, | ||
423 | ch); | ||
424 | return ch; | ||
425 | } | ||
426 | |||
427 | |||
428 | /** | ||
429 | * Callback called when a client disconnected from the service | ||
430 | * | ||
431 | * @param cls closure for the service | ||
432 | * @param c the client that disconnected | ||
433 | * @param internal_cls a `struct ClientHandle *` | ||
434 | */ | ||
435 | static void | ||
436 | client_disconnect_cb (void *cls, | ||
437 | struct GNUNET_SERVICE_Client *c, | ||
438 | void *internal_cls) | ||
439 | { | ||
440 | struct ClientHandle *ch = internal_cls; | ||
441 | |||
442 | GNUNET_CONTAINER_DLL_remove (ch_head, | ||
443 | ch_tail, | ||
444 | ch); | ||
445 | GNUNET_free (ch); | ||
446 | } | ||
447 | |||
448 | |||
449 | /** | ||
450 | * Define "main" method using service macro. | ||
451 | */ | ||
452 | GNUNET_SERVICE_MAIN | ||
453 | ("nat", | ||
454 | GNUNET_SERVICE_OPTION_NONE, | ||
455 | &run, | ||
456 | &client_connect_cb, | ||
457 | &client_disconnect_cb, | ||
458 | NULL, | ||
459 | GNUNET_MQ_hd_var_size (autoconfig_request, | ||
460 | GNUNET_MESSAGE_TYPE_NAT_AUTO_REQUEST_CFG, | ||
461 | struct GNUNET_NAT_AutoconfigRequestMessage, | ||
462 | NULL), | ||
463 | GNUNET_MQ_handler_end ()); | ||
464 | |||
465 | |||
466 | #if defined(LINUX) && defined(__GLIBC__) | ||
467 | #include <malloc.h> | ||
468 | |||
469 | /** | ||
470 | * MINIMIZE heap size (way below 128k) since this process doesn't need much. | ||
471 | */ | ||
472 | void __attribute__ ((constructor)) | ||
473 | GNUNET_ARM_memory_init () | ||
474 | { | ||
475 | mallopt (M_TRIM_THRESHOLD, 4 * 1024); | ||
476 | mallopt (M_TOP_PAD, 1 * 1024); | ||
477 | malloc_trim (0); | ||
478 | } | ||
479 | #endif | ||
480 | |||
481 | /* end of gnunet-service-nat.c */ | ||