Author: file
Date: Thu Mar 12 09:00:37 2015
New Revision: 432832

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=432832
Log:
res_resolver_unbound: Add configuration support.

Modified:
    team/group/dns/res/res_resolver_unbound.c

Modified: team/group/dns/res/res_resolver_unbound.c
URL: 
http://svnview.digium.com/svn/asterisk/team/group/dns/res/res_resolver_unbound.c?view=diff&rev=432832&r1=432831&r2=432832
==============================================================================
--- team/group/dns/res/res_resolver_unbound.c (original)
+++ team/group/dns/res/res_resolver_unbound.c Thu Mar 12 09:00:37 2015
@@ -31,6 +31,44 @@
 #include "asterisk/linkedlists.h"
 #include "asterisk/dns_core.h"
 #include "asterisk/dns_resolver.h"
+#include "asterisk/config.h"
+#include "asterisk/config_options.h"
+
+/*** DOCUMENTATION
+       <configInfo name="res_resolver_unbound" language="en_US">
+               <configFile name="resolver_unbound.conf">
+                       <configObject name="globals">
+                               <synopsis>Options that apply globally to 
res_resolver_unbound</synopsis>
+                               <configOption name="hosts">
+                                       <synopsis>Full path to an optional 
hosts file</synopsis>
+                                       <description><para>Hosts specified in a 
hosts file will be resolved within the resolver itself. If a value
+                                       of system is provided the 
system-specific file will be used.</para></description>
+                               </configOption>
+                               <configOption name="resolv">
+                                       <synopsis>Full path to an optional 
resolv.conf file</synopsis>
+                                       <description><para>The resolv.conf file 
specifies the nameservers to contact when resolving queries. If a
+                                       value of system is provided the 
system-specific file will be used.</para></description>
+                               </configOption>
+                               <configOption name="nameserver">
+                                       <synopsis>Nameserver to use for 
queries</synopsis>
+                                       <description><para>An explicit 
nameserver can be specified which is used for resolving queries. If multiple
+                                       nameserver lines are specified the 
first will be the primary with failover occurring, in order, to the other
+                                       nameservers as 
backups.</para></description>
+                               </configOption>
+                               <configOption name="debug">
+                                       <synopsis>Unbound debug level</synopsis>
+                                       <description><para>The debugging level 
for the unbound resolver. While there is no explicit range generally
+                                       the higher the number the more debug is 
output.</para></description>
+                               </configOption>
+                               <configOption name="ta_file">
+                                       <synopsis>Trust anchor file</synopsis>
+                                       <description><para>Full path to a file 
with DS and DNSKEY records in zone file format. This file is provided
+                                       to unbound and is used as a source for 
trust anchors.</para></description>
+                               </configOption>
+                       </configObject>
+               </configFile>
+       </configInfo>
+ ***/
 
 /*! \brief Structure for an unbound resolver */
 struct unbound_resolver {
@@ -44,10 +82,73 @@
 struct unbound_resolver_data {
        /*! \brief ID for the specific query */
        int id;
-};
-
-/*! \brief Unbound resolver */
-static struct unbound_resolver *resolver;
+       /*! \brief The resolver in use for the query */
+       struct unbound_resolver *resolver;
+};
+
+/*! \brief Unbound configuration state information */
+struct unbound_config_state {
+       /*! \brief The configured resolver */
+       struct unbound_resolver *resolver;
+};
+
+/*! \brief A structure to hold global configuration-related options */
+struct unbound_global_config {
+       AST_DECLARE_STRING_FIELDS(
+               AST_STRING_FIELD(hosts);   /*!< Optional hosts file */
+               AST_STRING_FIELD(resolv);  /*!< Optional resolv.conf file */
+               AST_STRING_FIELD(ta_file); /*!< Optional trust anchor file */
+       );
+       /*! \brief List of nameservers (in order) to use for queries */
+       struct ao2_container *nameservers;
+       /*! \brief Debug level for the resolver */
+       unsigned int debug;
+       /*! \brief State information */
+       struct unbound_config_state *state;
+};
+
+/*! \brief A container for config related information */
+struct unbound_config {
+       struct unbound_global_config *global;
+};
+
+/*!
+ * \brief Allocate a unbound_config to hold a snapshot of the complete results 
of parsing a config
+ * \internal
+ * \returns A void pointer to a newly allocated unbound_config
+ */
+static void *unbound_config_alloc(void);
+
+/*! \brief An aco_type structure to link the "general" category to the 
unbound_global_config type */
+static struct aco_type global_option = {
+       .type = ACO_GLOBAL,
+       .name = "globals",
+       .item_offset = offsetof(struct unbound_config, global),
+       .category_match = ACO_WHITELIST,
+       .category = "^general$",
+};
+
+static struct aco_type *global_options[] = ACO_TYPES(&global_option);
+
+static struct aco_file resolver_unbound_conf = {
+       .filename = "resolver_unbound.conf",
+       .types = ACO_TYPES(&global_option),
+};
+
+/*! \brief A global object container that will contain the global_config that 
gets swapped out on reloads */
+static AO2_GLOBAL_OBJ_STATIC(globals);
+
+/*!
+ * \brief Finish initializing new configuration
+ * \internal
+ */
+static int unbound_config_preapply_callback(void);
+
+/*! \brief Register information about the configs being processed by this 
module */
+CONFIG_INFO_STANDARD(cfg_info, globals, unbound_config_alloc,
+       .files = ACO_FILES(&resolver_unbound_conf),
+       .pre_apply_config = unbound_config_preapply_callback,
+);
 
 /*! \brief Destructor for unbound resolver */
 static void unbound_resolver_destroy(void *obj)
