![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
dnsmgr.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 2005-2006, Kevin P. Fleming 00005 * 00006 * Kevin P. Fleming <kpfleming@digium.com> 00007 * 00008 * See http://www.asterisk.org for more information about 00009 * the Asterisk project. Please do not directly contact 00010 * any of the maintainers of this project for assistance; 00011 * the project provides a web site, mailing lists and IRC 00012 * channels for your use. 00013 * 00014 * This program is free software, distributed under the terms of 00015 * the GNU General Public License Version 2. See the LICENSE file 00016 * at the top of the source tree. 00017 */ 00018 00019 /*! \file 00020 * 00021 * \brief Background DNS update manager 00022 * 00023 * \author Kevin P. Fleming <kpfleming@digium.com> 00024 */ 00025 00026 #include "asterisk.h" 00027 00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 49116 $") 00029 00030 #include <sys/types.h> 00031 #include <netinet/in.h> 00032 #include <sys/socket.h> 00033 #include <arpa/inet.h> 00034 #include <resolv.h> 00035 #include <stdio.h> 00036 #include <string.h> 00037 #include <unistd.h> 00038 #include <stdlib.h> 00039 #include <regex.h> 00040 #include <signal.h> 00041 00042 #include "asterisk/dnsmgr.h" 00043 #include "asterisk/linkedlists.h" 00044 #include "asterisk/utils.h" 00045 #include "asterisk/config.h" 00046 #include "asterisk/logger.h" 00047 #include "asterisk/sched.h" 00048 #include "asterisk/options.h" 00049 #include "asterisk/cli.h" 00050 00051 static struct sched_context *sched; 00052 static int refresh_sched = -1; 00053 static pthread_t refresh_thread = AST_PTHREADT_NULL; 00054 00055 struct ast_dnsmgr_entry { 00056 /*! where we will store the resulting address */ 00057 struct in_addr *result; 00058 /*! the last result, used to check if address has changed */ 00059 struct in_addr last; 00060 /*! Set to 1 if the entry changes */ 00061 int changed:1; 00062 ast_mutex_t lock; 00063 AST_LIST_ENTRY(ast_dnsmgr_entry) list; 00064 /*! just 1 here, but we use calloc to allocate the correct size */ 00065 char name[1]; 00066 }; 00067 00068 static AST_LIST_HEAD_STATIC(entry_list, ast_dnsmgr_entry); 00069 00070 AST_MUTEX_DEFINE_STATIC(refresh_lock); 00071 00072 #define REFRESH_DEFAULT 300 00073 00074 static int enabled; 00075 static int refresh_interval; 00076 00077 struct refresh_info { 00078 struct entry_list *entries; 00079 int verbose; 00080 unsigned int regex_present:1; 00081 regex_t filter; 00082 }; 00083 00084 static struct refresh_info master_refresh_info = { 00085 .entries = &entry_list, 00086 .verbose = 0, 00087 }; 00088 00089 struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr *result) 00090 { 00091 struct ast_dnsmgr_entry *entry; 00092 00093 if (!result || ast_strlen_zero(name) || !(entry = ast_calloc(1, sizeof(*entry) + strlen(name)))) 00094 return NULL; 00095 00096 entry->result = result; 00097 ast_mutex_init(&entry->lock); 00098 strcpy(entry->name, name); 00099 00100 AST_LIST_LOCK(&entry_list); 00101 AST_LIST_INSERT_HEAD(&entry_list, entry, list); 00102 AST_LIST_UNLOCK(&entry_list); 00103 00104 return entry; 00105 } 00106 00107 void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry) 00108 { 00109 if (!entry) 00110 return; 00111 00112 AST_LIST_LOCK(&entry_list); 00113 AST_LIST_REMOVE(&entry_list, entry, list); 00114 AST_LIST_UNLOCK(&entry_list); 00115 if (option_verbose > 3) 00116 ast_verbose(VERBOSE_PREFIX_4 "removing dns manager for '%s'\n", entry->name); 00117 00118 ast_mutex_destroy(&entry->lock); 00119 free(entry); 00120 } 00121 00122 int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr) 00123 { 00124 if (ast_strlen_zero(name) || !result || !dnsmgr) 00125 return -1; 00126 00127 if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name)) 00128 return 0; 00129 00130 if (option_verbose > 3) 00131 ast_verbose(VERBOSE_PREFIX_4 "doing dnsmgr_lookup for '%s'\n", name); 00132 00133 /* if it's actually an IP address and not a name, 00134 there's no need for a managed lookup */ 00135 if (inet_aton(name, result)) 00136 return 0; 00137 00138 /* if the manager is disabled, do a direct lookup and return the result, 00139 otherwise register a managed lookup for the name */ 00140 if (!enabled) { 00141 struct ast_hostent ahp; 00142 struct hostent *hp; 00143 00144 if ((hp = ast_gethostbyname(name, &ahp))) 00145 memcpy(result, hp->h_addr, sizeof(result)); 00146 return 0; 00147 } else { 00148 if (option_verbose > 2) 00149 ast_verbose(VERBOSE_PREFIX_2 "adding dns manager for '%s'\n", name); 00150 *dnsmgr = ast_dnsmgr_get(name, result); 00151 return !*dnsmgr; 00152 } 00153 } 00154 00155 /* 00156 * Refresh a dnsmgr entry 00157 */ 00158 static int dnsmgr_refresh(struct ast_dnsmgr_entry *entry, int verbose) 00159 { 00160 struct ast_hostent ahp; 00161 struct hostent *hp; 00162 char iabuf[INET_ADDRSTRLEN]; 00163 char iabuf2[INET_ADDRSTRLEN]; 00164 struct in_addr tmp; 00165 int changed = 0; 00166 00167 ast_mutex_lock(&entry->lock); 00168 if (verbose && (option_verbose > 2)) 00169 ast_verbose(VERBOSE_PREFIX_2 "refreshing '%s'\n", entry->name); 00170 00171 if ((hp = ast_gethostbyname(entry->name, &ahp))) { 00172 /* check to see if it has changed, do callback if requested (where de callback is defined ????) */ 00173 memcpy(&tmp, hp->h_addr, sizeof(tmp)); 00174 if (tmp.s_addr != entry->last.s_addr) { 00175 ast_copy_string(iabuf, ast_inet_ntoa(entry->last), sizeof(iabuf)); 00176 ast_copy_string(iabuf2, ast_inet_ntoa(tmp), sizeof(iabuf2)); 00177 ast_log(LOG_NOTICE, "host '%s' changed from %s to %s\n", 00178 entry->name, iabuf, iabuf2); 00179 memcpy(entry->result, hp->h_addr, sizeof(entry->result)); 00180 memcpy(&entry->last, hp->h_addr, sizeof(entry->last)); 00181 changed = entry->changed = 1; 00182 } 00183 00184 } 00185 ast_mutex_unlock(&entry->lock); 00186 return changed; 00187 } 00188 00189 int ast_dnsmgr_refresh(struct ast_dnsmgr_entry *entry) 00190 { 00191 return dnsmgr_refresh(entry, 0); 00192 } 00193 00194 /* 00195 * Check if dnsmgr entry has changed from since last call to this function 00196 */ 00197 int ast_dnsmgr_changed(struct ast_dnsmgr_entry *entry) 00198 { 00199 int changed; 00200 00201 ast_mutex_lock(&entry->lock); 00202 00203 changed = entry->changed; 00204 entry->changed = 0; 00205 00206 ast_mutex_unlock(&entry->lock); 00207 00208 return changed; 00209 } 00210 00211 static void *do_refresh(void *data) 00212 { 00213 for (;;) { 00214 pthread_testcancel(); 00215 usleep((ast_sched_wait(sched)*1000)); 00216 pthread_testcancel(); 00217 ast_sched_runq(sched); 00218 } 00219 return NULL; 00220 } 00221 00222 static int refresh_list(void *data) 00223 { 00224 struct refresh_info *info = data; 00225 struct ast_dnsmgr_entry *entry; 00226 00227 /* if a refresh or reload is already in progress, exit now */ 00228 if (ast_mutex_trylock(&refresh_lock)) { 00229 if (info->verbose) 00230 ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n"); 00231 return -1; 00232 } 00233 00234 if (option_verbose > 2) 00235 ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n"); 00236 AST_LIST_LOCK(info->entries); 00237 AST_LIST_TRAVERSE(info->entries, entry, list) { 00238 if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0)) 00239 continue; 00240 00241 dnsmgr_refresh(entry, info->verbose); 00242 } 00243 AST_LIST_UNLOCK(info->entries); 00244 00245 ast_mutex_unlock(&refresh_lock); 00246 00247 /* automatically reschedule based on the interval */ 00248 return refresh_interval * 1000; 00249 } 00250 00251 void dnsmgr_start_refresh(void) 00252 { 00253 if (refresh_sched > -1) { 00254 ast_sched_del(sched, refresh_sched); 00255 refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1); 00256 } 00257 } 00258 00259 static int do_reload(int loading); 00260 00261 static int handle_cli_reload(int fd, int argc, char *argv[]) 00262 { 00263 if (argc > 2) 00264 return RESULT_SHOWUSAGE; 00265 00266 do_reload(0); 00267 return 0; 00268 } 00269 00270 static int handle_cli_refresh(int fd, int argc, char *argv[]) 00271 { 00272 struct refresh_info info = { 00273 .entries = &entry_list, 00274 .verbose = 1, 00275 }; 00276 00277 if (argc > 3) 00278 return RESULT_SHOWUSAGE; 00279 00280 if (argc == 3) { 00281 if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB)) 00282 return RESULT_SHOWUSAGE; 00283 else 00284 info.regex_present = 1; 00285 } 00286 00287 refresh_list(&info); 00288 00289 if (info.regex_present) 00290 regfree(&info.filter); 00291 00292 return 0; 00293 } 00294 00295 static int handle_cli_status(int fd, int argc, char *argv[]) 00296 { 00297 int count = 0; 00298 struct ast_dnsmgr_entry *entry; 00299 00300 if (argc > 2) 00301 return RESULT_SHOWUSAGE; 00302 00303 ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled"); 00304 ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval); 00305 AST_LIST_LOCK(&entry_list); 00306 AST_LIST_TRAVERSE(&entry_list, entry, list) 00307 count++; 00308 AST_LIST_UNLOCK(&entry_list); 00309 ast_cli(fd, "Number of entries: %d\n", count); 00310 00311 return 0; 00312 } 00313 00314 static struct ast_cli_entry cli_reload = { 00315 { "dnsmgr", "reload", NULL }, 00316 handle_cli_reload, "Reloads the DNS manager configuration", 00317 "Usage: dnsmgr reload\n" 00318 " Reloads the DNS manager configuration.\n" 00319 }; 00320 00321 static struct ast_cli_entry cli_refresh = { 00322 { "dnsmgr", "refresh", NULL }, 00323 handle_cli_refresh, "Performs an immediate refresh", 00324 "Usage: dnsmgr refresh [pattern]\n" 00325 " Peforms an immediate refresh of the managed DNS entries.\n" 00326 " Optional regular expression pattern is used to filter the entries to refresh.\n", 00327 }; 00328 00329 static struct ast_cli_entry cli_status = { 00330 { "dnsmgr", "status", NULL }, 00331 handle_cli_status, "Display the DNS manager status", 00332 "Usage: dnsmgr status\n" 00333 " Displays the DNS manager status.\n" 00334 }; 00335 00336 int dnsmgr_init(void) 00337 { 00338 if (!(sched = sched_context_create())) { 00339 ast_log(LOG_ERROR, "Unable to create schedule context.\n"); 00340 return -1; 00341 } 00342 ast_cli_register(&cli_reload); 00343 ast_cli_register(&cli_status); 00344 return do_reload(1); 00345 } 00346 00347 int dnsmgr_reload(void) 00348 { 00349 return do_reload(0); 00350 } 00351 00352 static int do_reload(int loading) 00353 { 00354 struct ast_config *config; 00355 const char *interval_value; 00356 const char *enabled_value; 00357 int interval; 00358 int was_enabled; 00359 int res = -1; 00360 00361 /* ensure that no refresh cycles run while the reload is in progress */ 00362 ast_mutex_lock(&refresh_lock); 00363 00364 /* reset defaults in preparation for reading config file */ 00365 refresh_interval = REFRESH_DEFAULT; 00366 was_enabled = enabled; 00367 enabled = 0; 00368 00369 if (refresh_sched > -1) 00370 ast_sched_del(sched, refresh_sched); 00371 00372 if ((config = ast_config_load("dnsmgr.conf"))) { 00373 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) { 00374 enabled = ast_true(enabled_value); 00375 } 00376 if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) { 00377 if (sscanf(interval_value, "%d", &interval) < 1) 00378 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value); 00379 else if (interval < 0) 00380 ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval); 00381 else 00382 refresh_interval = interval; 00383 } 00384 ast_config_destroy(config); 00385 } 00386 00387 if (enabled && refresh_interval) 00388 ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval); 00389 00390 /* if this reload enabled the manager, create the background thread 00391 if it does not exist */ 00392 if (enabled) { 00393 if (!was_enabled && (refresh_thread == AST_PTHREADT_NULL)) { 00394 if (ast_pthread_create_background(&refresh_thread, NULL, do_refresh, NULL) < 0) { 00395 ast_log(LOG_ERROR, "Unable to start refresh thread.\n"); 00396 } 00397 ast_cli_register(&cli_refresh); 00398 } 00399 /* make a background refresh happen right away */ 00400 refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1); 00401 res = 0; 00402 } 00403 /* if this reload disabled the manager and there is a background thread, 00404 kill it */ 00405 else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) { 00406 /* wake up the thread so it will exit */ 00407 pthread_cancel(refresh_thread); 00408 pthread_kill(refresh_thread, SIGURG); 00409 pthread_join(refresh_thread, NULL); 00410 refresh_thread = AST_PTHREADT_NULL; 00411 ast_cli_unregister(&cli_refresh); 00412 res = 0; 00413 } 00414 else 00415 res = 0; 00416 00417 ast_mutex_unlock(&refresh_lock); 00418 00419 return res; 00420 }