PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/asterisk-1.4/main/dnsmgr.c

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