Merge "res_stasis: Fix stale data in ARI bridges"
[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 #include "asterisk/module.h"
33 #include "asterisk/sched.h"
34 #include "asterisk/config.h"
35 #include "asterisk/stun.h"
36 #include "asterisk/netsock2.h"
37 #include "asterisk/lock.h"
38 #include "asterisk/acl.h"
39 #include "asterisk/cli.h"
40 #include "asterisk/json.h"
41 #include "asterisk/stasis.h"
42 #include "asterisk/stasis_system.h"
43 #include "asterisk/astobj2.h"
44
45 #include <fcntl.h>
46
47 #define DEFAULT_MONITOR_REFRESH 30      /*!< Default refresh period in seconds */
48 #define DEFAULT_RETRIES 3               /*!< retries shown in stun show status
49                                              matching static retries in stun.c */
50
51 static const char stun_conf_file[] = "res_stun_monitor.conf";
52 static struct ast_sched_context *sched;
53
54 static struct {
55         /*! STUN monitor protection lock. */
56         ast_mutex_t lock;
57         /*! Current perceived external address. */
58         struct sockaddr_in external_addr;
59         /*! STUN server host name. */
60         const char *server_hostname;
61         /*! Port of STUN server to use */
62         unsigned int stun_port;
63         /*! Number of seconds between polls to the STUN server for the external address. */
64         unsigned int refresh;
65         /*! Monitoring STUN socket. */
66         int stun_sock;
67         /*! TRUE if the STUN monitor is enabled. */
68         unsigned int monitor_enabled:1;
69         /*! TRUE if the perceived external address is valid/known. */
70         unsigned int external_addr_known:1;
71         /*! TRUE if we have already griped about a STUN poll failing. */
72         unsigned int stun_poll_failed_gripe:1;
73 } args;
74
75 static void stun_close_sock(void)
76 {
77         if (0 <= args.stun_sock) {
78                 close(args.stun_sock);
79                 args.stun_sock = -1;
80         }
81 }
82
83 /* \brief called by scheduler to send STUN request */
84 static int stun_monitor_request(const void *blarg)
85 {
86         int res;
87         struct sockaddr_in answer;
88         static const struct sockaddr_in no_addr = { 0, };
89
90         ast_mutex_lock(&args.lock);
91         if (!args.monitor_enabled) {
92                 goto monitor_request_cleanup;
93         }
94
95         if (args.stun_sock < 0) {
96                 struct ast_sockaddr stun_addr;
97
98                 /* STUN socket not open.  Refresh the server DNS address resolution. */
99                 if (!args.server_hostname) {
100                         /* No STUN hostname? */
101                         goto monitor_request_cleanup;
102                 }
103
104                 /* Lookup STUN address. */
105                 memset(&stun_addr, 0, sizeof(stun_addr));
106                 stun_addr.ss.ss_family = AF_INET;
107                 if (ast_get_ip(&stun_addr, args.server_hostname)) {
108                         /* Lookup failed. */
109                         ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n",
110                                 args.server_hostname);
111                         goto monitor_request_cleanup;
112                 }
113                 ast_sockaddr_set_port(&stun_addr, args.stun_port);
114
115                 /* open socket binding */
116                 args.stun_sock = socket(AF_INET, SOCK_DGRAM, 0);
117                 if (args.stun_sock < 0) {
118                         ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
119                         goto monitor_request_cleanup;
120                 }
121                 if (ast_connect(args.stun_sock, &stun_addr)) {
122                         ast_log(LOG_WARNING, "STUN Failed to connect to %s: %s\n",
123                                 ast_sockaddr_stringify(&stun_addr), strerror(errno));
124                         stun_close_sock();
125                         goto monitor_request_cleanup;
126                 }
127         }
128
129         res = ast_stun_request(args.stun_sock, NULL, NULL, &answer);
130         if (res) {
131                 /*
132                  * STUN request timed out or errored.
133                  *
134                  * Refresh the server DNS address resolution next time around.
135                  */
136                 if (!args.stun_poll_failed_gripe) {
137                         args.stun_poll_failed_gripe = 1;
138                         ast_log(LOG_WARNING, "STUN poll %s. Re-evaluating STUN server address.\n",
139                                 res < 0 ? "failed" : "got no response");
140                 }
141                 stun_close_sock();
142         } else {
143                 args.stun_poll_failed_gripe = 0;
144                 if (memcmp(&no_addr, &answer, sizeof(no_addr))
145                         && memcmp(&args.external_addr, &answer, sizeof(args.external_addr))) {
146                         const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
147                         int newport = ntohs(answer.sin_port);
148
149                         ast_log(LOG_NOTICE, "Old external address/port %s:%d now seen as %s:%d.\n",
150                                 ast_inet_ntoa(args.external_addr.sin_addr),
151                                 ntohs(args.external_addr.sin_port), newaddr, newport);
152
153                         args.external_addr = answer;
154
155                         if (args.external_addr_known) {
156                                 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
157                                 RAII_VAR(struct ast_json_payload *, json_payload, NULL, ao2_cleanup);
158                                 RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
159
160                                 if (!ast_network_change_type()) {
161                                         goto publish_failure;
162                                 }
163
164                                 /* This json_object doesn't actually contain anything yet. We have to reference something
165                                  * for stasis, and this is useful for if we want to ever add data for any reason. */
166                                 json_object = ast_json_object_create();
167                                 if (!json_object) {
168                                         goto publish_failure;
169                                 }
170
171                                 if (!(json_payload = ast_json_payload_create(json_object))) {
172                                         goto publish_failure;
173                                 }
174
175                                 msg = stasis_message_create(ast_network_change_type(), json_payload);
176
177 publish_failure:
178                                 if (msg) {
179                                         stasis_publish(ast_system_topic(), msg);
180                                 } else {
181                                         ast_log(LOG_ERROR, "Failed to issue network change message.\n");
182                                 }
183                         } else {
184                                 /* this was the first external address we found, do not alert listeners
185                                  * until this address changes to something else. */
186                                 args.external_addr_known = 1;
187                         }
188                 }
189         }
190
191 monitor_request_cleanup:
192         /* always refresh this scheduler item.  It will be removed elsewhere when
193          * it is supposed to go away */
194         res = args.refresh * 1000;
195         ast_mutex_unlock(&args.lock);
196
197         return res;
198 }
199
200 /*!
201  * \internal
202  * \brief Stops the STUN monitor thread.
203  *
204  * \note do not hold the args->lock while calling this
205  *
206  * \return Nothing
207  */
208 static void stun_stop_monitor(void)
209 {
210         ast_mutex_lock(&args.lock);
211         args.monitor_enabled = 0;
212         ast_free((char *) args.server_hostname);
213         args.server_hostname = NULL;
214         stun_close_sock();
215         ast_mutex_unlock(&args.lock);
216
217         if (sched) {
218                 ast_sched_context_destroy(sched);
219                 sched = NULL;
220                 ast_log(LOG_NOTICE, "STUN monitor stopped\n");
221         }
222 }
223
224 /*!
225  * \internal
226  * \brief Starts the STUN monitor thread.
227  *
228  * \note The args->lock MUST be held when calling this function
229  *
230  * \return Nothing
231  */
232 static int stun_start_monitor(void)
233 {
234         /* if scheduler thread is not started, make sure to start it now */
235         if (sched) {
236                 return 0; /* already started */
237         }
238
239         if (!(sched = ast_sched_context_create())) {
240                 ast_log(LOG_ERROR, "Failed to create stun monitor scheduler context\n");
241                 return -1;
242         }
243
244         if (ast_sched_start_thread(sched)) {
245                 ast_sched_context_destroy(sched);
246                 sched = NULL;
247                 stun_close_sock();
248                 return -1;
249         }
250
251         if (ast_sched_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
252                 ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
253                 ast_sched_context_destroy(sched);
254                 sched = NULL;
255                 return -1;
256         }
257
258         ast_log(LOG_NOTICE, "STUN monitor started\n");
259
260         return 0;
261 }
262
263 /*!
264  * \internal
265  * \brief Parse and setup the stunaddr parameter.
266  *
267  * \param value Configuration parameter variable value.
268  *
269  * \retval 0 on success.
270  * \retval -1 on error.
271  */
272 static int setup_stunaddr(const char *value, int reload)
273 {
274         char *val;
275         char *host_str;
276         char *port_str;
277         unsigned int port;
278         struct ast_sockaddr stun_addr;
279
280         if (ast_strlen_zero(value)) {
281                 /* Setting to an empty value disables STUN monitoring. */
282                 args.monitor_enabled = 0;
283                 return 0;
284         }
285
286         val = ast_strdupa(value);
287         if (!ast_sockaddr_split_hostport(val, &host_str, &port_str, 0)
288                 || ast_strlen_zero(host_str)) {
289                 return -1;
290         }
291
292         /* Determine STUN port */
293         if (ast_strlen_zero(port_str)
294                 || 1 != sscanf(port_str, "%30u", &port)) {
295                 port = STANDARD_STUN_PORT;
296         }
297
298         host_str = ast_strdup(host_str);
299         if (!host_str) {
300                 return -1;
301         }
302
303         /* Lookup STUN address. */
304         memset(&stun_addr, 0, sizeof(stun_addr));
305         stun_addr.ss.ss_family = AF_INET;
306         if (ast_get_ip(&stun_addr, host_str)) {
307                 ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n", host_str);
308
309                 /* Only treat this as fatal if we are reloading */
310                 if (reload) {
311                         ast_free(host_str);
312                         return -1;
313                 }
314         }
315
316         /* Save STUN server information. */
317         ast_free((char *) args.server_hostname);
318         args.server_hostname = host_str;
319         args.stun_port = port;
320
321         /* Enable STUN monitor */
322         args.monitor_enabled = 1;
323         return 0;
324 }
325
326 static int load_config(int startup)
327 {
328         struct ast_flags config_flags = { 0, };
329         struct ast_config *cfg;
330         struct ast_variable *v;
331
332         if (!startup) {
333                 ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
334         }
335
336         cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags);
337         if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
338                 ast_log(LOG_WARNING, "Unable to load config %s\n", stun_conf_file);
339                 return -1;
340         }
341         if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
342                 return 0;
343         }
344
345         /* clean up any previous open socket */
346         stun_close_sock();
347         args.stun_poll_failed_gripe = 0;
348
349         /* set defaults */
350         args.monitor_enabled = 0;
351         args.refresh = DEFAULT_MONITOR_REFRESH;
352
353         for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
354                 if (!strcasecmp(v->name, "stunaddr")) {
355                         if (setup_stunaddr(v->value, !startup)) {
356                                 ast_log(LOG_WARNING, "Invalid STUN server address: %s at line %d\n",
357                                         v->value, v->lineno);
358                         }
359                 } else if (!strcasecmp(v->name, "stunrefresh")) {
360                         if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
361                                 ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
362                                 args.refresh = DEFAULT_MONITOR_REFRESH;
363                         }
364                 } else {
365                         ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
366                                 v->value, v->lineno);
367                 }
368         }
369
370         ast_config_destroy(cfg);
371
372         return 0;
373 }
374
375 /*! \brief Execute stun show status command */
376 static void _stun_show_status(int fd)
377 {
378         const char *status;
379
380 #define DATALN "%-25s %-5u %-7u %-8d %-7s %-16s %-d\n"
381 #define HEADER "%-25s %-5s %-7s %-8s %-7s %-16s %-s\n"
382
383         /*! we only have one stun server, but start to play well with more */
384         ast_cli(fd, HEADER, "Hostname", "Port", "Period", "Retries", "Status", "ExternAddr", "ExternPort");
385
386         if (args.stun_poll_failed_gripe) {
387                 status = "FAIL";
388         } else if (args.external_addr_known) {
389                 status = "OK";
390         } else {
391                 status = "INIT";
392         }
393         ast_cli( fd, DATALN,
394                      args.server_hostname,
395                      args.stun_port,
396                      args.refresh,
397                      DEFAULT_RETRIES,
398                      status,
399                      ast_inet_ntoa(args.external_addr.sin_addr),
400                      ntohs(args.external_addr.sin_port)
401                    );
402
403 #undef HEADER
404 #undef DATALN
405 }
406
407 static char *handle_cli_stun_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
408 {
409         switch (cmd) {
410         case CLI_INIT:
411                 e->command = "stun show status";
412                 e->usage =
413                     "Usage: stun show status\n"
414                     "       List all known STUN servers and statuses.\n";
415                 return NULL;
416         case CLI_GENERATE:
417                 return NULL;
418         }
419
420         if (a->argc != 3) {
421                 return CLI_SHOWUSAGE;
422         }
423
424         _stun_show_status(a->fd);
425         return CLI_SUCCESS;
426 }
427
428 static struct ast_cli_entry cli_stun[] = {
429         AST_CLI_DEFINE(handle_cli_stun_show_status, "Show STUN servers and statuses"),
430 };
431
432 static int __reload(int startup)
433 {
434         int res;
435
436         ast_mutex_lock(&args.lock);
437         if (!(res = load_config(startup)) && args.monitor_enabled) {
438                 res = stun_start_monitor();
439         }
440         ast_mutex_unlock(&args.lock);
441
442         if (res < 0 || !args.monitor_enabled) {
443                 stun_stop_monitor();
444         }
445
446         return res;
447 }
448
449 static int reload(void)
450 {
451         return __reload(0);
452 }
453
454 static int unload_module(void)
455 {
456         stun_stop_monitor();
457         ast_mutex_destroy(&args.lock);
458
459         /*! Unregister CLI commands */
460         ast_cli_unregister_multiple(cli_stun, ARRAY_LEN(cli_stun));
461
462         return 0;
463 }
464
465 static int load_module(void)
466 {
467         ast_mutex_init(&args.lock);
468         args.stun_sock = -1;
469         if (__reload(1)) {
470                 ast_mutex_destroy(&args.lock);
471                 return AST_MODULE_LOAD_DECLINE;
472         }
473
474         /*! Register CLI commands */
475         ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
476
477         return AST_MODULE_LOAD_SUCCESS;
478 }
479
480 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
481         .support_level = AST_MODULE_SUPPORT_CORE,
482         .load = load_module,
483         .unload = unload_module,
484         .reload = reload,
485         .load_pri = AST_MODPRI_CHANNEL_DEPEND
486 );