Wed Jan 8 2020 09:49:50

Asterisk developer's documentation


res_stun_monitor.c
Go to the documentation of this file.
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: 346700 $")
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_thread *sched;
48 
49 static struct {
50  /*! STUN monitor protection 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  */
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  sched = ast_sched_thread_destroy(sched);
200  ast_log(LOG_NOTICE, "STUN monitor stopped\n");
201  }
202 }
203 
204 /*!
205  * \internal
206  * \brief Starts the STUN monitor thread.
207  *
208  * \note The args->lock MUST be held when calling this function
209  *
210  * \return Nothing
211  */
212 static int stun_start_monitor(void)
213 {
214  /* if scheduler thread is not started, make sure to start it now */
215  if (sched) {
216  return 0; /* already started */
217  }
218 
219  if (!(sched = ast_sched_thread_create())) {
220  ast_log(LOG_ERROR, "Failed to create stun monitor scheduler thread\n");
221  return -1;
222  }
223 
224  if (ast_sched_thread_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
225  ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
226  sched = ast_sched_thread_destroy(sched);
227  return -1;
228  }
229 
230  ast_log(LOG_NOTICE, "STUN monitor started\n");
231  return 0;
232 }
233 
234 /*!
235  * \internal
236  * \brief Parse and setup the stunaddr parameter.
237  *
238  * \param value Configuration parameter variable value.
239  *
240  * \retval 0 on success.
241  * \retval -1 on error.
242  */
243 static int setup_stunaddr(const char *value)
244 {
245  char *val;
246  char *host_str;
247  char *port_str;
248  unsigned int port;
249  struct ast_sockaddr stun_addr;
250 
251  if (ast_strlen_zero(value)) {
252  /* Setting to an empty value disables STUN monitoring. */
253  args.monitor_enabled = 0;
254  return 0;
255  }
256 
257  val = ast_strdupa(value);
258  if (!ast_sockaddr_split_hostport(val, &host_str, &port_str, 0)
259  || ast_strlen_zero(host_str)) {
260  return -1;
261  }
262 
263  /* Determine STUN port */
264  if (ast_strlen_zero(port_str)
265  || 1 != sscanf(port_str, "%30u", &port)) {
266  port = STANDARD_STUN_PORT;
267  }
268 
269  host_str = ast_strdup(host_str);
270  if (!host_str) {
271  return -1;
272  }
273 
274  /* Lookup STUN address. */
275  memset(&stun_addr, 0, sizeof(stun_addr));
276  stun_addr.ss.ss_family = AF_INET;
277  if (ast_get_ip(&stun_addr, host_str)) {
278  ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n", host_str);
279  ast_free(host_str);
280  return -1;
281  }
282 
283  /* Save STUN server information. */
284  ast_free((char *) args.server_hostname);
285  args.server_hostname = host_str;
286  args.stun_port = port;
287 
288  /* Enable STUN monitor */
289  args.monitor_enabled = 1;
290  return 0;
291 }
292 
293 static int load_config(int startup)
294 {
295  struct ast_flags config_flags = { 0, };
296  struct ast_config *cfg;
297  struct ast_variable *v;
298 
299  if (!startup) {
300  ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
301  }
302 
303  cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags);
304  if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
305  ast_log(LOG_WARNING, "Unable to load config %s\n", stun_conf_file);
306  return -1;
307  }
308  if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
309  return 0;
310  }
311 
312  /* clean up any previous open socket */
313  stun_close_sock();
314  args.stun_poll_failed_gripe = 0;
315 
316  /* set defaults */
317  args.monitor_enabled = 0;
318  args.refresh = DEFAULT_MONITOR_REFRESH;
319 
320  for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
321  if (!strcasecmp(v->name, "stunaddr")) {
322  if (setup_stunaddr(v->value)) {
323  ast_log(LOG_WARNING, "Invalid STUN server address: %s at line %d\n",
324  v->value, v->lineno);
325  }
326  } else if (!strcasecmp(v->name, "stunrefresh")) {
327  if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
328  ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
329  args.refresh = DEFAULT_MONITOR_REFRESH;
330  }
331  } else {
332  ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
333  v->value, v->lineno);
334  }
335  }
336 
337  ast_config_destroy(cfg);
338 
339  return 0;
340 }
341 
342 static int __reload(int startup)
343 {
344  int res;
345 
346  ast_mutex_lock(&args.lock);
347  if (!(res = load_config(startup)) && args.monitor_enabled) {
348  res = stun_start_monitor();
349  }
350  ast_mutex_unlock(&args.lock);
351 
352  if (res < 0 || !args.monitor_enabled) {
354  }
355 
356  return res;
357 }
358 
359 static int reload(void)
360 {
361  return __reload(0);
362 }
363 
364 static int unload_module(void)
365 {
367  ast_mutex_destroy(&args.lock);
368  return 0;
369 }
370 
371 static int load_module(void)
372 {
373  ast_mutex_init(&args.lock);
374  args.stun_sock = -1;
375  if (__reload(1)) {
376  ast_mutex_destroy(&args.lock);
378  }
379 
381 }
382 
384  .load = load_module,
385  .unload = unload_module,
386  .reload = reload,
387  .load_pri = AST_MODPRI_CHANNEL_DEPEND
388  );
struct sockaddr_storage ss
Definition: netsock2.h:64
ast_mutex_t lock
Definition: sched.c:84
An event.
Definition: event.c:85
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
struct ast_sched_thread * ast_sched_thread_destroy(struct ast_sched_thread *st)
Destroy a scheduler and its thread.
Definition: sched.c:143
#define DEFAULT_MONITOR_REFRESH
static int stun_monitor_request(const void *blarg)
struct ast_sched_thread * ast_sched_thread_create(void)
Create a scheduler with a dedicated thread.
Definition: sched.c:167
int ast_sched_thread_add_variable(struct ast_sched_thread *st, int when, ast_sched_cb cb, const void *data, int variable)
Add a variable reschedule time scheduler entry.
Definition: sched.c:195
#define ast_strdup(a)
Definition: astmm.h:109
Definition: ast_expr2.c:325
static int __reload(int startup)
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define LOG_WARNING
Definition: logger.h:144
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
int lineno
Definition: config.h:87
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
Configuration File Parser.
static int reload(void)
#define ast_mutex_lock(a)
Definition: lock.h:155
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
int value
Definition: syslog.c:39
Socket address structure.
Definition: netsock2.h:63
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: config.c:2499
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
struct sockaddr_in external_addr
int ast_event_queue(struct ast_event *event)
Queue an event.
Definition: event.c:1517
const char * value
Definition: config.h:79
int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
Splits a string into its host and port components.
Definition: netsock2.c:132
static const int STANDARD_STUN_PORT
Definition: stun.h:35
int ast_stun_request(int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer)
Generic STUN request.
Definition: stun.c:373
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
Access Control of various sorts.
Scheduler Routines (derived from cheops)
static int load_config(int startup)
unsigned int stun_port
const char * name
Definition: config.h:77
static int stun_start_monitor(void)
STUN support.
Network socket handling.
static struct sched_context * sched
Definition: chan_gtalk.c:227
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
static int setup_stunaddr(const char *value)
#define LOG_ERROR
Definition: logger.h:155
static struct @350 args
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:422
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:210
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
static int load_module(void)
int ast_get_ip(struct ast_sockaddr *addr, const char *hostname)
Get the IP address given a hostname.
Definition: acl.c:700
#define LOG_NOTICE
Definition: logger.h:133
int errno
const char * ast_inet_ntoa(struct in_addr ia)
thread-safe replacement for inet_ntoa().
Definition: utils.c:564
#define ast_free(a)
Definition: astmm.h:97
static const char stun_conf_file[]
static void stun_close_sock(void)
int stun_sock
Structure used to handle boolean flags.
Definition: utils.h:200
unsigned int monitor_enabled
const char * server_hostname
void ast_event_destroy(struct ast_event *event)
Destroy an event.
Definition: event.c:1314
struct ast_event * ast_event_new(enum ast_event_type event_type,...)
Create a new event.
Definition: event.c:1202
unsigned int stun_poll_failed_gripe
struct ast_variable * next
Definition: config.h:82
#define ast_mutex_init(pmutex)
Definition: lock.h:152
static int unload_module(void)
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
#define ast_mutex_destroy(a)
Definition: lock.h:154
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
int ast_connect(int sockfd, const struct ast_sockaddr *addr)
Wrapper around connect(2) that uses struct ast_sockaddr.
Definition: netsock2.c:467
Structure for mutex and tracking information.
Definition: lock.h:121
unsigned int external_addr_known
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
#define ast_mutex_unlock(a)
Definition: lock.h:156
unsigned int refresh
static void stun_stop_monitor(void)