dns_srv.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 DNS SRV Record Support
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 <netinet/in.h>
00035 #include <arpa/nameser.h>
00036 #include <resolv.h>
00037 
00038 #include "asterisk/dns_core.h"
00039 #include "asterisk/dns_srv.h"
00040 #include "asterisk/linkedlists.h"
00041 #include "asterisk/dns_internal.h"
00042 #include "asterisk/utils.h"
00043 
00044 struct ast_dns_record *dns_srv_alloc(struct ast_dns_query *query, const char *data, const size_t size)
00045 {
00046    uint16_t priority;
00047    uint16_t weight;
00048    uint16_t port;
00049    const char *ptr;
00050    const char *end_of_record;
00051    struct ast_dns_srv_record *srv;
00052    int host_size;
00053    char host[NI_MAXHOST] = "";
00054 
00055    ptr = dns_find_record(data, size, query->result->answer, query->result->answer_size);
00056    ast_assert(ptr != NULL);
00057 
00058    end_of_record = ptr + size;
00059 
00060    /* PRIORITY */
00061    ptr += dns_parse_short((unsigned char *) ptr, &priority);
00062    if (ptr >= end_of_record) {
00063       return NULL;
00064    }
00065 
00066    /* WEIGHT */
00067    ptr += dns_parse_short((unsigned char *) ptr, &weight);
00068    if (ptr >= end_of_record) {
00069       return NULL;
00070    }
00071 
00072    /* PORT */
00073    ptr += dns_parse_short((unsigned char *) ptr, &port);
00074    if (ptr >= end_of_record) {
00075       return NULL;
00076    }
00077 
00078    host_size = dn_expand((unsigned char *)query->result->answer, (unsigned char *) end_of_record, (unsigned char *) ptr, host, sizeof(host) - 1);
00079    if (host_size < 0) {
00080       ast_log(LOG_ERROR, "Failed to expand domain name: %s\n", strerror(errno));
00081       return NULL;
00082    }
00083 
00084    if (!strcmp(host, ".")) {
00085       return NULL;
00086    }
00087 
00088    srv = ast_calloc(1, sizeof(*srv) + size + host_size + 1);
00089    if (!srv) {
00090       return NULL;
00091    }
00092 
00093    srv->priority = priority;
00094    srv->weight = weight;
00095    srv->port = port;
00096 
00097    srv->host = srv->data + size;
00098    strcpy((char *)srv->host, host); /* SAFE */
00099    ((char *)srv->host)[host_size] = '\0';
00100 
00101    srv->generic.data_ptr = srv->data;
00102 
00103    return (struct ast_dns_record *)srv;
00104 }
00105 
00106 /* This implementation was taken from the existing srv.c which, after reading the RFC, implements it
00107  * as it should.
00108  */
00109 void dns_srv_sort(struct ast_dns_result *result)
00110 {
00111    struct ast_dns_record *current;
00112    struct dns_records newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00113 
00114    while (AST_LIST_FIRST(&result->records)) {
00115       unsigned short cur_priority = 0;
00116       struct dns_records temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00117 
00118       /* Find the lowest current priority to work on */
00119       AST_LIST_TRAVERSE(&result->records, current, list) {
00120          if (!cur_priority || ((struct ast_dns_srv_record *)current)->priority < cur_priority) {
00121             cur_priority = ((struct ast_dns_srv_record *)current)->priority;
00122          }
00123       }
00124 
00125       /* Find all records which match this priority */
00126       AST_LIST_TRAVERSE_SAFE_BEGIN(&result->records, current, list) {
00127          if (((struct ast_dns_srv_record *)current)->priority != cur_priority) {
00128             continue;
00129          }
00130 
00131          AST_LIST_REMOVE_CURRENT(list);
00132 
00133          /* Records with a weight of zero must always be at the head */
00134          if (((struct ast_dns_srv_record *)current)->weight == 0) {
00135             AST_LIST_INSERT_HEAD(&temp_list, current, list);
00136          } else {
00137             AST_LIST_INSERT_TAIL(&temp_list, current, list);
00138          }
00139       }
00140       AST_LIST_TRAVERSE_SAFE_END;
00141 
00142       /* Apply weighting - as each record is passed the sum of all previous weights (plus its own) is stored away, and then a random weight
00143        * is calculated. The first record with a weight sum greater than the random weight is put in the new list and the whole thing starts
00144        * once again.
00145        */
00146       while (AST_LIST_FIRST(&temp_list)) {
00147          unsigned int weight_sum = 0;
00148          unsigned int random_weight;
00149 
00150          AST_LIST_TRAVERSE(&temp_list, current, list) {
00151             ((struct ast_dns_srv_record *)current)->weight_sum = weight_sum += ((struct ast_dns_srv_record *)current)->weight;
00152          }
00153 
00154          /* if all the remaining entries have weight == 0,
00155             then just append them to the result list and quit */
00156          if (weight_sum == 0) {
00157             AST_LIST_APPEND_LIST(&newlist, &temp_list, list);
00158             break;
00159          }
00160 
00161          random_weight = 1 + (unsigned int) ((float) weight_sum * (ast_random() / ((float) RAND_MAX + 1.0)));
00162 
00163          AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) {
00164             if (((struct ast_dns_srv_record *)current)->weight_sum < random_weight) {
00165                continue;
00166             }
00167 
00168             AST_LIST_MOVE_CURRENT(&newlist, list);
00169             break;
00170          }
00171          AST_LIST_TRAVERSE_SAFE_END;
00172       }
00173 
00174    }
00175 
00176    /* now that the new list has been ordered,
00177       put it in place */
00178 
00179    AST_LIST_APPEND_LIST(&result->records, &newlist, list);
00180 }
00181 
00182 const char *ast_dns_srv_get_host(const struct ast_dns_record *record)
00183 {
00184    struct ast_dns_srv_record *srv = (struct ast_dns_srv_record *) record;
00185 
00186    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_srv);
00187    return srv->host;
00188 }
00189 
00190 unsigned short ast_dns_srv_get_priority(const struct ast_dns_record *record)
00191 {
00192    struct ast_dns_srv_record *srv = (struct ast_dns_srv_record *) record;
00193 
00194    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_srv);
00195    return srv->priority;
00196 }
00197 
00198 unsigned short ast_dns_srv_get_weight(const struct ast_dns_record *record)
00199 {
00200    struct ast_dns_srv_record *srv = (struct ast_dns_srv_record *) record;
00201 
00202    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_srv);
00203    return srv->weight;
00204 }
00205 
00206 unsigned short ast_dns_srv_get_port(const struct ast_dns_record *record)
00207 {
00208    struct ast_dns_srv_record *srv = (struct ast_dns_srv_record *) record;
00209 
00210    ast_assert(ast_dns_record_get_rr_type(record) == ns_t_srv);
00211    return srv->port;
00212 }

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