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