Re-resolve the STUN address if a STUN poll fails for res_stun_monitor.
[asterisk/asterisk.git] / res / res_stun_monitor.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * David Vossel <dvossel@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief STUN Network Monitor
22  *
23  * \author David Vossel <dvossel@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include "asterisk/module.h"
35 #include "asterisk/event.h"
36 #include "asterisk/sched.h"
37 #include "asterisk/config.h"
38 #include "asterisk/stun.h"
39 #include "asterisk/netsock2.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/acl.h"
42 #include <fcntl.h>
43
44 #define DEFAULT_MONITOR_REFRESH 30      /*!< Default refresh period in seconds */
45
46 static const char stun_conf_file[] = "res_stun_monitor.conf";
47 static struct ast_sched_context *sched;
48
49 static struct {
50         /*! STUN monitor protection lock. */
51         ast_mutex_t lock;
52         /*! Current perceived external address. */
53         struct sockaddr_in external_addr;
54         /*! STUN server host name. */
55         const char *server_hostname;
56         /*! Port of STUN server to use */
57         unsigned int stun_port;
58         /*! Number of seconds between polls to the STUN server for the external address. */
59         unsigned int refresh;
60         /*! Monitoring STUN socket. */
61         int stun_sock;
62         /*! TRUE if the STUN monitor is enabled. */
63         unsigned int monitor_enabled:1;
64         /*! TRUE if the perceived external address is valid/known. */
65         unsigned int external_addr_known:1;
66         /*! TRUE if we have already griped about a STUN poll failing. */
67         unsigned int stun_poll_failed_gripe:1;
68 } args;
69
70 static void stun_close_sock(void)
71 {
72         if (0 <= args.stun_sock) {
73                 close(args.stun_sock);
74                 args.stun_sock = -1;
75         }
76 }
77
78 /* \brief called by scheduler to send STUN request */
79 static int stun_monitor_request(const void *blarg)
80 {
81         int res;
82         struct sockaddr_in answer;
83         static const struct sockaddr_in no_addr = { 0, };
84
85         ast_mutex_lock(&args.lock);
86         if (!args.monitor_enabled) {
87                 goto monitor_request_cleanup;
88         }
89
90         if (args.stun_sock < 0) {
91                 struct ast_sockaddr stun_addr;
92
93                 /* STUN socket not open.  Refresh the server DNS address resolution. */
94                 if (!args.server_hostname) {
95                         /* No STUN hostname? */
96                         goto monitor_request_cleanup;
97                 }
98
99                 /* Lookup STUN address. */
100                 memset(&stun_addr, 0, sizeof(stun_addr));
101                 stun_addr.ss.ss_family = AF_INET;
102                 if (ast_get_ip(&stun_addr, args.server_hostname)) {
103                         /* Lookup failed. */
104                         ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n",
105                                 args.server_hostname);
106                         goto monitor_request_cleanup;
107                 }
108                 ast_sockaddr_set_port(&stun_addr, args.stun_port);
109
110                 /* open socket binding */
111                 args.stun_sock = socket(AF_INET, SOCK_DGRAM, 0);
112                 if (args.stun_sock < 0) {
113                         ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
114                         goto monitor_request_cleanup;
115                 }
116                 if (ast_connect(args.stun_sock, &stun_addr)) {
117                         ast_log(LOG_WARNING, "STUN Failed to connect to %s: %s\n",
118                                 ast_sockaddr_stringify(&stun_addr), strerror(errno));
119                         stun_close_sock();
120                         goto monitor_request_cleanup;
121                 }
122         }
123
124         res = ast_stun_request(args.stun_sock, NULL, NULL, &answer);
125         if (res) {
126                 /*
127                  * STUN request timed out or errored.
128                  *
129                  * Refresh the server DNS address resolution next time around.
130                  */
131                 if (!args.stun_poll_failed_gripe) {
132                         args.stun_poll_failed_gripe = 1;
133                         ast_log(LOG_WARNING, "STUN poll %s. Re-evaluating STUN server address.\n",
134                                 res < 0 ? "failed" : "got no response");
135                 }
136                 stun_close_sock();
137         } else {
138                 args.stun_poll_failed_gripe = 0;
139                 if (memcmp(&no_addr, &answer, sizeof(no_addr))
140                         && memcmp(&args.external_addr, &answer, sizeof(args.external_addr))) {
141                         const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
142                         int newport = ntohs(answer.sin_port);
143
144                         ast_log(LOG_NOTICE, "Old external address/port %s:%d now seen as %s:%d.\n",
145                                 ast_inet_ntoa(args.external_addr.sin_addr),
146                                 ntohs(args.external_addr.sin_port), newaddr, newport);
147
148                         args.external_addr = answer;
149
150                         if (args.external_addr_known) {
151                                 struct ast_event *event;
152
153                                 /*
154                                  * The external address was already known, and has changed...
155                                  * generate event.
156                                  */
157                                 event = ast_event_new(AST_EVENT_NETWORK_CHANGE, AST_EVENT_IE_END);
158                                 if (!event) {
159                                         ast_log(LOG_ERROR, "Could not create AST_EVENT_NETWORK_CHANGE event.\n");
160                                 } else if (ast_event_queue(event)) {
161                                         ast_event_destroy(event);
162                                         ast_log(LOG_ERROR, "Could not queue AST_EVENT_NETWORK_CHANGE event.\n");
163                                 }
164                         } else {
165                                 /* this was the first external address we found, do not alert listeners
166                                  * until this address changes to something else. */
167                                 args.external_addr_known = 1;
168                         }
169                 }
170         }
171
172 monitor_request_cleanup:
173         /* always refresh this scheduler item.  It will be removed elsewhere when
174          * it is supposed to go away */
175         res = args.refresh * 1000;
176         ast_mutex_unlock(&args.lock);
177
178         return res;
179 }
180
181 /*!
182  * \internal
183  * \brief Stops the STUN monitor thread.
184  *
185  * \note do not hold the args->lock while calling this
186  *
187  * \return Nothing
188  */
189 static void stun_stop_monitor(void)
190 {
191         ast_mutex_lock(&args.lock);
192         args.monitor_enabled = 0;
193         ast_free((char *) args.server_hostname);
194         args.server_hostname = NULL;
195         stun_close_sock();
196         ast_mutex_unlock(&args.lock);
197
198         if (sched) {
199                 ast_sched_context_destroy(sched);
200                 sched = NULL;
201                 ast_log(LOG_NOTICE, "STUN monitor stopped\n");
202         }
203 }
204
205 /*!
206  * \internal
207  * \brief Starts the STUN monitor thread.
208  *
209  * \note The args->lock MUST be held when calling this function
210  *
211  * \return Nothing
212  */
213 static int stun_start_monitor(void)
214 {
215         /* if scheduler thread is not started, make sure to start it now */
216         if (sched) {
217                 return 0; /* already started */
218         }
219
220         if (!(sched = ast_sched_context_create())) {
221                 ast_log(LOG_ERROR, "Failed to create stun monitor scheduler context\n");
222                 return -1;
223         }
224
225         if (ast_sched_start_thread(sched)) {
226                 ast_sched_context_destroy(sched);
227                 sched = NULL;
228                 stun_close_sock();
229                 return -1;
230         }
231
232         if (ast_sched_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
233                 ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
234                 ast_sched_context_destroy(sched);
235                 sched = NULL;
236                 return -1;
237         }
238
239         ast_log(LOG_NOTICE, "STUN monitor started\n");
240
241         return 0;
242 }
243
244 /*!
245  * \internal
246  * \brief Parse and setup the stunaddr parameter.
247  *
248  * \param value Configuration parameter variable value.
249  *
250  * \retval 0 on success.
251  * \retval -1 on error.
252  */
253 static int setup_stunaddr(const char *value)
254 {
255         char *val;
256         char *host_str;
257         char *port_str;
258         unsigned int port;
259         struct ast_sockaddr stun_addr;
260
261         if (ast_strlen_zero(value)) {
262                 /* Setting to an empty value disables STUN monitoring. */
263                 args.monitor_enabled = 0;
264                 return 0;
265         }
266
267         val = ast_strdupa(value);
268         if (!ast_sockaddr_split_hostport(val, &host_str, &port_str, 0)
269                 || ast_strlen_zero(host_str)) {
270                 return -1;
271         }
272
273         /* Determine STUN port */
274         if (ast_strlen_zero(port_str)
275                 || 1 != sscanf(port_str, "%30u", &port)) {
276                 port = STANDARD_STUN_PORT;
277         }
278
279         host_str = ast_strdup(host_str);
280         if (!host_str) {
281                 return -1;
282         }
283
284         /* Lookup STUN address. */
285         memset(&stun_addr, 0, sizeof(stun_addr));
286         stun_addr.ss.ss_family = AF_INET;
287         if (ast_get_ip(&stun_addr, host_str)) {
288                 ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n", host_str);
289                 ast_free(host_str);
290                 return -1;
291         }
292
293         /* Save STUN server information. */
294         ast_free((char *) args.server_hostname);
295         args.server_hostname = host_str;
296         args.stun_port = port;
297
298         /* Enable STUN monitor */
299         args.monitor_enabled = 1;
300         return 0;
301 }
302
303 static int load_config(int startup)
304 {
305         struct ast_flags config_flags = { 0, };
306         struct ast_config *cfg;
307         struct ast_variable *v;
308
309         if (!startup) {
310                 ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
311         }
312
313         cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags);
314         if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
315                 ast_log(LOG_WARNING, "Unable to load config %s\n", stun_conf_file);
316                 return -1;
317         }
318         if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
319                 return 0;
320         }
321
322         /* clean up any previous open socket */
323         stun_close_sock();
324         args.stun_poll_failed_gripe = 0;
325
326         /* set defaults */
327         args.monitor_enabled = 0;
328         args.refresh = DEFAULT_MONITOR_REFRESH;
329
330         for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
331                 if (!strcasecmp(v->name, "stunaddr")) {
332                         if (setup_stunaddr(v->value)) {
333                                 ast_log(LOG_WARNING, "Invalid STUN server address: %s at line %d\n",
334                                         v->value, v->lineno);
335                         }
336                 } else if (!strcasecmp(v->name, "stunrefresh")) {
337                         if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
338                                 ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
339                                 args.refresh = DEFAULT_MONITOR_REFRESH;
340                         }
341                 } else {
342                         ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
343                                 v->value, v->lineno);
344                 }
345         }
346
347         ast_config_destroy(cfg);
348
349         return 0;
350 }
351
352 static int __reload(int startup)
353 {
354         int res;
355
356         ast_mutex_lock(&args.lock);
357         if (!(res = load_config(startup)) && args.monitor_enabled) {
358                 res = stun_start_monitor();
359         }
360         ast_mutex_unlock(&args.lock);
361
362         if (res < 0 || !args.monitor_enabled) {
363                 stun_stop_monitor();
364         }
365
366         return res;
367 }
368
369 static int reload(void)
370 {
371         return __reload(0);
372 }
373
374 static int unload_module(void)
375 {
376         stun_stop_monitor();
377         ast_mutex_destroy(&args.lock);
378         return 0;
379 }
380
381 static int load_module(void)
382 {
383         ast_mutex_init(&args.lock);
384         args.stun_sock = -1;
385         if (__reload(1)) {
386                 ast_mutex_destroy(&args.lock);
387                 return AST_MODULE_LOAD_DECLINE;
388         }
389
390         return AST_MODULE_LOAD_SUCCESS;
391 }
392
393 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
394                 .load = load_module,
395                 .unload = unload_module,
396                 .reload = reload,
397                 .load_pri = AST_MODPRI_CHANNEL_DEPEND
398         );