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