d4b9d94d2d1d2d8bd9a0d704b95944320300327a
[asterisk/asterisk.git] / main / dnsmgr.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005-2006, Kevin P. Fleming
5  *
6  * Kevin P. Fleming <kpfleming@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 /*! \file
20  *
21  * \brief Background DNS update manager
22  *
23  * \author Kevin P. Fleming <kpfleming@digium.com>
24  *
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.
29  */
30
31 /*** MODULEINFO
32         <support_level>core</support_level>
33  ***/
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include "asterisk/_private.h"
40 #include <regex.h>
41 #include <signal.h>
42
43 #include "asterisk/dnsmgr.h"
44 #include "asterisk/linkedlists.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/config.h"
47 #include "asterisk/sched.h"
48 #include "asterisk/cli.h"
49 #include "asterisk/manager.h"
50 #include "asterisk/acl.h"
51
52 static struct ast_sched_context *sched;
53 static int refresh_sched = -1;
54 static pthread_t refresh_thread = AST_PTHREADT_NULL;
55
56 struct ast_dnsmgr_entry {
57         /*! where we will store the resulting IP address and port number */
58         struct ast_sockaddr *result;
59         /*! SRV record to lookup, if provided. Composed of service, protocol, and domain name: _Service._Proto.Name */
60         char *service;
61         /*! Address family to filter DNS responses. */
62         unsigned int family;
63         /*! Set to 1 if the entry changes */
64         unsigned int changed:1;
65         /*! Data to pass back to update_func */
66         void *data;
67         /*! The callback function to execute on address update */
68         dns_update_func update_func;
69         ast_mutex_t lock;
70         AST_RWLIST_ENTRY(ast_dnsmgr_entry) list;
71         /*! just 1 here, but we use calloc to allocate the correct size */
72         char name[1];
73 };
74
75 static AST_RWLIST_HEAD_STATIC(entry_list, ast_dnsmgr_entry);
76
77 AST_MUTEX_DEFINE_STATIC(refresh_lock);
78
79 #define REFRESH_DEFAULT 300
80
81 static int enabled;
82 static int refresh_interval;
83
84 struct refresh_info {
85         struct entry_list *entries;
86         int verbose;
87         unsigned int regex_present:1;
88         regex_t filter;
89 };
90
91 static struct refresh_info master_refresh_info = {
92         .entries = &entry_list,
93         .verbose = 0,
94 };
95
96 struct ast_dnsmgr_entry *ast_dnsmgr_get_family(const char *name, struct ast_sockaddr *result, const char *service, unsigned int family)
97 {
98         struct ast_dnsmgr_entry *entry;
99         int total_size = sizeof(*entry) + strlen(name) + (service ? strlen(service) + 1 : 0);
100
101         if (!result || ast_strlen_zero(name) || !(entry = ast_calloc(1, total_size))) {
102                 return NULL;
103         }
104
105         entry->result = result;
106         ast_mutex_init(&entry->lock);
107         strcpy(entry->name, name);
108         if (service) {
109                 entry->service = ((char *) entry) + sizeof(*entry) + strlen(name);
110                 strcpy(entry->service, service);
111         }
112         entry->family = family;
113
114         AST_RWLIST_WRLOCK(&entry_list);
115         AST_RWLIST_INSERT_HEAD(&entry_list, entry, list);
116         AST_RWLIST_UNLOCK(&entry_list);
117
118         return entry;
119 }
120
121 struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct ast_sockaddr *result, const char *service)
122 {
123         return ast_dnsmgr_get_family(name, result, service, 0);
124 }
125
126 void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
127 {
128         if (!entry) {
129                 return;
130         }
131
132         AST_RWLIST_WRLOCK(&entry_list);
133         AST_RWLIST_REMOVE(&entry_list, entry, list);
134         AST_RWLIST_UNLOCK(&entry_list);
135         ast_debug(6, "removing dns manager for '%s'\n", entry->name);
136
137         ast_mutex_destroy(&entry->lock);
138         ast_free(entry);
139 }
140
141 static int internal_dnsmgr_lookup(const char *name, struct ast_sockaddr *result, struct ast_dnsmgr_entry **dnsmgr, const char *service, dns_update_func func, void *data)
142 {
143         unsigned int family;
144
145         if (ast_strlen_zero(name) || !result || !dnsmgr) {
146                 return -1;
147         }
148
149         if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name)) {
150                 return 0;
151         }
152
153         /* Lookup address family filter. */
154         family = result->ss.ss_family;
155
156         /*
157          * If it's actually an IP address and not a name, there's no
158          * need for a managed lookup.
159          */
160         if (ast_sockaddr_parse(result, name, PARSE_PORT_FORBID)) {
161                 return 0;
162         }
163
164         ast_debug(6, "doing dnsmgr_lookup for '%s'\n", name);
165
166         /* do a lookup now but add a manager so it will automagically get updated in the background */
167         ast_get_ip_or_srv(result, name, service);
168
169         /* if dnsmgr is not enable don't bother adding an entry */
170         if (!enabled) {
171                 return 0;
172         }
173
174         ast_debug(6, "adding dns manager for '%s'\n", name);
175         *dnsmgr = ast_dnsmgr_get_family(name, result, service, family);
176         (*dnsmgr)->update_func = func;
177         (*dnsmgr)->data = data;
178         return !*dnsmgr;
179 }
180
181 int ast_dnsmgr_lookup(const char *name, struct ast_sockaddr *result, struct ast_dnsmgr_entry **dnsmgr, const char *service)
182 {
183         return internal_dnsmgr_lookup(name, result, dnsmgr, service, NULL, NULL);
184 }
185
186 int ast_dnsmgr_lookup_cb(const char *name, struct ast_sockaddr *result, struct ast_dnsmgr_entry **dnsmgr, const char *service, dns_update_func func, void *data)
187 {
188         return internal_dnsmgr_lookup(name, result, dnsmgr, service, func, data);
189 }
190
191 /*
192  * Refresh a dnsmgr entry
193  */
194 static int dnsmgr_refresh(struct ast_dnsmgr_entry *entry, int verbose)
195 {
196         struct ast_sockaddr tmp = { .len = 0, };
197         int changed = 0;
198
199         ast_mutex_lock(&entry->lock);
200
201         ast_debug(6, "refreshing '%s'\n", entry->name);
202
203         tmp.ss.ss_family = entry->family;
204         if (!ast_get_ip_or_srv(&tmp, entry->name, entry->service)) {
205                 if (!ast_sockaddr_port(&tmp)) {
206                         ast_sockaddr_set_port(&tmp, ast_sockaddr_port(entry->result));
207                 }
208                 if (ast_sockaddr_cmp(&tmp, entry->result)) {
209                         const char *old_addr = ast_strdupa(ast_sockaddr_stringify(entry->result));
210                         const char *new_addr = ast_strdupa(ast_sockaddr_stringify(&tmp));
211
212                         if (entry->update_func) {
213                                 entry->update_func(entry->result, &tmp, entry->data);
214                         } else {
215                                 ast_log(LOG_NOTICE, "dnssrv: host '%s' changed from %s to %s\n",
216                                                 entry->name, old_addr, new_addr);
217
218                                 ast_sockaddr_copy(entry->result, &tmp);
219                                 changed = entry->changed = 1;
220                         }
221                 }
222         }
223
224         ast_mutex_unlock(&entry->lock);
225
226         return changed;
227 }
228
229 int ast_dnsmgr_refresh(struct ast_dnsmgr_entry *entry)
230 {
231         return dnsmgr_refresh(entry, 0);
232 }
233
234 /*
235  * Check if dnsmgr entry has changed from since last call to this function
236  */
237 int ast_dnsmgr_changed(struct ast_dnsmgr_entry *entry)
238 {
239         int changed;
240
241         ast_mutex_lock(&entry->lock);
242
243         changed = entry->changed;
244         entry->changed = 0;
245
246         ast_mutex_unlock(&entry->lock);
247
248         return changed;
249 }
250
251 static void *do_refresh(void *data)
252 {
253         for (;;) {
254                 pthread_testcancel();
255                 usleep((ast_sched_wait(sched)*1000));
256                 pthread_testcancel();
257                 ast_sched_runq(sched);
258         }
259         return NULL;
260 }
261
262 static int refresh_list(const void *data)
263 {
264         struct refresh_info *info = (struct refresh_info *)data;
265         struct ast_dnsmgr_entry *entry;
266
267         /* if a refresh or reload is already in progress, exit now */
268         if (ast_mutex_trylock(&refresh_lock)) {
269                 if (info->verbose) {
270                         ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
271                 }
272                 return -1;
273         }
274
275         ast_debug(6, "Refreshing DNS lookups.\n");
276         AST_RWLIST_RDLOCK(info->entries);
277         AST_RWLIST_TRAVERSE(info->entries, entry, list) {
278                 if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0)) {
279                         continue;
280                 }
281
282                 dnsmgr_refresh(entry, info->verbose);
283         }
284         AST_RWLIST_UNLOCK(info->entries);
285
286         ast_mutex_unlock(&refresh_lock);
287
288         /* automatically reschedule based on the interval */
289         return refresh_interval * 1000;
290 }
291
292 void dnsmgr_start_refresh(void)
293 {
294         if (refresh_sched > -1) {
295                 AST_SCHED_DEL(sched, refresh_sched);
296                 refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
297         }
298 }
299
300 static int do_reload(int loading);
301
302 static char *handle_cli_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
303 {
304         switch (cmd) {
305         case CLI_INIT:
306                 e->command = "dnsmgr reload";
307                 e->usage =
308                         "Usage: dnsmgr reload\n"
309                         "       Reloads the DNS manager configuration.\n";
310                 return NULL;
311         case CLI_GENERATE:
312                 return NULL;
313         }
314         if (a->argc > 2) {
315                 return CLI_SHOWUSAGE;
316         }
317
318         do_reload(0);
319         return CLI_SUCCESS;
320 }
321
322 static char *handle_cli_refresh(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
323 {
324         struct refresh_info info = {
325                 .entries = &entry_list,
326                 .verbose = 1,
327         };
328         switch (cmd) {
329         case CLI_INIT:
330                 e->command = "dnsmgr refresh";
331                 e->usage =
332                         "Usage: dnsmgr refresh [pattern]\n"
333                         "       Peforms an immediate refresh of the managed DNS entries.\n"
334                         "       Optional regular expression pattern is used to filter the entries to refresh.\n";
335                 return NULL;
336         case CLI_GENERATE:
337                 return NULL;
338         }
339
340         if (!enabled) {
341                 ast_cli(a->fd, "DNS Manager is disabled.\n");
342                 return 0;
343         }
344
345         if (a->argc > 3) {
346                 return CLI_SHOWUSAGE;
347         }
348
349         if (a->argc == 3) {
350                 if (regcomp(&info.filter, a->argv[2], REG_EXTENDED | REG_NOSUB)) {
351                         return CLI_SHOWUSAGE;
352                 } else {
353                         info.regex_present = 1;
354                 }
355         }
356
357         refresh_list(&info);
358
359         if (info.regex_present) {
360                 regfree(&info.filter);
361         }
362
363         return CLI_SUCCESS;
364 }
365
366 static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
367 {
368         int count = 0;
369         struct ast_dnsmgr_entry *entry;
370         switch (cmd) {
371         case CLI_INIT:
372                 e->command = "dnsmgr status";
373                 e->usage =
374                         "Usage: dnsmgr status\n"
375                         "       Displays the DNS manager status.\n";
376                 return NULL;
377         case CLI_GENERATE:
378                 return NULL;
379         }
380
381         if (a->argc > 2) {
382                 return CLI_SHOWUSAGE;
383         }
384
385         ast_cli(a->fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
386         ast_cli(a->fd, "Refresh Interval: %d seconds\n", refresh_interval);
387         AST_RWLIST_RDLOCK(&entry_list);
388         AST_RWLIST_TRAVERSE(&entry_list, entry, list)
389                 count++;
390         AST_RWLIST_UNLOCK(&entry_list);
391         ast_cli(a->fd, "Number of entries: %d\n", count);
392
393         return CLI_SUCCESS;
394 }
395
396 static struct ast_cli_entry cli_reload = AST_CLI_DEFINE(handle_cli_reload, "Reloads the DNS manager configuration");
397 static struct ast_cli_entry cli_refresh = AST_CLI_DEFINE(handle_cli_refresh, "Performs an immediate refresh");
398 static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the DNS manager status");
399
400 int dnsmgr_init(void)
401 {
402         if (!(sched = ast_sched_context_create())) {
403                 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
404                 return -1;
405         }
406         ast_cli_register(&cli_reload);
407         ast_cli_register(&cli_status);
408         ast_cli_register(&cli_refresh);
409         return do_reload(1);
410 }
411
412 int dnsmgr_reload(void)
413 {
414         return do_reload(0);
415 }
416
417 static int do_reload(int loading)
418 {
419         struct ast_config *config;
420         struct ast_variable *v;
421         struct ast_flags config_flags = { loading ? 0 : CONFIG_FLAG_FILEUNCHANGED };
422         int interval;
423         int was_enabled;
424
425         if ((config = ast_config_load2("dnsmgr.conf", "dnsmgr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
426                 return 0;
427         }
428
429         /* ensure that no refresh cycles run while the reload is in progress */
430         ast_mutex_lock(&refresh_lock);
431
432         /* reset defaults in preparation for reading config file */
433         refresh_interval = REFRESH_DEFAULT;
434         was_enabled = enabled;
435         enabled = 0;
436
437         if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
438                 ast_mutex_unlock(&refresh_lock);
439                 return 0;
440         }
441
442         AST_SCHED_DEL(sched, refresh_sched);
443
444         for (v = ast_variable_browse(config, "general"); v; v = v->next) {
445                 if (!strcasecmp(v->name, "enable")) {
446                         enabled = ast_true(v->value);
447                 } else if (!strcasecmp(v->name, "refreshinterval")) {
448                         if (sscanf(v->value, "%30d", &interval) < 1) {
449                                 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
450                         } else if (interval < 0) {
451                                 ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
452                         } else {
453                                 refresh_interval = interval;
454                         }
455                 }
456         }
457         ast_config_destroy(config);
458
459         if (enabled && refresh_interval) {
460                 ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
461         }
462
463         /* if this reload enabled the manager, create the background thread
464            if it does not exist */
465         if (enabled) {
466                 if (!was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
467                         if (ast_pthread_create_background(&refresh_thread, NULL, do_refresh, NULL) < 0) {
468                                 ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
469                         }
470                 }
471                 /* make a background refresh happen right away */
472                 refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
473         /* if this reload disabled the manager and there is a background thread, kill it */
474         } else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
475                 /* wake up the thread so it will exit */
476                 pthread_cancel(refresh_thread);
477                 pthread_kill(refresh_thread, SIGURG);
478                 pthread_join(refresh_thread, NULL);
479                 refresh_thread = AST_PTHREADT_NULL;
480         }
481
482         ast_mutex_unlock(&refresh_lock);
483         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: DNSmgr\r\nStatus: %s\r/nMessage: DNSmgr reload Requested\r\n", enabled ? "Enabled" : "Disabled");
484
485         return 0;
486 }