Asterisk - The Open Source Telephony Project GIT-master-7e7a603
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 ast_extension_states "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#include <netinet/in.h>
39#include <arpa/nameser.h>
40#ifdef __APPLE__
41#include <arpa/nameser_compat.h>
42#endif
43#include <resolv.h>
44
45#include "asterisk/channel.h"
46#include "asterisk/srv.h"
47#include "asterisk/dns.h"
48#include "asterisk/utils.h"
50
51#ifdef __APPLE__
52#undef T_SRV
53#define T_SRV 33
54#endif
55
56struct srv_entry {
57 unsigned short priority;
58 unsigned short weight;
59 unsigned short port;
60 unsigned int weight_sum;
62 char host[1];
63};
64
66 unsigned int have_weights:1;
67 struct srv_entry *prev;
68 unsigned int num_records;
70};
71
72static int parse_srv(unsigned char *answer, int len, unsigned char *msg, struct srv_entry **result)
73{
74 struct srv {
75 unsigned short priority;
76 unsigned short weight;
77 unsigned short port;
78 } __attribute__((__packed__)) *srv = (struct srv *) answer;
79
80 int res = 0;
81 char repl[256] = "";
82 struct srv_entry *entry;
83
84 if (len < sizeof(*srv))
85 return -1;
86
87 answer += sizeof(*srv);
88 len -= sizeof(*srv);
89
90 if ((res = dn_expand(msg, answer + len, answer, repl, sizeof(repl) - 1)) <= 0) {
91 ast_log(LOG_WARNING, "Failed to expand hostname\n");
92 return -1;
93 }
94
95 /* the magic value "." for the target domain means that this service
96 is *NOT* available at the domain we searched */
97 if (!strcmp(repl, "."))
98 return -1;
99
100 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(repl))))
101 return -1;
102
103 entry->priority = ntohs(srv->priority);
104 entry->weight = ntohs(srv->weight);
105 entry->port = ntohs(srv->port);
106 strcpy(entry->host, repl);
107
108 *result = entry;
109
110 return 0;
111}
112
113static int srv_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
114{
115 struct srv_context *c = (struct srv_context *) context;
116 struct srv_entry *entry = NULL;
117 struct srv_entry *current;
118
119 if (parse_srv(answer, len, fullanswer, &entry))
120 return -1;
121
122 if (entry->weight)
123 c->have_weights = 1;
124
126 /* insert this entry just before the first existing
127 entry with a higher priority */
128 if (current->priority <= entry->priority)
129 continue;
130
132 entry = NULL;
133 break;
134 }
136
137 /* if we didn't find a place to insert the entry before an existing
138 entry, then just add it to the end */
139 if (entry)
140 AST_LIST_INSERT_TAIL(&c->entries, entry, list);
141
142 return 0;
143}
144
145/* Do the bizarre SRV record weight-handling algorithm
146 involving sorting and random number generation...
147
148 See RFC 2782 if you want know why this code does this
149*/
151{
152 struct srv_entry *current;
153 struct srv_entries newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
154
155 while (AST_LIST_FIRST(&context->entries)) {
156 unsigned int random_weight;
157 unsigned int weight_sum;
158 unsigned short cur_priority = AST_LIST_FIRST(&context->entries)->priority;
159 struct srv_entries temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
160 weight_sum = 0;
161
163 if (current->priority != cur_priority)
164 break;
165
166 AST_LIST_MOVE_CURRENT(&temp_list, list);
167 }
169
170 while (AST_LIST_FIRST(&temp_list)) {
171 weight_sum = 0;
172 AST_LIST_TRAVERSE(&temp_list, current, list)
173 current->weight_sum = weight_sum += current->weight;
174
175 /* if all the remaining entries have weight == 0,
176 then just append them to the result list and quit */
177 if (weight_sum == 0) {
178 AST_LIST_APPEND_LIST(&newlist, &temp_list, list);
179 break;
180 }
181
182 random_weight = 1 + (unsigned int) ((float) weight_sum * (ast_random() / ((float) RAND_MAX + 1.0)));
183
184 AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) {
185 if (current->weight < random_weight)
186 continue;
187
188 AST_LIST_MOVE_CURRENT(&newlist, list);
189 break;
190 }
192 }
193
194 }
195
196 /* now that the new list has been ordered,
197 put it in place */
198
199 AST_LIST_APPEND_LIST(&context->entries, &newlist, list);
200}
201
202int ast_srv_lookup(struct srv_context **context, const char *service, const char **host, unsigned short *port)
203{
204 struct srv_entry *cur;
205
206 if (*context == NULL) {
207 if (!(*context = ast_calloc(1, sizeof(struct srv_context)))) {
208 return -1;
209 }
210 AST_LIST_HEAD_INIT_NOLOCK(&(*context)->entries);
211
212 if (((ast_search_dns(*context, service, C_IN, T_SRV, srv_callback)) < 1) ||
213 AST_LIST_EMPTY(&(*context)->entries)) {
215 *context = NULL;
216 return -1;
217 }
218
219 if ((*context)->have_weights) {
221 }
222
223 (*context)->prev = AST_LIST_FIRST(&(*context)->entries);
224 *host = (*context)->prev->host;
225 *port = (*context)->prev->port;
226 AST_LIST_TRAVERSE(&(*context)->entries, cur, list) {
227 ++((*context)->num_records);
228 }
229 return 0;
230 }
231
232 if (((*context)->prev = AST_LIST_NEXT((*context)->prev, list))) {
233 /* Retrieve next item in result */
234 *host = (*context)->prev->host;
235 *port = (*context)->prev->port;
236 return 0;
237 } else {
238 /* No more results */
239 while ((cur = AST_LIST_REMOVE_HEAD(&(*context)->entries, list))) {
240 ast_free(cur);
241 }
243 *context = NULL;
244 return 1;
245 }
246}
247
249{
250 const char *host;
251 unsigned short port;
252
253 if (*context) {
254 /* We have a context to clean up. */
255 while (!(ast_srv_lookup(context, NULL, &host, &port))) {
256 }
257 }
258}
259
260int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
261{
263 struct srv_entry *current;
264 int ret;
265
266 if (chan && ast_autoservice_start(chan) < 0) {
267 return -1;
268 }
269
270 ret = ast_search_dns(&context, service, C_IN, T_SRV, srv_callback);
271
272 if (context.have_weights) {
274 }
275
276 if (chan) {
277 ret |= ast_autoservice_stop(chan);
278 }
279
280 /* TODO: there could be a "." entry in the returned list of
281 answers... if so, this requires special handling */
282
283 /* the list of entries will be sorted in the proper selection order
284 already, so we just need the first one (if any) */
285
286 if ((ret > 0) && (current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
287 ast_copy_string(host, current->host, hostlen);
288 *port = current->port;
290 ast_debug(4, "ast_get_srv: SRV lookup for '%s' mapped to host %s, port %d\n",
291 service, host, *port);
292 } else {
293 host[0] = '\0';
294 *port = -1;
295 }
296
297 while ((current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
299 }
300
301 return ret;
302}
303
305{
306 return context->num_records;
307}
308
309int ast_srv_get_nth_record(struct srv_context *context, int record_num, const char **host,
310 unsigned short *port, unsigned short *priority, unsigned short *weight)
311{
312 int i = 1;
313 int res = -1;
314 struct srv_entry *entry;
315
316 if (record_num < 1 || record_num > context->num_records) {
317 return res;
318 }
319
320 AST_LIST_TRAVERSE(&context->entries, entry, list) {
321 if (i == record_num) {
322 *host = entry->host;
323 *port = entry->port;
324 *priority = entry->priority;
325 *weight = entry->weight;
326 res = 0;
327 break;
328 }
329 ++i;
330 }
331
332 return res;
333}
char weight
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
enum ast_cc_service_type service
Definition: ccss.c:383
static int priority
static PGresult * result
Definition: cel_pgsql.c:84
static int answer(void *data)
Definition: chan_pjsip.c:683
General Asterisk PBX channel definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
DNS support for Asterisk.
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:491
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_WARNING
A set of macros to manage forward-linked lists.
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
#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:225
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#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_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
#define AST_LIST_MOVE_CURRENT(newhead, field)
Move the current list entry to another list.
Definition: linkedlists.h:582
#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_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#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
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
size_t current
Definition: main/cli.c:113
#define NULL
Definition: resample.c:96
unsigned int ast_srv_get_record_count(struct srv_context *context)
Get the number of records for a given SRV context.
Definition: srv.c:304
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:309
static int parse_srv(unsigned char *answer, int len, unsigned char *msg, struct srv_entry **result)
Definition: srv.c:72
static void process_weights(struct srv_context *context)
Definition: srv.c:150
void ast_srv_cleanup(struct srv_context **context)
Cleanup resources associated with ast_srv_lookup.
Definition: srv.c:248
static int srv_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Definition: srv.c:113
int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
Lookup entry in SRV records Returns 1 if found, 0 if not found, -1 on hangup.
Definition: srv.c:260
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:202
Support for DNS SRV records, used in to locate SIP services.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Main Channel structure associated with a channel.
Definition: search.h:40
unsigned int have_weights
Definition: srv.c:66
unsigned int num_records
Definition: srv.c:68
struct srv_entry * prev
Definition: srv.c:67
struct srv_context::srv_entries entries
Definition: srv.c:56
struct srv_entry::@391 list
unsigned short weight
Definition: srv.c:58
unsigned short priority
Definition: srv.c:57
unsigned int weight_sum
Definition: srv.c:60
char host[1]
Definition: srv.c:62
unsigned short port
Definition: srv.c:59
static struct test_val c
Utility functions.
long int ast_random(void)
Definition: utils.c:2312