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