@@ -79,9 +180,6 @@
 
        /* Each async result should be invoked in a separate thread so others 
are not blocked */
        ub_ctx_async(resolver->context, 1);
-
-       ub_ctx_resolvconf(resolver->context, NULL);
-       ub_ctx_hosts(resolver->context, NULL);
 
        return resolver;
 }
@@ -171,6 +269,7 @@
 
 static int unbound_resolver_resolve(struct ast_dns_query *query)
 {
+       struct unbound_config *cfg = ao2_global_obj_ref(globals);
        struct unbound_resolver_data *data;
        int res;
 
@@ -180,9 +279,10 @@
                        ast_dns_query_get_name(query));
                return -1;
        }
+       data->resolver = ao2_bump(cfg->global->state->resolver);
        ast_dns_resolver_set_data(query, data);
 
-       res = ub_resolve_async(resolver->context, ast_dns_query_get_name(query),
+       res = ub_resolve_async(data->resolver->context, 
ast_dns_query_get_name(query),
                ast_dns_query_get_rr_type(query), 
ast_dns_query_get_rr_class(query),
                ao2_bump(query), unbound_resolver_callback, &data->id);
 
@@ -193,6 +293,7 @@
        }
 
        ao2_ref(data, -1);
+       ao2_ref(cfg, -1);
 
        return res;
 }
@@ -202,7 +303,7 @@
        struct unbound_resolver_data *data = ast_dns_resolver_get_data(query);
        int res;
 
