dns_core.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2015, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@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 Core DNS Functionality
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  */
00025 
00026 /*** MODULEINFO
00027    <support_level>core</support_level>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 434490 $")
00033 
00034 #include "asterisk/linkedlists.h"
00035 #include "asterisk/vector.h"
00036 #include "asterisk/astobj2.h"
00037 #include "asterisk/strings.h"
00038 #include "asterisk/sched.h"
00039 #include "asterisk/dns_core.h"
00040 #include "asterisk/dns_srv.h"
00041 #include "asterisk/dns_tlsa.h"
00042 #include "asterisk/dns_recurring.h"
00043 #include "asterisk/dns_resolver.h"
00044 #include "asterisk/dns_internal.h"
00045 
00046 #include <netinet/in.h>
00047 #include <arpa/nameser.h>
00048 
00049 AST_RWLIST_HEAD_STATIC(resolvers, ast_dns_resolver);
00050 
00051 static struct ast_sched_context *sched;
00052 
00053 struct ast_sched_context *ast_dns_get_sched(void)
00054 {
00055    return sched;
00056 }
00057 
00058 const char *ast_dns_query_get_name(const struct ast_dns_query *query)
00059 {
00060    return query->name;
00061 }
00062 
00063 int ast_dns_query_get_rr_type(const struct ast_dns_query *query)
00064 {
00065    return query->rr_type;
00066 }
00067 
00068 int ast_dns_query_get_rr_class(const struct ast_dns_query *query)
00069 {
00070    return query->rr_class;
00071 }
00072 
00073 void *ast_dns_query_get_data(const struct ast_dns_query *query)
00074 {
00075    return query->user_data;
00076 }
00077 
00078 struct ast_dns_result *ast_dns_query_get_result(const struct ast_dns_query *query)
00079 {
00080    return query->result;
00081 }
00082 
00083 unsigned int ast_dns_result_get_secure(const struct ast_dns_result *result)
00084 {
00085    return result->secure;
00086 }
00087 
00088 unsigned int ast_dns_result_get_bogus(const struct ast_dns_result *result)
00089 {
00090    return result->bogus;
00091 }
00092 
00093 unsigned int ast_dns_result_get_rcode(const struct ast_dns_result *result)
00094 {
00095    return result->rcode;
00096 }
00097 
00098 const char *ast_dns_result_get_canonical(const struct ast_dns_result *result)
00099 {
00100    return result->canonical;
00101 }
00102 
00103 const struct ast_dns_record *ast_dns_result_get_records(const struct ast_dns_result *result)
00104 {
00105    return AST_LIST_FIRST(&result->records);
00106 }
00107 
00108 const char *ast_dns_result_get_answer(const struct ast_dns_result *result)
00109 {
00110    return result->answer;
00111 }
00112 
00113 int ast_dns_result_get_lowest_ttl(const struct ast_dns_result *result)
00114 {
00115    int ttl = 0;
00116    const struct ast_dns_record *record;
00117 
00118    if (ast_dns_result_get_rcode(result) == ns_r_nxdomain) {
00119       return 0;
00120    }
00121 
00122    for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
00123       if (!ttl || (ast_dns_record_get_ttl(record) && (ast_dns_record_get_ttl(record) < ttl))) {
00124          ttl = ast_dns_record_get_ttl(record);
00125       }
00126    }
00127 
00128    return ttl;
00129 }
00130 
00131 void ast_dns_result_free(struct ast_dns_result *result)
00132 {
00133    struct ast_dns_record *record;
00134 
00135    if (!result) {
00136       return;
00137    }
00138 
00139    while ((record = AST_LIST_REMOVE_HEAD(&result->records, list))) {
00140       ast_free(record);
00141    }
00142 
00143    ast_free(result);
00144 }
00145 
00146 int ast_dns_record_get_rr_type(const struct ast_dns_record *record)
00147 {
00148    return record->rr_type;
00149 }
00150 
00151 int ast_dns_record_get_rr_class(const struct ast_dns_record *record)
00152 {
00153    return record->rr_class;
00154 }
00155 
00156 int ast_dns_record_get_ttl(const struct ast_dns_record *record)
00157 {
00158    return record->ttl;
00159 }
00160 
00161 const char *ast_dns_record_get_data(const struct ast_dns_record *record)
00162 {
00163    return record->data_ptr;
00164 }
00165 
00166 const struct ast_dns_record *ast_dns_record_get_next(const struct ast_dns_record *record)
00167 {
00168    return AST_LIST_NEXT(record, list);
00169 }
00170 
00171 /*! \brief Destructor for an active DNS query */
00172 static void dns_query_active_destroy(void *data)
00173 {
00174    struct ast_dns_query_active *active = data;
00175 
00176    ao2_cleanup(active->query);
00177 }
00178 
00179 /*! \brief \brief Destructor for a DNS query */
00180 static void dns_query_destroy(void *data)
00181 {
00182    struct ast_dns_query *query = data;
00183 
00184    ao2_cleanup(query->user_data);
00185    ao2_cleanup(query->resolver_data);
00186    ast_dns_result_free(query->result);
00187 }
00188 
00189 struct ast_dns_query_active *ast_dns_resolve_async(const char *name, int rr_type, int rr_class, ast_dns_resolve_callback callback, void *data)
00190 {
00191    struct ast_dns_query_active *active;
00192 
00193    if (ast_strlen_zero(name)) {
00194       ast_log(LOG_WARNING, "Could not perform asynchronous resolution, no name provided\n");
00195       return NULL;
00196    } else if (rr_type > ns_t_max) {
00197       ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', resource record type '%d' exceeds maximum\n",
00198          name, rr_type);
00199       return NULL;
00200    } else if (rr_type < 0) {
00201       ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', invalid resource record type '%d'\n",
00202          name, rr_type);
00203       return NULL;
00204    } else if (rr_class > ns_c_max) {
00205       ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', resource record class '%d' exceeds maximum\n",
00206          name, rr_class);
00207       return NULL;
00208    } else if (rr_class < 0) {
00209       ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', invalid resource class '%d'\n",
00210          name, rr_class);
00211       return NULL;
00212    } else if (!callback) {
00213       ast_log(LOG_WARNING, "Could not perform asynchronous resolution of '%s', no callback provided\n",
00214          name);
00215       return NULL;
00216    }
00217 
00218    active = ao2_alloc_options(sizeof(*active), dns_query_active_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
00219    if (!active) {
00220       return NULL;
00221    }
00222 
00223    active->query = ao2_alloc_options(sizeof(*active->query) + strlen(name) + 1, dns_query_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
00224    if (!active->query) {
00225       ao2_ref(active, -1);
00226       return NULL;
00227    }
00228 
00229    active->query->callback = callback;
00230    active->query->user_data = ao2_bump(data);
00231    active->query->rr_type = rr_type;
00232    active->query->rr_class = rr_class;
00233    strcpy(active->query->name, name); /* SAFE */
00234 
00235    AST_RWLIST_RDLOCK(&resolvers);
00236    active->query->resolver = AST_RWLIST_FIRST(&resolvers);
00237    AST_RWLIST_UNLOCK(&resolvers);
00238 
00239    if (!active->query->resolver) {
00240       ast_log(LOG_ERROR, "Attempted to do a DNS query for '%s' of class '%d' and type '%d' but no resolver is available\n",
00241          name, rr_class, rr_type);
00242       ao2_ref(active, -1);
00243       return NULL;
00244    }
00245 
00246    if (active->query->resolver->resolve(active->query)) {
00247       ast_log(LOG_ERROR, "Resolver '%s' returned an error when resolving '%s' of class '%d' and type '%d'\n",
00248          active->query->resolver->name, name, rr_class, rr_type);
00249       ao2_ref(active, -1);
00250       return NULL;
00251    }
00252 
00253    return active;
00254 }
00255 
00256 int ast_dns_resolve_cancel(struct ast_dns_query_active *active)
00257 {
00258    return active->query->resolver->cancel(active->query);
00259 }
00260 
00261 /*! \brief Structure used for signaling back for synchronous resolution completion */
00262 struct dns_synchronous_resolve {
00263    /*! \brief Lock used for signaling */
00264    ast_mutex_t lock;
00265    /*! \brief Condition used for signaling */
00266    ast_cond_t cond;
00267    /*! \brief Whether the query has completed */
00268    unsigned int completed;
00269    /*! \brief The result from the query */
00270    struct ast_dns_result *result;
00271 };
00272 
00273 /*! \brief Destructor for synchronous resolution structure */
00274 static void dns_synchronous_resolve_destroy(void *data)
00275 {
00276    struct dns_synchronous_resolve *synchronous = data;
00277 
00278    ast_mutex_destroy(&synchronous->lock);
00279    ast_cond_destroy(&synchronous->cond);
00280 
00281    /* This purposely does not unref result as it has been passed to the caller */
00282 }
00283 
00284 /*! \brief Callback used to implement synchronous resolution */
00285 static void dns_synchronous_resolve_callback(const struct ast_dns_query *query)
00286 {
00287    struct dns_synchronous_resolve *synchronous = ast_dns_query_get_data(query);
00288 
00289    synchronous->result = query->result;
00290    ((struct ast_dns_query *)query)->result = NULL;
00291 
00292    ast_mutex_lock(&synchronous->lock);
00293    synchronous->completed = 1;
00294    ast_cond_signal(&synchronous->cond);
00295    ast_mutex_unlock(&synchronous->lock);
00296 }
00297 
00298 int ast_dns_resolve(const char *name, int rr_type, int rr_class, struct ast_dns_result **result)
00299 {
00300    struct dns_synchronous_resolve *synchronous;
00301    struct ast_dns_query_active *active;
00302 
00303    if (ast_strlen_zero(name)) {
00304       ast_log(LOG_WARNING, "Could not perform synchronous resolution, no name provided\n");
00305       return -1;
00306    } else if (rr_type > ns_t_max) {
00307       ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', resource record type '%d' exceeds maximum\n",
00308          name, rr_type);
00309       return -1;
00310    } else if (rr_type < 0) {
00311       ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', invalid resource record type '%d'\n",
00312          name, rr_type);
00313       return -1;
00314    } else if (rr_class > ns_c_max) {
00315       ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', resource record class '%d' exceeds maximum\n",
00316          name, rr_class);
00317       return -1;
00318    } else if (rr_class < 0) {
00319       ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', invalid resource class '%d'\n",
00320          name, rr_class);
00321       return -1;
00322    } else if (!result) {
00323       ast_log(LOG_WARNING, "Could not perform synchronous resolution of '%s', no result pointer provided for storing results\n",
00324          name);
00325       return -1;
00326    }
00327 
00328    synchronous = ao2_alloc_options(sizeof(*synchronous), dns_synchronous_resolve_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
00329    if (!synchronous) {
00330       return -1;
00331    }
00332 
00333    ast_mutex_init(&synchronous->lock);
00334    ast_cond_init(&synchronous->cond, NULL);
00335 
00336    active = ast_dns_resolve_async(name, rr_type, rr_class, dns_synchronous_resolve_callback, synchronous);
00337    if (active) {
00338       /* Wait for resolution to complete */
00339       ast_mutex_lock(&synchronous->lock);
00340       while (!synchronous->completed) {
00341          ast_cond_wait(&synchronous->cond, &synchronous->lock);
00342       }
00343       ast_mutex_unlock(&synchronous->lock);
00344       ao2_ref(active, -1);
00345    }
00346 
00347    *result = synchronous->result;
00348    ao2_ref(synchronous, -1);
00349 
00350    return *result ? 0 : -1;
00351 }
00352 
00353 int ast_dns_resolver_set_data(struct ast_dns_query *query, void *data)
00354 {
00355    if (query->resolver_data) {
00356       return -1;
00357    }
00358 
00359    query->resolver_data = ao2_bump(data);
00360 
00361    return 0;
00362 }
00363 
00364 void *ast_dns_resolver_get_data(const struct ast_dns_query *query)
00365 {
00366    return query->resolver_data;
00367 }
00368 
00369 int ast_dns_resolver_set_result(struct ast_dns_query *query, unsigned int secure, unsigned int bogus,
00370    unsigned int rcode, const char *canonical, const char *answer, size_t answer_size)
00371 {
00372    char *buf_ptr;
00373 
00374    if (secure && bogus) {
00375       ast_debug(2, "Query '%p': Could not set result information, it can not be both secure and bogus\n",
00376          query);
00377       return -1;
00378    }
00379 
00380    if (ast_strlen_zero(canonical)) {
00381       ast_debug(2, "Query '%p': Could not set result information since no canonical name was provided\n",
00382          query);
00383       return -1;
00384    }
00385 
00386    if (!answer || answer_size == 0) {
00387       ast_debug(2, "Query '%p': Could not set result information since no DNS answer was provided\n",
00388          query);
00389       return -1;
00390    }
00391 
00392    ast_dns_result_free(query->result);
00393 
00394    query->result = ast_calloc(1, sizeof(*query->result) + strlen(canonical) + 1 + answer_size);
00395    if (!query->result) {
00396       return -1;
00397    }
00398 
00399    query->result->secure = secure;
00400    query->result->bogus = bogus;
00401    query->result->rcode = rcode;
00402 
00403    buf_ptr = query->result->buf;
00404    strcpy(buf_ptr, canonical); /* SAFE */
00405    query->result->canonical = buf_ptr;
00406 
00407    buf_ptr += strlen(canonical) + 1;
00408    memcpy(buf_ptr, answer, answer_size); /* SAFE */
00409    query->result->answer = buf_ptr;
00410    query->result->answer_size = answer_size;
00411 
00412    return 0;
00413 }
00414 
00415 static struct ast_dns_record *generic_record_alloc(struct ast_dns_query *query, const char *data, const size_t size)
00416 {
00417    struct ast_dns_record *record;
00418 
00419    record = ast_calloc(1, sizeof(*record) + size);
00420    if (!record) {
00421       return NULL;
00422    }
00423 
00424    record->data_ptr = record->data;
00425 
00426    return record;
00427 }
00428 
00429 typedef struct ast_dns_record *(*dns_alloc_fn)(struct ast_dns_query *query, const char *data, const size_t size);
00430 
00431 static dns_alloc_fn dns_alloc_table [] = {
00432    [ns_t_naptr] = dns_naptr_alloc,
00433    [ns_t_srv] = dns_srv_alloc,
00434 };
00435 
00436 static struct ast_dns_record *allocate_dns_record(int rr_type, struct ast_dns_query *query, const char *data, const size_t size)
00437 {
00438    dns_alloc_fn allocator = dns_alloc_table[rr_type] ?: generic_record_alloc;
00439 
00440    return allocator(query, data, size);
00441 }
00442 
00443 int ast_dns_resolver_add_record(struct ast_dns_query *query, int rr_type, int rr_class, int ttl, const char *data, const size_t size)
00444 {
00445    struct ast_dns_record *record;
00446 
00447    if (rr_type < 0) {
00448       ast_debug(2, "Query '%p': Could not add record, invalid resource record type '%d'\n",
00449          query, rr_type);
00450       return -1;
00451    } else if (rr_type > ns_t_max) {
00452       ast_debug(2, "Query '%p': Could not add record, resource record type '%d' exceeds maximum\n",
00453          query, rr_type);
00454       return -1;
00455    } else if (rr_class < 0) {
00456       ast_debug(2, "Query '%p': Could not add record, invalid resource record class '%d'\n",
00457          query, rr_class);
00458       return -1;
00459    } else if (rr_class > ns_c_max) {
00460       ast_debug(2, "Query '%p': Could not add record, resource record class '%d' exceeds maximum\n",
00461          query, rr_class);
00462       return -1;
00463    } else if (ttl < 0) {
00464       ast_debug(2, "Query '%p': Could not add record, invalid TTL '%d'\n",
00465          query, ttl);
00466       return -1;
00467    } else if (!data || !size) {
00468       ast_debug(2, "Query '%p': Could not add record, no data specified\n",
00469          query);
00470       return -1;
00471    } else if (!query->result) {
00472       ast_debug(2, "Query '%p': No result was set on the query, thus records can not be added\n",
00473          query);
00474       return -1;
00475    }
00476 
00477    record = allocate_dns_record(rr_type, query, data, size);
00478    if (!record) {
00479       return -1;
00480    }
00481 
00482    record->rr_type = rr_type;
00483    record->rr_class = rr_class;
00484    record->ttl = ttl;
00485    record->data_len = size;
00486    memcpy(record->data_ptr, data, size);
00487 
00488    AST_LIST_INSERT_TAIL(&query->result->records, record, list);
00489 
00490    return 0;
00491 }
00492 
00493 typedef void (*dns_sort_fn)(struct ast_dns_result *result);
00494 
00495 static dns_sort_fn dns_sort_table [] = {
00496    [ns_t_naptr] = dns_naptr_sort,
00497    [ns_t_srv] = dns_srv_sort,
00498 };
00499 
00500 static void sort_result(int rr_type, struct ast_dns_result *result)
00501 {
00502    if (dns_sort_table[rr_type]) {
00503       dns_sort_table[rr_type](result);
00504    }
00505 }
00506 
00507 void ast_dns_resolver_completed(struct ast_dns_query *query)
00508 {
00509    sort_result(ast_dns_query_get_rr_type(query), query->result);
00510 
00511    query->callback(query);
00512 }
00513 
00514 static void dns_shutdown(void)
00515 {
00516    if (sched) {
00517       ast_sched_context_destroy(sched);
00518       sched = NULL;
00519    }
00520 }
00521 
00522 int ast_dns_resolver_register(struct ast_dns_resolver *resolver)
00523 {
00524    struct ast_dns_resolver *iter;
00525    int inserted = 0;
00526 
00527    if (!resolver) {
00528       return -1;
00529    } else if (ast_strlen_zero(resolver->name)) {
00530       ast_log(LOG_ERROR, "Registration of DNS resolver failed as it does not have a name\n");
00531       return -1;
00532    } else if (!resolver->resolve) {
00533       ast_log(LOG_ERROR, "DNS resolver '%s' does not implement the resolve callback which is required\n",
00534          resolver->name);
00535       return -1;
00536    } else if (!resolver->cancel) {
00537       ast_log(LOG_ERROR, "DNS resolver '%s' does not implement the cancel callback which is required\n",
00538          resolver->name);
00539       return -1;
00540    }
00541 
00542    AST_RWLIST_WRLOCK(&resolvers);
00543 
00544    /* On the first registration of a resolver start a scheduler for recurring queries */
00545    if (AST_LIST_EMPTY(&resolvers) && !sched) {
00546       sched = ast_sched_context_create();
00547       if (!sched) {
00548          ast_log(LOG_ERROR, "DNS resolver '%s' could not be registered: Failed to create scheduler for recurring DNS queries\n",
00549             resolver->name);
00550          AST_RWLIST_UNLOCK(&resolvers);
00551          return -1;
00552       }
00553 
00554       if (ast_sched_start_thread(sched)) {
00555          ast_log(LOG_ERROR, "DNS resolver '%s' could not be registered: Failed to start thread for recurring DNS queries\n",
00556             resolver->name);
00557          dns_shutdown();
00558          AST_RWLIST_UNLOCK(&resolvers);
00559          return -1;
00560       }
00561 
00562       ast_register_cleanup(dns_shutdown);
00563    }
00564 
00565    AST_LIST_TRAVERSE(&resolvers, iter, next) {
00566       if (!strcmp(iter->name, resolver->name)) {
00567          ast_log(LOG_ERROR, "A DNS resolver with the name '%s' is already registered\n", resolver->name);
00568          AST_RWLIST_UNLOCK(&resolvers);
00569          return -1;
00570       }
00571    }
00572 
00573    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&resolvers, iter, next) {
00574       if (iter->priority > resolver->priority) {
00575          AST_RWLIST_INSERT_BEFORE_CURRENT(resolver, next);
00576          inserted = 1;
00577          break;
00578       }
00579    }
00580    AST_RWLIST_TRAVERSE_SAFE_END;
00581 
00582    if (!inserted) {
00583       AST_RWLIST_INSERT_TAIL(&resolvers, resolver, next);
00584    }
00585 
00586    AST_RWLIST_UNLOCK(&resolvers);
00587 
00588    ast_verb(2, "Registered DNS resolver '%s' with priority '%d'\n", resolver->name, resolver->priority);
00589 
00590    return 0;
00591 }
00592 
00593 void ast_dns_resolver_unregister(struct ast_dns_resolver *resolver)
00594 {
00595    struct ast_dns_resolver *iter;
00596 
00597    if (!resolver) {
00598       return;
00599    }
00600 
00601    AST_RWLIST_WRLOCK(&resolvers);
00602    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&resolvers, iter, next) {
00603       if (resolver == iter) {
00604          AST_RWLIST_REMOVE_CURRENT(next);
00605          break;
00606       }
00607    }
00608    AST_RWLIST_TRAVERSE_SAFE_END;
00609    AST_RWLIST_UNLOCK(&resolvers);
00610 
00611    ast_verb(2, "Unregistered DNS resolver '%s'\n", resolver->name);
00612 }
00613 
00614 char *dns_find_record(const char *record, size_t record_size, const char *response, size_t response_size)
00615 {
00616    size_t remaining_size = response_size;
00617    const char *search_base = response;
00618    char *record_offset;
00619 
00620    while (1) {
00621       record_offset = memchr(search_base, record[0], remaining_size);
00622 
00623       ast_assert(record_offset != NULL);
00624       ast_assert(search_base + remaining_size - record_offset >= record_size);
00625 
00626       if (!memcmp(record_offset, record, record_size)) {
00627          return record_offset;
00628       }
00629 
00630       remaining_size -= record_offset - search_base;
00631       search_base = record_offset + 1;
00632    }
00633 }
00634 
00635 int dns_parse_short(unsigned char *cur, uint16_t *val)
00636 {
00637    /* This assignment takes a big-endian 16-bit value and stores it in the
00638     * machine's native byte order. Using this method allows us to avoid potential
00639     * alignment issues in case the order is not on a short-addressable boundary.
00640     * See http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html for
00641     * more information
00642     */
00643    *val = (cur[1] << 0) | (cur[0] << 8);
00644    return sizeof(*val);
00645 }
00646 
00647 int dns_parse_string(char *cur, uint8_t *size, char **val)
00648 {
00649    *size = *cur++;
00650    *val = cur;
00651    return *size + 1;
00652 }

Generated on Thu Apr 16 06:27:33 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6