glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / nat / gnunet-service-nat_externalip.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2015, 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 /**
16  * Code to figure out what our external IPv4 address(es) might
17  * be (external IPv4s are what is seen on the rest of the Internet).
18  *
19  * This can be implemented using different methods, and we allow
20  * the main service to be notified about changes to what we believe
21  * is our external IPv4 address.
22  *
23  * Note that this is explicitly only about NATed systems; if one
24  * of our network interfaces has a global IP address this does
25  * not count as "external".
26  *
27  * @file nat/gnunet-service-nat_externalip.c
28  * @brief Functions for monitoring external IPv4 addresses
29  * @author Christian Grothoff
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_statistics_service.h"
37 #include "gnunet_resolver_service.h"
38 #include "gnunet_nat_service.h"
39 #include "gnunet-service-nat.h"
40 #include "gnunet-service-nat_externalip.h"
41 #include "gnunet-service-nat_stun.h"
42 #include "gnunet-service-nat_mini.h"
43 #include "gnunet-service-nat_helper.h"
44 #include "nat.h"
45 #include <gcrypt.h>
46
47
48 /**
49  * How long do we wait until we re-try running `external-ip` if the
50  * command failed to terminate nicely?
51  */
52 #define EXTERN_IP_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
53
54 /**
55  * How long do we wait until we re-try running `external-ip` if the
56  * command failed (but terminated)?
57  */
58 #define EXTERN_IP_RETRY_FAILURE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
59
60 /**
61  * How long do we wait until we re-try running `external-ip` if the
62  * command succeeded?
63  */
64 #define EXTERN_IP_RETRY_SUCCESS GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
65
66
67 /**
68  * Handle to monitor for external IP changes.
69  */
70 struct GN_ExternalIPMonitor
71 {
72   /**
73    * Kept in DLL.
74    */
75   struct GN_ExternalIPMonitor *next;
76
77   /**
78    * Kept in DLL.
79    */
80   struct GN_ExternalIPMonitor *prev;
81
82   /**
83    * Function to call when we believe our external IPv4 address changed.
84    */
85   GN_NotifyExternalIPv4Change cb;
86
87   /**
88    * Closure for @e cb.
89    */
90   void *cb_cls;
91
92 };
93
94
95 /**
96  * List of monitors, kept in DLL.
97  */
98 static struct GN_ExternalIPMonitor *mon_head;
99
100 /**
101  * List of monitors, kept in DLL.
102  */
103 static struct GN_ExternalIPMonitor *mon_tail;
104
105 /**
106  * Task run to obtain our external IP (if #enable_upnp is set
107  * and if we find we have a NATed IP address).
108  */
109 static struct GNUNET_SCHEDULER_Task *probe_external_ip_task;
110
111 /**
112  * Handle to our operation to run `external-ip`.
113  */
114 static struct GNUNET_NAT_ExternalHandle *probe_external_ip_op;
115
116 /**
117  * What is our external IP address as claimed by `external-ip`?
118  * 0 for unknown.
119  */
120 static struct in_addr mini_external_ipv4;
121
122
123 /**
124  * Tell relevant clients about a change in our external
125  * IPv4 address.
126  *
127  * @param add #GNUNET_YES to add, #GNUNET_NO to remove
128  * @param v4 the external address that changed
129  */
130 static void
131 notify_monitors_external_ipv4_change (int add,
132                                       const struct in_addr *v4)
133 {
134   for (struct GN_ExternalIPMonitor *mon = mon_head;
135        NULL != mon;
136        mon = mon->next)
137     mon->cb (mon->cb_cls,
138              v4,
139              add);
140 }
141
142
143 /**
144  * Task used to run `external-ip` to get our external IPv4
145  * address and pass it to NATed clients if possible.
146  *
147  * @param cls NULL
148  */
149 static void
150 run_external_ip (void *cls);
151
152
153 /**
154  * We learn our current external IP address.  If it changed,
155  * notify all of our applicable clients. Also re-schedule
156  * #run_external_ip with an appropriate timeout.
157  *
158  * @param cls NULL
159  * @param addr the address, NULL on errors
160  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
161  */
162 static void
163 handle_external_ip (void *cls,
164                     const struct in_addr *addr,
165                     enum GNUNET_NAT_StatusCode result)
166 {
167   char buf[INET_ADDRSTRLEN];
168
169   probe_external_ip_op = NULL;
170   GNUNET_SCHEDULER_cancel (probe_external_ip_task);
171   probe_external_ip_task
172     = GNUNET_SCHEDULER_add_delayed ((NULL == addr)
173                                     ? EXTERN_IP_RETRY_FAILURE
174                                     : EXTERN_IP_RETRY_SUCCESS,
175                                     &run_external_ip,
176                                     NULL);
177   switch (result)
178   {
179   case GNUNET_NAT_ERROR_SUCCESS:
180     GNUNET_assert (NULL != addr);
181     if (addr->s_addr == mini_external_ipv4.s_addr)
182       return; /* not change */
183     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
184                 "Our external IP is now %s\n",
185                 inet_ntop (AF_INET,
186                            addr,
187                            buf,
188                            sizeof (buf)));
189     if (0 != mini_external_ipv4.s_addr)
190       notify_monitors_external_ipv4_change (GNUNET_NO,
191                                             &mini_external_ipv4);
192     mini_external_ipv4 = *addr;
193     notify_monitors_external_ipv4_change (GNUNET_YES,
194                                           &mini_external_ipv4);
195     break;
196   default:
197     if (0 != mini_external_ipv4.s_addr)
198       notify_monitors_external_ipv4_change (GNUNET_NO,
199                                             &mini_external_ipv4);
200     mini_external_ipv4.s_addr = 0;
201     break;
202   }
203 }
204
205
206 /**
207  * Task used to run `external-ip` to get our external IPv4
208  * address and pass it to NATed clients if possible.
209  *
210  * @param cls NULL
211  */
212 static void
213 run_external_ip (void *cls)
214 {
215   probe_external_ip_task
216     = GNUNET_SCHEDULER_add_delayed (EXTERN_IP_RETRY_TIMEOUT,
217                                     &run_external_ip,
218                                     NULL);
219   if (NULL != probe_external_ip_op)
220   {
221     GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
222     probe_external_ip_op = NULL;
223   }
224   probe_external_ip_op
225     = GNUNET_NAT_mini_get_external_ipv4_ (&handle_external_ip,
226                                           NULL);
227 }
228
229
230 /**
231  * We have changed our opinion about being NATed in the first
232  * place. Adapt our probing.
233  *
234  * @param have_nat #GNUNET_YES if we believe we are behind NAT
235  */
236 void
237 GN_nat_status_changed (int have_nat)
238 {
239   if (GNUNET_YES != enable_upnp)
240     return;
241   if ( (GNUNET_YES == have_nat) &&
242        (NULL == probe_external_ip_task) &&
243        (NULL == probe_external_ip_op) )
244   {
245     probe_external_ip_task
246       = GNUNET_SCHEDULER_add_now (&run_external_ip,
247                                   NULL);
248     return;
249   }
250   if (GNUNET_NO == have_nat)
251   {
252     if (NULL != probe_external_ip_task)
253     {
254       GNUNET_SCHEDULER_cancel (probe_external_ip_task);
255       probe_external_ip_task = NULL;
256     }
257     if (NULL != probe_external_ip_op)
258     {
259       GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
260       probe_external_ip_op = NULL;
261     }
262   }
263 }
264
265
266 /**
267  * Start monitoring external IPv4 addresses.
268  *
269  * @param cb function to call on changes
270  * @param cb_cls closure for @a cb
271  * @return handle to cancel
272  */
273 struct GN_ExternalIPMonitor *
274 GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb,
275                                 void *cb_cls)
276 {
277   struct GN_ExternalIPMonitor *mon;
278
279   mon = GNUNET_new (struct GN_ExternalIPMonitor);
280   mon->cb = cb;
281   mon->cb_cls = cb_cls;
282   GNUNET_CONTAINER_DLL_insert (mon_head,
283                                mon_tail,
284                                mon);
285   if (0 != mini_external_ipv4.s_addr)
286     cb (cb_cls,
287         &mini_external_ipv4,
288         GNUNET_YES);
289   return mon;
290 }
291
292
293 /**
294  * Stop calling monitor.
295  *
296  * @param mon monitor to call
297  */
298 void
299 GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon)
300 {
301   GNUNET_CONTAINER_DLL_remove (mon_head,
302                                mon_tail,
303                                mon);
304   GNUNET_free (mon);
305 }
306
307 /* end of gnunet-service-nat_externalip.c */