-       res = ub_cancel(resolver->context, data->id);
+       res = ub_cancel(data->resolver->context, data->id);
        if (!res) {
                /* When this query was started we bumped the ref, now that it 
has been cancelled we have ownership and
                 * need to drop it
@@ -220,25 +321,222 @@
        .cancel = unbound_resolver_cancel,
 };
 
+static void unbound_config_destructor(void *obj)
+{
+       struct unbound_config *cfg = obj;
+
+       ao2_cleanup(cfg->global);
+}
+
+static void unbound_global_config_destructor(void *obj)
+{
+       struct unbound_global_config *global = obj;
+
+       ast_string_field_free_memory(global);
+       ao2_cleanup(global->nameservers);
+       ao2_cleanup(global->state);
+}
+
+static void unbound_config_state_destructor(void *obj)
+{
+       struct unbound_config_state *state = obj;
+
+       if (state->resolver) {
+               unbound_resolver_stop(state->resolver);
+               ao2_ref(state->resolver, -1);
+       }
+}
+
+static void *unbound_config_alloc(void)
+{
+       struct unbound_config *cfg;
+
+       cfg = ao2_alloc_options(sizeof(*cfg), unbound_config_destructor, 
AO2_ALLOC_OPT_LOCK_NOLOCK);
+       if (!cfg) {
+               return NULL;
+       }
+
+       /* Allocate/initialize memory */
+       cfg->global = ao2_alloc_options(sizeof(*cfg->global), 
unbound_global_config_destructor,
+               AO2_ALLOC_OPT_LOCK_NOLOCK);
+       if (!cfg->global) {
+               goto error;
+       }
+
+       if (ast_string_field_init(cfg->global, 128)) {
+               goto error;
+       }
+
+       cfg->global->nameservers = 
ast_str_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1);
+       if (!cfg->global->nameservers) {
+               goto error;
+       }
+
+       return cfg;
+error:
+       ao2_ref(cfg, -1);
+       return NULL;
+}
+
+static int unbound_config_preapply(struct unbound_config *cfg)
+{
+       int res = 0;
+       struct ao2_iterator it_nameservers;
+       const char *nameserver;
+
+       cfg->global->state = ao2_alloc_options(sizeof(*cfg->global->state), 
unbound_config_state_destructor,
+               AO2_ALLOC_OPT_LOCK_NOLOCK);
+       if (!cfg->global->state) {
+               ast_log(LOG_ERROR, "Could not allocate unbound resolver state 
structure\n");
+               return -1;
+       }
+
+       cfg->global->state->resolver = unbound_resolver_alloc();
+       if (!cfg->global->state->resolver) {
+               ast_log(LOG_ERROR, "Could not create an unbound resolver\n");
+               return -1;
+       }
+
+       if (!strcmp(cfg->global->hosts, "system")) {
+               res = ub_ctx_hosts(cfg->global->state->resolver->context, NULL);
+       } else if (!ast_strlen_zero(cfg->global->hosts)) {
+               res = ub_ctx_hosts(cfg->global->state->resolver->context, 
cfg->global->hosts);
+       }
+
+       if (res) {
+               ast_log(LOG_ERROR, "Failed to set hosts file to '%s' in unbound 
resolver: %s\n",
+                       cfg->global->hosts, ub_strerror(res));
+               return -1;
+       }
+
+       if (!strcmp(cfg->global->resolv, "system")) {
+               res = ub_ctx_resolvconf(cfg->global->state->resolver->context, 
NULL);
+       } else if (!ast_strlen_zero(cfg->global->resolv)) {
+               res = ub_ctx_resolvconf(cfg->global->state->resolver->context, 
cfg->global->resolv);
+       }
+
+       if (res) {
+               ast_log(LOG_ERROR, "Failed to set resolv.conf file to '%s' in 
unbound resolver: %s\n",
+                       cfg->global->resolv, ub_strerror(res));
+               return -1;
+       }
+
+       it_nameservers = ao2_iterator_init(cfg->global->nameservers, 0);
+       while ((nameserver = ao2_iterator_next(&it_nameservers))) {
+               res = ub_ctx_set_fwd(cfg->global->state->resolver->context, 
nameserver);
+
+               if (res) {
+                       ast_log(LOG_ERROR, "Failed to add nameserver '%s' to 
unbound resolver: %s\n",
+                               nameserver, ub_strerror(res));
+                       ao2_iterator_destroy(&it_nameservers);
+                       return -1;
+               }
+       }
+       ao2_iterator_destroy(&it_nameservers);
+
+       ub_ctx_debuglevel(cfg->global->state->resolver->context, 
cfg->global->debug);
+
+       if (!ast_strlen_zero(cfg->global->ta_file)) {
+               res = ub_ctx_add_ta_file(cfg->global->state->resolver->context, 
cfg->global->ta_file);
+
+               if (res) {
+                       ast_log(LOG_ERROR, "Failed to set trusted anchor file 
to '%s' in unbound resolver: %s\n",
+                               cfg->global->ta_file, ub_strerror(res));
+                       return -1;
+               }
+       }
+
+       if (unbound_resolver_start(cfg->global->state->resolver)) {
+               ast_log(LOG_ERROR, "Could not start unbound resolver thread\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int unbound_config_apply_default(void)
+{
+       struct unbound_config *cfg;
+
+       cfg = unbound_config_alloc();
+       if (!cfg) {
+               ast_log(LOG_ERROR, "Could not create default configuration for 
unbound resolver\n");
+               return -1;
+       }
+
+       aco_set_defaults(&global_option, "general", cfg->global);
+
+       if (unbound_config_preapply(cfg)) {
+               return -1;
+       }
+
+       ast_verb(1, "Starting unbound resolver using default configuration\n");
+
+       ao2_global_obj_replace_unref(globals, cfg);
+       ao2_ref(cfg, -1);
+
+       return 0;
+}
+
+static int unbound_config_preapply_callback(void)
+{
+       return unbound_config_preapply(aco_pending_config(&cfg_info));
+}
+
+static int reload_module(void)
+{
+       if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
+               return AST_MODULE_RELOAD_ERROR;
+       }
+
+       return 0;
+}
+
 static int unload_module(void)
 {
-       unbound_resolver_stop(resolver);
-       ao2_replace(resolver, NULL);
+       aco_info_destroy(&cfg_info);
+       ao2_global_obj_release(globals);
        return 0;
 }
 
+static int custom_nameserver_handler(const struct aco_option *opt, struct 
ast_variable *var, void *obj)
+{
+       struct unbound_global_config *global = obj;
+
+       return ast_str_container_add(global->nameservers, var->value);
+}
+
 static int load_module(void)
 {
-       resolver = unbound_resolver_alloc();
-       if (!resolver) {
+       struct ast_config *cfg;
+       struct ast_flags cfg_flags = { 0, };
+
+       if (aco_info_init(&cfg_info)) {
                return AST_MODULE_LOAD_DECLINE;
        }
 
-       if (unbound_resolver_start(resolver) ||
-               ast_dns_resolver_register(&unbound_resolver)) {
-               unload_module();
-               return AST_MODULE_LOAD_DECLINE;
-       }
+       aco_option_register(&cfg_info, "hosts", ACO_EXACT, global_options, "", 
OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, hosts));
+       aco_option_register(&cfg_info, "resolv", ACO_EXACT, global_options, "", 
OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, resolv));
+       aco_option_register_custom(&cfg_info, "nameserver", ACO_EXACT, 
global_options, "", custom_nameserver_handler, 0);
+       aco_option_register(&cfg_info, "debug", ACO_EXACT, global_options, "0", 
OPT_UINT_T, 0, FLDSET(struct unbound_global_config, debug));
+       aco_option_register(&cfg_info, "ta_file", ACO_EXACT, global_options, 
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct unbound_global_config, ta_file));
+
+       /* This purposely checks for a configuration file so we don't output an 
error message in ACO if one is not present */
+       cfg = ast_config_load(resolver_unbound_conf.filename, cfg_flags);
+       if (!cfg) {
+               if (unbound_config_apply_default()) {
+                       unload_module();
+                       return AST_MODULE_LOAD_DECLINE;
+               }
+       } else {
+               ast_config_destroy(cfg);
+               if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
+                       unload_module();
+                       return AST_MODULE_LOAD_DECLINE;
+               }
+       }
+
+       ast_dns_resolver_register(&unbound_resolver);
 
        ast_module_shutdown_ref(ast_module_info->self);
 
@@ -249,5 +547,6 @@
                .support_level = AST_MODULE_SUPPORT_CORE,
                .load = load_module,
                .unload = unload_module,
+               .reload = reload_module,
                .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
               );


-- 
_____________________________________________________________________
-- Bandwidth and Colocation Provided by http://www.api-digital.com --

svn-commits mailing list
To UNSUBSCRIBE or update options visit:
   http://lists.digium.com/mailman/listinfo/svn-commits

Reply via email to