Asterisk - The Open Source Telephony Project GIT-master-a358458
dns_srv.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2015, Digium, Inc.
5 *
6 * Joshua Colp <jcolp@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
19/*! \file
20 *
21 * \brief DNS SRV Record Support
22 *
23 * \author Joshua Colp <jcolp@digium.com>
24 */
25
26/*** MODULEINFO
27 <support_level>core</support_level>
28 ***/
29
30#include "asterisk.h"
31
32#include <netinet/in.h>
33#include <arpa/nameser.h>
34#include <resolv.h>
35
36#include "asterisk/dns_core.h"
37#include "asterisk/dns_srv.h"
40#include "asterisk/utils.h"
41
42struct ast_dns_record *dns_srv_alloc(struct ast_dns_query *query, const char *data, const size_t size)
43{
44 uint16_t priority;
45 uint16_t weight;
46 uint16_t port;
47 const char *ptr;
48 const char *end_of_record;
49 struct ast_dns_srv_record *srv;
50 int host_size;
51 char host[NI_MAXHOST] = "";
52 size_t host_len;
53
54 ptr = dns_find_record(data, size, query->result->answer, query->result->answer_size);
55 ast_assert(ptr != NULL);
56
57 end_of_record = ptr + size;
58
59 /* PRIORITY */
60 ptr += dns_parse_short((unsigned char *) ptr, &priority);
61 if (ptr >= end_of_record) {
62 return NULL;
63 }
64
65 /* WEIGHT */
66 ptr += dns_parse_short((unsigned char *) ptr, &weight);
67 if (ptr >= end_of_record) {
68 return NULL;
69 }
70
71 /* PORT */
72 ptr += dns_parse_short((unsigned char *) ptr, &port);
73 if (ptr >= end_of_record) {
74 return NULL;
75 }
76
77 /*
78 * The return value from dn_expand represents the size of the replacement
79 * in the buffer which MAY be compressed. Since the expanded replacement
80 * is NULL terminated, you can use strlen() to get the expanded size.
81 */
82 host_size = dn_expand((unsigned char *)query->result->answer,
83 (unsigned char *) end_of_record, (unsigned char *) ptr, host, sizeof(host) - 1);
84 if (host_size < 0) {
85 ast_log(LOG_ERROR, "Failed to expand domain name: %s\n", strerror(errno));
86 return NULL;
87 }
88
89 if (!strcmp(host, ".")) {
90 return NULL;
91 }
92
93 host_len = strlen(host) + 1;
94 srv = ast_calloc(1, sizeof(*srv) + size + host_len);
95 if (!srv) {
96 return NULL;
97 }
98
99 srv->priority = priority;
100 srv->weight = weight;
101 srv->port = port;
102
103 srv->host = srv->data + size;
104 ast_copy_string((char *)srv->host, host, host_len); /* SAFE */
105 srv->generic.data_ptr = srv->data;
106
107 return (struct ast_dns_record *)srv;
108}
109
110/* This implementation was taken from the existing srv.c which, after reading the RFC, implements it
111 * as it should.
112 */
114{
115 struct ast_dns_record *current;
116 struct dns_records newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
117
118 while (AST_LIST_FIRST(&result->records)) {
119 unsigned short cur_priority = ((struct ast_dns_srv_record *)(AST_LIST_FIRST(&result->records)))->priority;
120 struct dns_records temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
121
122 /* Find the lowest current priority to work on, but if the priority is already zero there is no lower priority */
123 if (cur_priority) {
124 AST_LIST_TRAVERSE(&result->records, current, list) {
125 if (((struct ast_dns_srv_record *)current)->priority < cur_priority) {
126 cur_priority = ((struct ast_dns_srv_record *)current)->priority;
127 }
128 }
129 }
130
131 /* Find all records which match this priority */
133 if (((struct ast_dns_srv_record *)current)->priority != cur_priority) {
134 continue;
135 }
136
138
139 /* Records with a weight of zero must always be at the head */
140 if (((struct ast_dns_srv_record *)current)->weight == 0) {
141 AST_LIST_INSERT_HEAD(&temp_list, current, list);
142 } else {
143 AST_LIST_INSERT_TAIL(&temp_list, current, list);
144 }
145 }
147
148 /* Apply weighting - as each record is passed the sum of all previous weights (plus its own) is stored away, and then a random weight
149 * 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
150 * once again.
151 */
152 while (AST_LIST_FIRST(&temp_list)) {
153 unsigned int weight_sum = 0;
154 unsigned int random_weight;
155
156 AST_LIST_TRAVERSE(&temp_list, current, list) {
157 ((struct ast_dns_srv_record *)current)->weight_sum = weight_sum += ((struct ast_dns_srv_record *)current)->weight;
158 }
159
160 /* if all the remaining entries have weight == 0,
161 then just append them to the result list and quit */
162 if (weight_sum == 0) {
163 AST_LIST_APPEND_LIST(&newlist, &temp_list, list);
164 break;
165 }
166
167 random_weight = 1 + (unsigned int) ((float) weight_sum * (ast_random() / ((float) RAND_MAX + 1.0)));
168
169 AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) {
170 if (((struct ast_dns_srv_record *)current)->weight_sum < random_weight) {
171 continue;
172 }
173
174 AST_LIST_MOVE_CURRENT(&newlist, list);
175 break;
176 }
178 }
179
180 }
181
182 /* now that the new list has been ordered,
183 put it in place */
184
185 AST_LIST_APPEND_LIST(&result->records, &newlist, list);
186}
187
188const char *ast_dns_srv_get_host(const struct ast_dns_record *record)
189{
190 struct ast_dns_srv_record *srv = (struct ast_dns_srv_record *) record;
191
192 ast_assert(ast_dns_record_get_rr_type(record) == T_SRV);
193 return srv->host;
194}
195
196unsigned short ast_dns_srv_get_priority(const struct ast_dns_record *record)
197{
198 struct ast_dns_srv_record *srv = (struct ast_dns_srv_record *) record;
199
200 ast_assert(ast_dns_record_get_rr_type(record) == T_SRV);
201 return srv->priority;
202}
203
204unsigned short ast_dns_srv_get_weight(const struct ast_dns_record *record)
205{
206 struct ast_dns_srv_record *srv = (struct ast_dns_srv_record *) record;
207
208 ast_assert(ast_dns_record_get_rr_type(record) == T_SRV);
209 return srv->weight;
210}
211
212unsigned short ast_dns_srv_get_port(const struct ast_dns_record *record)
213{
214 struct ast_dns_srv_record *srv = (struct ast_dns_srv_record *) record;
215
216 ast_assert(ast_dns_record_get_rr_type(record) == T_SRV);
217 return srv->port;
218}
char weight
Asterisk main include file. File version handling, generic pbx functions.
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
static int priority
static PGresult * result
Definition: cel_pgsql.c:84
Core DNS API.
int ast_dns_record_get_rr_type(const struct ast_dns_record *record)
Get the resource record type of a DNS record.
Definition: dns_core.c:145
Internal DNS structure definitions.
char * dns_find_record(const char *record, size_t record_size, const char *response, size_t response_size)
Find the location of a DNS record within the entire DNS answer.
Definition: dns_core.c:701
int dns_parse_short(unsigned char *cur, uint16_t *val)
Parse a 16-bit unsigned value from a DNS record.
Definition: dns_core.c:722
const char * ast_dns_srv_get_host(const struct ast_dns_record *record)
Get the hostname from an SRV record.
Definition: dns_srv.c:188
unsigned short ast_dns_srv_get_priority(const struct ast_dns_record *record)
Get the priority from an SRV record.
Definition: dns_srv.c:196
unsigned short ast_dns_srv_get_weight(const struct ast_dns_record *record)
Get the weight from an SRV record.
Definition: dns_srv.c:204
struct ast_dns_record * dns_srv_alloc(struct ast_dns_query *query, const char *data, const size_t size)
Allocate and parse a DNS SRV record.
Definition: dns_srv.c:42
void dns_srv_sort(struct ast_dns_result *result)
Sort the SRV records on a result.
Definition: dns_srv.c:113
unsigned short ast_dns_srv_get_port(const struct ast_dns_record *record)
Get the port from an SRV record.
Definition: dns_srv.c:212
DNS SRV Record Parsing API.
#define LOG_ERROR
A set of macros to manage forward-linked lists.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
Definition: linkedlists.h:252
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_MOVE_CURRENT(newhead, field)
Move the current list entry to another list.
Definition: linkedlists.h:582
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_LIST_APPEND_LIST(head, list, field)
Appends a whole list to the tail of a list.
Definition: linkedlists.h:783
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
size_t current
Definition: main/cli.c:113
int errno
#define NULL
Definition: resample.c:96
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
A DNS query.
Definition: dns_internal.h:137
struct ast_dns_result * result
Result of the DNS query.
Definition: dns_internal.h:147
For AST_LIST.
Definition: dns_internal.h:39
char data[0]
The raw DNS record.
Definition: dns_internal.h:60
char * data_ptr
pointer to record-specific data.
Definition: dns_internal.h:58
The result of a DNS query.
Definition: dns_internal.h:117
size_t answer_size
The size of the raw DNS answer.
Definition: dns_internal.h:131
const char * answer
The raw DNS answer.
Definition: dns_internal.h:129
An SRV record.
Definition: dns_internal.h:74
struct ast_dns_record generic
Generic DNS record information.
Definition: dns_internal.h:76
unsigned short weight
The weight of the SRV record.
Definition: dns_internal.h:82
unsigned short priority
The priority of the SRV record.
Definition: dns_internal.h:80
unsigned int weight_sum
The running weight sum.
Definition: dns_internal.h:86
char data[0]
Additional data.
Definition: dns_internal.h:88
unsigned short port
The port in the SRV record.
Definition: dns_internal.h:84
const char * host
The hostname in the SRV record.
Definition: dns_internal.h:78
Utility functions.
#define ast_assert(a)
Definition: utils.h:739
long int ast_random(void)
Definition: utils.c:2312