2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005-2006, Kevin P. Fleming
6 * Kevin P. Fleming <kpfleming@digium.com>
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.
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.
21 * \brief Background DNS update manager
23 * \author Kevin P. Fleming <kpfleming@digium.com>
25 * \bug There is a minor race condition. In the event that an IP address
26 * of a dnsmgr managed host changes, there is the potential for the consumer
27 * of that address to access the in_addr data at the same time that the dnsmgr
28 * thread is in the middle of updating it to the new address.
33 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
35 #include "asterisk/_private.h"
39 #include "asterisk/dnsmgr.h"
40 #include "asterisk/linkedlists.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/config.h"
43 #include "asterisk/sched.h"
44 #include "asterisk/cli.h"
45 #include "asterisk/manager.h"
46 #include "asterisk/acl.h"
48 static struct sched_context
*sched
;
49 static int refresh_sched
= -1;
50 static pthread_t refresh_thread
= AST_PTHREADT_NULL
;
52 struct ast_dnsmgr_entry
{
53 /*! where we will store the resulting IP address and port number */
54 struct sockaddr_in
*result
;
55 /*! the last result, used to check if address/port has changed */
56 struct sockaddr_in last
;
57 /*! SRV record to lookup, if provided. Composed of service, protocol, and domain name: _Service._Proto.Name */
59 /*! Set to 1 if the entry changes */
62 AST_RWLIST_ENTRY(ast_dnsmgr_entry
) list
;
63 /*! just 1 here, but we use calloc to allocate the correct size */
67 static AST_RWLIST_HEAD_STATIC(entry_list
, ast_dnsmgr_entry
);
69 AST_MUTEX_DEFINE_STATIC(refresh_lock
);
71 #define REFRESH_DEFAULT 300
74 static int refresh_interval
;
77 struct entry_list
*entries
;
79 unsigned int regex_present
:1;
83 static struct refresh_info master_refresh_info
= {
84 .entries
= &entry_list
,
88 struct ast_dnsmgr_entry
*ast_dnsmgr_get(const char *name
, struct sockaddr_in
*result
, const char *service
)
90 struct ast_dnsmgr_entry
*entry
;
91 int total_size
= sizeof(*entry
) + strlen(name
) + (service
? strlen(service
) + 1 : 0);
93 if (!result
|| ast_strlen_zero(name
) || !(entry
= ast_calloc(1, total_size
)))
96 entry
->result
= result
;
97 ast_mutex_init(&entry
->lock
);
98 strcpy(entry
->name
, name
);
99 memcpy(&entry
->last
, result
, sizeof(entry
->last
));
101 entry
->service
= ((char *) entry
) + sizeof(*entry
) + strlen(name
);
102 strcpy(entry
->service
, service
);
105 AST_RWLIST_WRLOCK(&entry_list
);
106 AST_RWLIST_INSERT_HEAD(&entry_list
, entry
, list
);
107 AST_RWLIST_UNLOCK(&entry_list
);
112 void ast_dnsmgr_release(struct ast_dnsmgr_entry
*entry
)
117 AST_RWLIST_WRLOCK(&entry_list
);
118 AST_RWLIST_REMOVE(&entry_list
, entry
, list
);
119 AST_RWLIST_UNLOCK(&entry_list
);
120 ast_verb(4, "removing dns manager for '%s'\n", entry
->name
);
122 ast_mutex_destroy(&entry
->lock
);
126 int ast_dnsmgr_lookup(const char *name
, struct sockaddr_in
*result
, struct ast_dnsmgr_entry
**dnsmgr
, const char *service
)
128 if (ast_strlen_zero(name
) || !result
|| !dnsmgr
)
131 if (*dnsmgr
&& !strcasecmp((*dnsmgr
)->name
, name
))
134 /* if it's actually an IP address and not a name,
135 there's no need for a managed lookup */
136 if (inet_aton(name
, &result
->sin_addr
))
139 ast_verb(4, "doing dnsmgr_lookup for '%s'\n", name
);
141 /* do a lookup now but add a manager so it will automagically get updated in the background */
142 ast_get_ip_or_srv(result
, name
, service
);
144 /* if dnsmgr is not enable don't bother adding an entry */
148 ast_verb(3, "adding dns manager for '%s'\n", name
);
149 *dnsmgr
= ast_dnsmgr_get(name
, result
, service
);
154 * Refresh a dnsmgr entry
156 static int dnsmgr_refresh(struct ast_dnsmgr_entry
*entry
, int verbose
)
158 char iabuf
[INET_ADDRSTRLEN
];
159 char iabuf2
[INET_ADDRSTRLEN
];
160 struct sockaddr_in tmp
;
163 ast_mutex_lock(&entry
->lock
);
165 ast_verb(3, "refreshing '%s'\n", entry
->name
);
167 ast_get_ip_or_srv(&tmp
, entry
->name
, entry
->service
);
168 if (inaddrcmp(&tmp
, &entry
->last
)) {
169 ast_copy_string(iabuf
, ast_inet_ntoa(entry
->last
.sin_addr
), sizeof(iabuf
));
170 ast_copy_string(iabuf2
, ast_inet_ntoa(tmp
.sin_addr
), sizeof(iabuf2
));
171 ast_log(LOG_NOTICE
, "dnssrv: host '%s' changed from %s:%d to %s:%d\n",
172 entry
->name
, iabuf
, ntohs(entry
->last
.sin_port
), iabuf2
, ntohs(tmp
.sin_port
));
173 *entry
->result
= tmp
;
175 changed
= entry
->changed
= 1;
178 ast_mutex_unlock(&entry
->lock
);
182 int ast_dnsmgr_refresh(struct ast_dnsmgr_entry
*entry
)
184 return dnsmgr_refresh(entry
, 0);
188 * Check if dnsmgr entry has changed from since last call to this function
190 int ast_dnsmgr_changed(struct ast_dnsmgr_entry
*entry
)
194 ast_mutex_lock(&entry
->lock
);
196 changed
= entry
->changed
;
199 ast_mutex_unlock(&entry
->lock
);
204 static void *do_refresh(void *data
)
207 pthread_testcancel();
208 usleep((ast_sched_wait(sched
)*1000));
209 pthread_testcancel();
210 ast_sched_runq(sched
);
215 static int refresh_list(const void *data
)
217 struct refresh_info
*info
= (struct refresh_info
*)data
;
218 struct ast_dnsmgr_entry
*entry
;
220 /* if a refresh or reload is already in progress, exit now */
221 if (ast_mutex_trylock(&refresh_lock
)) {
223 ast_log(LOG_WARNING
, "DNS Manager refresh already in progress.\n");
227 ast_verb(3, "Refreshing DNS lookups.\n");
228 AST_RWLIST_RDLOCK(info
->entries
);
229 AST_RWLIST_TRAVERSE(info
->entries
, entry
, list
) {
230 if (info
->regex_present
&& regexec(&info
->filter
, entry
->name
, 0, NULL
, 0))
233 dnsmgr_refresh(entry
, info
->verbose
);
235 AST_RWLIST_UNLOCK(info
->entries
);
237 ast_mutex_unlock(&refresh_lock
);
239 /* automatically reschedule based on the interval */
240 return refresh_interval
* 1000;
243 void dnsmgr_start_refresh(void)
245 if (refresh_sched
> -1) {
246 AST_SCHED_DEL(sched
, refresh_sched
);
247 refresh_sched
= ast_sched_add_variable(sched
, 100, refresh_list
, &master_refresh_info
, 1);
251 static int do_reload(int loading
);
253 static char *handle_cli_reload(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
257 e
->command
= "dnsmgr reload";
259 "Usage: dnsmgr reload\n"
260 " Reloads the DNS manager configuration.\n";
266 return CLI_SHOWUSAGE
;
272 static char *handle_cli_refresh(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
274 struct refresh_info info
= {
275 .entries
= &entry_list
,
280 e
->command
= "dnsmgr refresh";
282 "Usage: dnsmgr refresh [pattern]\n"
283 " Peforms an immediate refresh of the managed DNS entries.\n"
284 " Optional regular expression pattern is used to filter the entries to refresh.\n";
290 return CLI_SHOWUSAGE
;
293 if ( regcomp(&info
.filter
, a
->argv
[2], REG_EXTENDED
| REG_NOSUB
) )
294 return CLI_SHOWUSAGE
;
296 info
.regex_present
= 1;
301 if (info
.regex_present
)
302 regfree(&info
.filter
);
307 static char *handle_cli_status(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
310 struct ast_dnsmgr_entry
*entry
;
313 e
->command
= "dnsmgr status";
315 "Usage: dnsmgr status\n"
316 " Displays the DNS manager status.\n";
323 return CLI_SHOWUSAGE
;
325 ast_cli(a
->fd
, "DNS Manager: %s\n", enabled
? "enabled" : "disabled");
326 ast_cli(a
->fd
, "Refresh Interval: %d seconds\n", refresh_interval
);
327 AST_RWLIST_RDLOCK(&entry_list
);
328 AST_RWLIST_TRAVERSE(&entry_list
, entry
, list
)
330 AST_RWLIST_UNLOCK(&entry_list
);
331 ast_cli(a
->fd
, "Number of entries: %d\n", count
);
336 static struct ast_cli_entry cli_reload
= AST_CLI_DEFINE(handle_cli_reload
, "Reloads the DNS manager configuration");
337 static struct ast_cli_entry cli_refresh
= AST_CLI_DEFINE(handle_cli_refresh
, "Performs an immediate refresh");
338 static struct ast_cli_entry cli_status
= AST_CLI_DEFINE(handle_cli_status
, "Display the DNS manager status");
340 int dnsmgr_init(void)
342 if (!(sched
= sched_context_create())) {
343 ast_log(LOG_ERROR
, "Unable to create schedule context.\n");
346 ast_cli_register(&cli_reload
);
347 ast_cli_register(&cli_status
);
348 ast_cli_register(&cli_refresh
);
352 int dnsmgr_reload(void)
357 static int do_reload(int loading
)
359 struct ast_config
*config
;
360 struct ast_flags config_flags
= { loading
? 0 : CONFIG_FLAG_FILEUNCHANGED
};
361 const char *interval_value
;
362 const char *enabled_value
;
367 if ((config
= ast_config_load2("dnsmgr.conf", "dnsmgr", config_flags
)) == CONFIG_STATUS_FILEUNCHANGED
)
370 /* ensure that no refresh cycles run while the reload is in progress */
371 ast_mutex_lock(&refresh_lock
);
373 /* reset defaults in preparation for reading config file */
374 refresh_interval
= REFRESH_DEFAULT
;
375 was_enabled
= enabled
;
378 AST_SCHED_DEL(sched
, refresh_sched
);
381 if ((enabled_value
= ast_variable_retrieve(config
, "general", "enable"))) {
382 enabled
= ast_true(enabled_value
);
384 if ((interval_value
= ast_variable_retrieve(config
, "general", "refreshinterval"))) {
385 if (sscanf(interval_value
, "%d", &interval
) < 1)
386 ast_log(LOG_WARNING
, "Unable to convert '%s' to a numeric value.\n", interval_value
);
387 else if (interval
< 0)
388 ast_log(LOG_WARNING
, "Invalid refresh interval '%d' specified, using default\n", interval
);
390 refresh_interval
= interval
;
392 ast_config_destroy(config
);
395 if (enabled
&& refresh_interval
)
396 ast_log(LOG_NOTICE
, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval
);
398 /* if this reload enabled the manager, create the background thread
399 if it does not exist */
401 if (!was_enabled
&& (refresh_thread
== AST_PTHREADT_NULL
)) {
402 if (ast_pthread_create_background(&refresh_thread
, NULL
, do_refresh
, NULL
) < 0) {
403 ast_log(LOG_ERROR
, "Unable to start refresh thread.\n");
406 /* make a background refresh happen right away */
407 refresh_sched
= ast_sched_add_variable(sched
, 100, refresh_list
, &master_refresh_info
, 1);
410 /* if this reload disabled the manager and there is a background thread,
412 else if (!enabled
&& was_enabled
&& (refresh_thread
!= AST_PTHREADT_NULL
)) {
413 /* wake up the thread so it will exit */
414 pthread_cancel(refresh_thread
);
415 pthread_kill(refresh_thread
, SIGURG
);
416 pthread_join(refresh_thread
, NULL
);
417 refresh_thread
= AST_PTHREADT_NULL
;
418 ast_cli_unregister(&cli_refresh
);
424 ast_mutex_unlock(&refresh_lock
);
425 manager_event(EVENT_FLAG_SYSTEM
, "Reload", "Module: DNSmgr\r\nStatus: %s\r/nMessage: DNSmgr reload Requested\r\n", enabled
? "Enabled" : "Disabled");