dnsmgr.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. /*! \file
  19. *
  20. * \brief Background DNS update manager
  21. *
  22. */
  23. #include <sys/types.h>
  24. #include <netinet/in.h>
  25. #include <sys/socket.h>
  26. #include <arpa/inet.h>
  27. #include <resolv.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <unistd.h>
  31. #include <stdlib.h>
  32. #include <regex.h>
  33. #include <signal.h>
  34. #include "asterisk.h"
  35. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  36. #include "asterisk/dnsmgr.h"
  37. #include "asterisk/linkedlists.h"
  38. #include "asterisk/utils.h"
  39. #include "asterisk/config.h"
  40. #include "asterisk/logger.h"
  41. #include "asterisk/sched.h"
  42. #include "asterisk/options.h"
  43. #include "asterisk/cli.h"
  44. static struct sched_context *sched;
  45. static int refresh_sched = -1;
  46. static pthread_t refresh_thread = AST_PTHREADT_NULL;
  47. struct ast_dnsmgr_entry {
  48. struct in_addr *result;
  49. AST_LIST_ENTRY(ast_dnsmgr_entry) list;
  50. char name[1];
  51. };
  52. static AST_LIST_HEAD_STATIC(entry_list, ast_dnsmgr_entry);
  53. AST_MUTEX_DEFINE_STATIC(refresh_lock);
  54. #define REFRESH_DEFAULT 300
  55. static int enabled = 0;
  56. static int refresh_interval;
  57. struct refresh_info {
  58. struct entry_list *entries;
  59. int verbose;
  60. unsigned int regex_present:1;
  61. regex_t filter;
  62. };
  63. static struct refresh_info master_refresh_info = {
  64. .entries = &entry_list,
  65. .verbose = 0,
  66. };
  67. struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr *result)
  68. {
  69. struct ast_dnsmgr_entry *entry;
  70. if (!result || ast_strlen_zero(name))
  71. return NULL;
  72. entry = calloc(1, sizeof(*entry) + strlen(name));
  73. if (!entry)
  74. return NULL;
  75. entry->result = result;
  76. strcpy(entry->name, name);
  77. AST_LIST_LOCK(&entry_list);
  78. AST_LIST_INSERT_HEAD(&entry_list, entry, list);
  79. AST_LIST_UNLOCK(&entry_list);
  80. return entry;
  81. }
  82. void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
  83. {
  84. if (!entry)
  85. return;
  86. AST_LIST_LOCK(&entry_list);
  87. AST_LIST_REMOVE(&entry_list, entry, list);
  88. AST_LIST_UNLOCK(&entry_list);
  89. free(entry);
  90. }
  91. int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr)
  92. {
  93. struct ast_hostent ahp;
  94. struct hostent *hp;
  95. if (ast_strlen_zero(name) || !result || !dnsmgr)
  96. return -1;
  97. if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
  98. return 0;
  99. if (option_verbose > 3)
  100. ast_verbose(VERBOSE_PREFIX_3 "doing lookup for '%s'\n", name);
  101. /* if it's actually an IP address and not a name,
  102. there's no need for a managed lookup */
  103. if (inet_aton(name, result))
  104. return 0;
  105. /* do a lookup now but add a manager so it will automagically get updated in the background */
  106. if ((hp = ast_gethostbyname(name, &ahp)))
  107. memcpy(result, hp->h_addr, sizeof(result));
  108. /* if dnsmgr is not enable don't bother adding an entry */
  109. if (!enabled)
  110. return 0;
  111. if (option_verbose > 2)
  112. ast_verbose(VERBOSE_PREFIX_2 "adding manager for '%s'\n", name);
  113. *dnsmgr = ast_dnsmgr_get(name, result);
  114. return !*dnsmgr;
  115. }
  116. static void *do_refresh(void *data)
  117. {
  118. for (;;) {
  119. pthread_testcancel();
  120. usleep((ast_sched_wait(sched)*1000));
  121. pthread_testcancel();
  122. ast_sched_runq(sched);
  123. }
  124. return NULL;
  125. }
  126. static int refresh_list(void *data)
  127. {
  128. struct refresh_info *info = data;
  129. struct ast_dnsmgr_entry *entry;
  130. struct ast_hostent ahp;
  131. struct hostent *hp;
  132. /* if a refresh or reload is already in progress, exit now */
  133. if (ast_mutex_trylock(&refresh_lock)) {
  134. if (info->verbose)
  135. ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
  136. return -1;
  137. }
  138. if (option_verbose > 2)
  139. ast_verbose(VERBOSE_PREFIX_2 "Refreshing DNS lookups.\n");
  140. AST_LIST_LOCK(info->entries);
  141. AST_LIST_TRAVERSE(info->entries, entry, list) {
  142. if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0))
  143. continue;
  144. if (info->verbose && (option_verbose > 2))
  145. ast_verbose(VERBOSE_PREFIX_2 "refreshing '%s'\n", entry->name);
  146. if ((hp = ast_gethostbyname(entry->name, &ahp))) {
  147. /* check to see if it has changed, do callback if requested */
  148. memcpy(entry->result, hp->h_addr, sizeof(entry->result));
  149. }
  150. }
  151. AST_LIST_UNLOCK(info->entries);
  152. ast_mutex_unlock(&refresh_lock);
  153. /* automatically reschedule based on the interval */
  154. return refresh_interval * 1000;
  155. }
  156. void dnsmgr_start_refresh(void)
  157. {
  158. if (refresh_sched > -1) {
  159. ast_sched_del(sched, refresh_sched);
  160. refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
  161. }
  162. }
  163. static int do_reload(int loading);
  164. static int handle_cli_reload(int fd, int argc, char *argv[])
  165. {
  166. if (argc > 2)
  167. return RESULT_SHOWUSAGE;
  168. do_reload(0);
  169. return 0;
  170. }
  171. static int handle_cli_refresh(int fd, int argc, char *argv[])
  172. {
  173. struct refresh_info info = {
  174. .entries = &entry_list,
  175. .verbose = 1,
  176. };
  177. if (argc > 3)
  178. return RESULT_SHOWUSAGE;
  179. if (argc == 3) {
  180. if (regcomp(&info.filter, argv[2], REG_EXTENDED | REG_NOSUB))
  181. return RESULT_SHOWUSAGE;
  182. else
  183. info.regex_present = 1;
  184. }
  185. refresh_list(&info);
  186. if (info.regex_present)
  187. regfree(&info.filter);
  188. return 0;
  189. }
  190. static int handle_cli_status(int fd, int argc, char *argv[])
  191. {
  192. int count = 0;
  193. struct ast_dnsmgr_entry *entry;
  194. if (argc > 2)
  195. return RESULT_SHOWUSAGE;
  196. ast_cli(fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
  197. ast_cli(fd, "Refresh Interval: %d seconds\n", refresh_interval);
  198. AST_LIST_LOCK(&entry_list);
  199. AST_LIST_TRAVERSE(&entry_list, entry, list)
  200. count++;
  201. AST_LIST_UNLOCK(&entry_list);
  202. ast_cli(fd, "Number of entries: %d\n", count);
  203. return 0;
  204. }
  205. static struct ast_cli_entry cli_reload = {
  206. .cmda = { "dnsmgr", "reload", NULL },
  207. .handler = handle_cli_reload,
  208. .summary = "Reloads the DNS manager configuration",
  209. .usage =
  210. "Usage: dnsmgr reload\n"
  211. " Reloads the DNS manager configuration.\n"
  212. };
  213. static struct ast_cli_entry cli_refresh = {
  214. .cmda = { "dnsmgr", "refresh", NULL },
  215. .handler = handle_cli_refresh,
  216. .summary = "Performs an immediate refresh",
  217. .usage =
  218. "Usage: dnsmgr refresh [pattern]\n"
  219. " Peforms an immediate refresh of the managed DNS entries.\n"
  220. " Optional regular expression pattern is used to filter the entries to refresh.\n",
  221. };
  222. static struct ast_cli_entry cli_status = {
  223. .cmda = { "dnsmgr", "status", NULL },
  224. .handler = handle_cli_status,
  225. .summary = "Display the DNS manager status",
  226. .usage =
  227. "Usage: dnsmgr status\n"
  228. " Displays the DNS manager status.\n"
  229. };
  230. int dnsmgr_init(void)
  231. {
  232. sched = sched_context_create();
  233. if (!sched) {
  234. ast_log(LOG_ERROR, "Unable to create schedule context.\n");
  235. return -1;
  236. }
  237. ast_cli_register(&cli_reload);
  238. ast_cli_register(&cli_status);
  239. return do_reload(1);
  240. }
  241. void dnsmgr_reload(void)
  242. {
  243. do_reload(0);
  244. }
  245. static int do_reload(int loading)
  246. {
  247. struct ast_config *config;
  248. const char *interval_value;
  249. const char *enabled_value;
  250. int interval;
  251. int was_enabled;
  252. int res = -1;
  253. /* ensure that no refresh cycles run while the reload is in progress */
  254. ast_mutex_lock(&refresh_lock);
  255. /* reset defaults in preparation for reading config file */
  256. refresh_interval = REFRESH_DEFAULT;
  257. was_enabled = enabled;
  258. enabled = 0;
  259. if (refresh_sched > -1)
  260. ast_sched_del(sched, refresh_sched);
  261. if ((config = ast_config_load("dnsmgr.conf"))) {
  262. if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
  263. enabled = ast_true(enabled_value);
  264. }
  265. if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) {
  266. if (sscanf(interval_value, "%d", &interval) < 1)
  267. ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value);
  268. else if (interval < 0)
  269. ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
  270. else
  271. refresh_interval = interval;
  272. }
  273. ast_config_destroy(config);
  274. }
  275. if (enabled && refresh_interval)
  276. ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
  277. /* if this reload enabled the manager, create the background thread
  278. if it does not exist */
  279. if (enabled) {
  280. if (!was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
  281. if (ast_pthread_create(&refresh_thread, NULL, do_refresh, NULL) < 0) {
  282. ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
  283. }
  284. ast_cli_register(&cli_refresh);
  285. }
  286. /* make a background refresh happen right away */
  287. refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
  288. res = 0;
  289. }
  290. /* if this reload disabled the manager and there is a background thread,
  291. kill it */
  292. else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
  293. /* wake up the thread so it will exit */
  294. pthread_cancel(refresh_thread);
  295. pthread_kill(refresh_thread, SIGURG);
  296. pthread_join(refresh_thread, NULL);
  297. refresh_thread = AST_PTHREADT_NULL;
  298. ast_cli_unregister(&cli_refresh);
  299. res = 0;
  300. }
  301. else
  302. res = 0;
  303. ast_mutex_unlock(&refresh_lock);
  304. return res;
  305. }