Wed Jan 8 2020 09:49:51

Asterisk developer's documentation


srv.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Funding provided by nic.at
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20 
21 /*! \file
22  *
23  * \brief DNS SRV Record Lookup Support for Asterisk
24  *
25  * \author Mark Spencer <markster@digium.com>
26  *
27  * \arg See also \ref AstENUM
28  *
29  * \note Funding provided by nic.at
30  */
31 
32 /*** MODULEINFO
33  <support_level>core</support_level>
34  ***/
35 
36 #include "asterisk.h"
37 
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369001 $")
39 
40 #include <netinet/in.h>
41 #include <arpa/nameser.h>
42 #ifdef __APPLE__
43 #if __APPLE_CC__ >= 1495
44 #include <arpa/nameser_compat.h>
45 #endif
46 #endif
47 #include <resolv.h>
48 
49 #include "asterisk/channel.h"
50 #include "asterisk/srv.h"
51 #include "asterisk/dns.h"
52 #include "asterisk/utils.h"
53 #include "asterisk/linkedlists.h"
54 
55 #ifdef __APPLE__
56 #undef T_SRV
57 #define T_SRV 33
58 #endif
59 
60 struct srv_entry {
61  unsigned short priority;
62  unsigned short weight;
63  unsigned short port;
64  unsigned int weight_sum;
66  char host[1];
67 };
68 
69 struct srv_context {
70  unsigned int have_weights:1;
71  struct srv_entry *prev;
72  unsigned int num_records;
74 };
75 
76 static int parse_srv(unsigned char *answer, int len, unsigned char *msg, struct srv_entry **result)
77 {
78  struct srv {
79  unsigned short priority;
80  unsigned short weight;
81  unsigned short port;
82  } __attribute__((__packed__)) *srv = (struct srv *) answer;
83 
84  int res = 0;
85  char repl[256] = "";
86  struct srv_entry *entry;
87 
88  if (len < sizeof(*srv))
89  return -1;
90 
91  answer += sizeof(*srv);
92  len -= sizeof(*srv);
93 
94  if ((res = dn_expand(msg, answer + len, answer, repl, sizeof(repl) - 1)) <= 0) {
95  ast_log(LOG_WARNING, "Failed to expand hostname\n");
96  return -1;
97  }
98 
99  /* the magic value "." for the target domain means that this service
100  is *NOT* available at the domain we searched */
101  if (!strcmp(repl, "."))
102  return -1;
103 
104  if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(repl))))
105  return -1;
106 
107  entry->priority = ntohs(srv->priority);
108  entry->weight = ntohs(srv->weight);
109  entry->port = ntohs(srv->port);
110  strcpy(entry->host, repl);
111 
112  *result = entry;
113 
114  return 0;
115 }
116 
117 static int srv_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
118 {
119  struct srv_context *c = (struct srv_context *) context;
120  struct srv_entry *entry = NULL;
121  struct srv_entry *current;
122 
123  if (parse_srv(answer, len, fullanswer, &entry))
124  return -1;
125 
126  if (entry->weight)
127  c->have_weights = 1;
128 
129  AST_LIST_TRAVERSE_SAFE_BEGIN(&c->entries, current, list) {
130  /* insert this entry just before the first existing
131  entry with a higher priority */
132  if (current->priority <= entry->priority)
133  continue;
134 
135  AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
136  entry = NULL;
137  break;
138  }
140 
141  /* if we didn't find a place to insert the entry before an existing
142  entry, then just add it to the end */
143  if (entry)
144  AST_LIST_INSERT_TAIL(&c->entries, entry, list);
145 
146  return 0;
147 }
148 
149 /* Do the bizarre SRV record weight-handling algorithm
150  involving sorting and random number generation...
151 
152  See RFC 2782 if you want know why this code does this
153 */
154 static void process_weights(struct srv_context *context)
155 {
156  struct srv_entry *current;
157  struct srv_entries newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
158 
159  while (AST_LIST_FIRST(&context->entries)) {
160  unsigned int random_weight;
161  unsigned int weight_sum;
162  unsigned short cur_priority = AST_LIST_FIRST(&context->entries)->priority;
163  struct srv_entries temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
164  weight_sum = 0;
165 
166  AST_LIST_TRAVERSE_SAFE_BEGIN(&context->entries, current, list) {
167  if (current->priority != cur_priority)
168  break;
169 
170  AST_LIST_MOVE_CURRENT(&temp_list, list);
171  }
173 
174  while (AST_LIST_FIRST(&temp_list)) {
175  weight_sum = 0;
176  AST_LIST_TRAVERSE(&temp_list, current, list)
177  current->weight_sum = weight_sum += current->weight;
178 
179  /* if all the remaining entries have weight == 0,
180  then just append them to the result list and quit */
181  if (weight_sum == 0) {
182  AST_LIST_APPEND_LIST(&newlist, &temp_list, list);
183  break;
184  }
185 
186  random_weight = 1 + (unsigned int) ((float) weight_sum * (ast_random() / ((float) RAND_MAX + 1.0)));
187 
188  AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) {
189  if (current->weight < random_weight)
190  continue;
191 
192  AST_LIST_MOVE_CURRENT(&newlist, list);
193  break;
194  }
196  }
197 
198  }
199 
200  /* now that the new list has been ordered,
201  put it in place */
202 
203  AST_LIST_APPEND_LIST(&context->entries, &newlist, list);
204 }
205 
206 int ast_srv_lookup(struct srv_context **context, const char *service, const char **host, unsigned short *port)
207 {
208  struct srv_entry *cur;
209 
210  if (*context == NULL) {
211  if (!(*context = ast_calloc(1, sizeof(struct srv_context)))) {
212  return -1;
213  }
214  AST_LIST_HEAD_INIT_NOLOCK(&(*context)->entries);
215 
216  if ((ast_search_dns(*context, service, C_IN, T_SRV, srv_callback)) < 0) {
217  ast_free(*context);
218  *context = NULL;
219  return -1;
220  }
221 
222  if ((*context)->have_weights) {
223  process_weights(*context);
224  }
225 
226  (*context)->prev = AST_LIST_FIRST(&(*context)->entries);
227  *host = (*context)->prev->host;
228  *port = (*context)->prev->port;
229  AST_LIST_TRAVERSE(&(*context)->entries, cur, list) {
230  ++((*context)->num_records);
231  }
232  return 0;
233  }
234 
235  if (((*context)->prev = AST_LIST_NEXT((*context)->prev, list))) {
236  /* Retrieve next item in result */
237  *host = (*context)->prev->host;
238  *port = (*context)->prev->port;
239  return 0;
240  } else {
241  /* No more results */
242  while ((cur = AST_LIST_REMOVE_HEAD(&(*context)->entries, list))) {
243  ast_free(cur);
244  }
245  ast_free(*context);
246  *context = NULL;
247  return 1;
248  }
249 }
250 
251 void ast_srv_cleanup(struct srv_context **context)
252 {
253  const char *host;
254  unsigned short port;
255 
256  if (*context) {
257  /* We have a context to clean up. */
258  while (!(ast_srv_lookup(context, NULL, &host, &port))) {
259  }
260  }
261 }
262 
263 int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
264 {
265  struct srv_context context = { .entries = AST_LIST_HEAD_NOLOCK_INIT_VALUE };
266  struct srv_entry *current;
267  int ret;
268 
269  if (chan && ast_autoservice_start(chan) < 0) {
270  return -1;
271  }
272 
273  ret = ast_search_dns(&context, service, C_IN, T_SRV, srv_callback);
274 
275  if (context.have_weights) {
276  process_weights(&context);
277  }
278 
279  if (chan) {
280  ret |= ast_autoservice_stop(chan);
281  }
282 
283  /* TODO: there could be a "." entry in the returned list of
284  answers... if so, this requires special handling */
285 
286  /* the list of entries will be sorted in the proper selection order
287  already, so we just need the first one (if any) */
288 
289  if ((ret > 0) && (current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
290  ast_copy_string(host, current->host, hostlen);
291  *port = current->port;
292  ast_free(current);
293  ast_verb(4, "ast_get_srv: SRV lookup for '%s' mapped to host %s, port %d\n",
294  service, host, *port);
295  } else {
296  host[0] = '\0';
297  *port = -1;
298  }
299 
300  while ((current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
301  ast_free(current);
302  }
303 
304  return ret;
305 }
306 
307 unsigned int ast_srv_get_record_count(struct srv_context *context)
308 {
309  return context->num_records;
310 }
311 
312 int ast_srv_get_nth_record(struct srv_context *context, int record_num, const char **host,
313  unsigned short *port, unsigned short *priority, unsigned short *weight)
314 {
315  int i = 1;
316  int res = -1;
317  struct srv_entry *entry;
318 
319  if (record_num < 1 || record_num > context->num_records) {
320  return res;
321  }
322 
323  AST_LIST_TRAVERSE(&context->entries, entry, list) {
324  if (i == record_num) {
325  *host = entry->host;
326  *port = entry->port;
327  *priority = entry->priority;
328  *weight = entry->weight;
329  res = 0;
330  break;
331  }
332  ++i;
333  }
334 
335  return res;
336 }
unsigned int have_weights
Definition: srv.c:70
Main Channel structure associated with a channel.
Definition: channel.h:742
struct srv_entry * prev
Definition: srv.c:71
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
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:179
char host[1]
Definition: srv.c:66
struct srv_entry::@300 list
#define LOG_WARNING
Definition: logger.h:144
static void process_weights(struct srv_context *context)
Definition: srv.c:154
Support for DNS SRV records, used in to locate SIP services.
int ast_search_dns(void *context, const char *dname, int class, int type, int(*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
Perform DNS lookup (used by DNS, enum and SRV lookups)
Definition: dns.c:259
unsigned int weight_sum
Definition: srv.c:64
void ast_srv_cleanup(struct srv_context **context)
Cleanup resources associated with ast_srv_lookup.
Definition: srv.c:251
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:438
enum ast_cc_service_type service
Definition: chan_sip.c:821
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
#define ast_verb(level,...)
Definition: logger.h:243
unsigned short port
Definition: srv.c:63
Utility functions.
struct srv_context::srv_entries entries
int ast_srv_get_nth_record(struct srv_context *context, int record_num, const char **host, unsigned short *port, unsigned short *priority, unsigned short *weight)
Retrieve details from a specific SRV record.
Definition: srv.c:312
int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
Definition: srv.c:263
General Asterisk PBX channel definitions.
long int ast_random(void)
Definition: utils.c:1640
A set of macros to manage forward-linked lists.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
#define AST_LIST_MOVE_CURRENT(newhead, field)
Definition: linkedlists.h:567
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:238
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:224
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
unsigned short priority
Definition: srv.c:61
unsigned int num_records
Definition: srv.c:72
static int parse_srv(unsigned char *answer, int len, unsigned char *msg, struct srv_entry **result)
Definition: srv.c:76
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
Definition: srv.c:60
#define ast_free(a)
Definition: astmm.h:97
unsigned int ast_srv_get_record_count(struct srv_context *context)
Get the number of records for a given SRV context.
Definition: srv.c:307
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
Definition: linkedlists.h:251
int ast_srv_lookup(struct srv_context **context, const char *service, const char **host, unsigned short *port)
Retrieve set of SRV lookups, in order.
Definition: srv.c:206
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:666
#define ast_calloc(a, b)
Definition: astmm.h:82
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:107
static int srv_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Definition: srv.c:117
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:584
DNS support for Asterisk.
unsigned short weight
Definition: srv.c:62
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define AST_LIST_APPEND_LIST(head, list, field)
Appends a whole list to the tail of a list.
Definition: linkedlists.h:768