Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
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"
38 #include "asterisk/linkedlists.h"
39 #include "asterisk/dns_internal.h"
40 #include "asterisk/utils.h"
41 
42 struct 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 */
132  AST_LIST_TRAVERSE_SAFE_BEGIN(&result->records, current, list) {
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 
188 const 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 
196 unsigned 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 
204 unsigned 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 
212 unsigned 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 }
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
char data[0]
Additional data.
Definition: dns_internal.h:88
char * data_ptr
pointer to record-specific data.
Definition: dns_internal.h:58
For AST_LIST.
Definition: dns_internal.h:39
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
#define ast_assert(a)
Definition: utils.h:695
#define NULL
Definition: resample.c:96
unsigned short port
The port in the SRV record.
Definition: dns_internal.h:84
static int priority
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
Utility functions.
#define ast_log
Definition: astobj2.c:42
static char host[256]
Definition: muted.c:77
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
An SRV record.
Definition: dns_internal.h:74
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
long int ast_random(void)
Definition: main/utils.c:2064
struct ast_dns_record generic
Generic DNS record information.
Definition: dns_internal.h:76
A set of macros to manage forward-linked lists.
#define AST_LIST_MOVE_CURRENT(newhead, field)
Move the current list entry to another list.
Definition: linkedlists.h:581
char data[0]
The raw DNS record.
Definition: dns_internal.h:60
The result of a DNS query.
Definition: dns_internal.h:117
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
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
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
char weight
struct ast_dns_result * result
Result of the DNS query.
Definition: dns_internal.h:147
int errno
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:710
Internal DNS structure definitions.
A DNS query.
Definition: dns_internal.h:137
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
unsigned short weight
The weight of the SRV record.
Definition: dns_internal.h:82
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
Definition: linkedlists.h:251
const char * host
The hostname in the SRV record.
Definition: dns_internal.h:78
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 ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static PGresult * result
Definition: cel_pgsql.c:88
unsigned int weight_sum
The running weight sum.
Definition: dns_internal.h:86
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
size_t answer_size
The size of the raw DNS answer.
Definition: dns_internal.h:131
DNS SRV Record Parsing API.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
void dns_srv_sort(struct ast_dns_result *result)
Sort the SRV records on a result.
Definition: dns_srv.c:113
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
const char * answer
The raw DNS answer.
Definition: dns_internal.h:129
struct ast_dns_result::dns_records records
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
Core DNS API.
unsigned short priority
The priority of the SRV record.
Definition: dns_internal.h:80
#define AST_LIST_APPEND_LIST(head, list, field)
Appends a whole list to the tail of a list.
Definition: linkedlists.h:782