CI: Various updates to buildAsterisk.sh
[asterisk/asterisk.git] / main / dns_query_set.c
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 Query Set API
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 "asterisk/vector.h"
33 #include "asterisk/astobj2.h"
34 #include "asterisk/utils.h"
35 #include "asterisk/linkedlists.h"
36 #include "asterisk/dns_core.h"
37 #include "asterisk/dns_query_set.h"
38 #include "asterisk/dns_internal.h"
39 #include "asterisk/dns_resolver.h"
40
41 /*! \brief The default number of expected queries to be added to the query set */
42 #define DNS_QUERY_SET_EXPECTED_QUERY_COUNT 5
43
44 /*! \brief Destructor for DNS query set */
45 static void dns_query_set_destroy(void *data)
46 {
47         struct ast_dns_query_set *query_set = data;
48         int idx;
49
50         for (idx = 0; idx < AST_VECTOR_SIZE(&query_set->queries); ++idx) {
51                 struct dns_query_set_query *query = AST_VECTOR_GET_ADDR(&query_set->queries, idx);
52
53                 ao2_ref(query->query, -1);
54         }
55         AST_VECTOR_FREE(&query_set->queries);
56
57         ao2_cleanup(query_set->user_data);
58 }
59
60 struct ast_dns_query_set *ast_dns_query_set_create(void)
61 {
62         struct ast_dns_query_set *query_set;
63
64         query_set = ao2_alloc_options(sizeof(*query_set), dns_query_set_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
65         if (!query_set) {
66                 return NULL;
67         }
68
69         if (AST_VECTOR_INIT(&query_set->queries, DNS_QUERY_SET_EXPECTED_QUERY_COUNT)) {
70                 ao2_ref(query_set, -1);
71                 return NULL;
72         }
73
74         return query_set;
75 }
76
77 /*! \brief Callback invoked upon completion of a DNS query */
78 static void dns_query_set_callback(const struct ast_dns_query *query)
79 {
80         struct ast_dns_query_set *query_set = ast_dns_query_get_data(query);
81
82         /* The reference count of the query set is bumped here in case this query holds the last reference */
83         ao2_ref(query_set, +1);
84
85         /* Drop the query set from the query so the query set can be destroyed if this is the last one */
86         ao2_cleanup(((struct ast_dns_query *)query)->user_data);
87         ((struct ast_dns_query *)query)->user_data = NULL;
88
89         if (ast_atomic_fetchadd_int(&query_set->queries_completed, +1) != (AST_VECTOR_SIZE(&query_set->queries) - 1)) {
90                 ao2_ref(query_set, -1);
91                 return;
92         }
93
94         /* All queries have been completed, invoke final callback */
95         if (query_set->queries_cancelled != AST_VECTOR_SIZE(&query_set->queries)) {
96                 query_set->callback(query_set);
97         }
98
99         ao2_cleanup(query_set->user_data);
100         query_set->user_data = NULL;
101
102         ao2_ref(query_set, -1);
103 }
104
105 int ast_dns_query_set_add(struct ast_dns_query_set *query_set, const char *name, int rr_type, int rr_class)
106 {
107         struct dns_query_set_query query = {
108                 .started = 0,
109         };
110
111         ast_assert(!query_set->in_progress);
112         if (query_set->in_progress) {
113                 ast_log(LOG_ERROR, "Attempted to add additional query to query set '%p' after resolution has started\n",
114                         query_set);
115                 return -1;
116         }
117
118         /*
119          * We are intentionally passing NULL for the user data even
120          * though dns_query_set_callback() is not NULL tolerant.  Doing
121          * this avoids a circular reference chain until the queries are
122          * started.  ast_dns_query_set_resolve_async() will set the
123          * query user_data for us later when we actually kick off the
124          * queries.
125          */
126         query.query = dns_query_alloc(name, rr_type, rr_class, dns_query_set_callback, NULL);
127         if (!query.query) {
128                 return -1;
129         }
130
131         if (AST_VECTOR_APPEND(&query_set->queries, query)) {
132                 ao2_ref(query.query, -1);
133                 return -1;
134         }
135
136         return 0;
137 }
138
139 size_t ast_dns_query_set_num_queries(const struct ast_dns_query_set *query_set)
140 {
141         return AST_VECTOR_SIZE(&query_set->queries);
142 }
143
144 struct ast_dns_query *ast_dns_query_set_get(const struct ast_dns_query_set *query_set, unsigned int index)
145 {
146         /* Only once all queries have been completed can results be retrieved */
147         if (query_set->queries_completed != AST_VECTOR_SIZE(&query_set->queries)) {
148                 return NULL;
149         }
150
151         /* If the index exceeds the number of queries... no query for you */
152         if (index >= AST_VECTOR_SIZE(&query_set->queries)) {
153                 return NULL;
154         }
155
156         return AST_VECTOR_GET_ADDR(&query_set->queries, index)->query;
157 }
158
159 void *ast_dns_query_set_get_data(const struct ast_dns_query_set *query_set)
160 {
161         return query_set->user_data;
162 }
163
164 void ast_dns_query_set_resolve_async(struct ast_dns_query_set *query_set, ast_dns_query_set_callback callback, void *data)
165 {
166         int idx;
167
168         ast_assert(!query_set->in_progress);
169         if (query_set->in_progress) {
170                 ast_log(LOG_ERROR, "Attempted to start asynchronous resolution of query set '%p' when it has already started\n",
171                         query_set);
172                 return;
173         }
174
175         query_set->in_progress = 1;
176         query_set->callback = callback;
177         query_set->user_data = ao2_bump(data);
178
179         /*
180          * Bump the query_set ref in case all queries complete
181          * before we are done kicking them off.
182          */
183         ao2_ref(query_set, +1);
184         for (idx = 0; idx < AST_VECTOR_SIZE(&query_set->queries); ++idx) {
185                 struct dns_query_set_query *query = AST_VECTOR_GET_ADDR(&query_set->queries, idx);
186
187                 query->query->user_data = ao2_bump(query_set);
188
189                 if (!query->query->resolver->resolve(query->query)) {
190                         query->started = 1;
191                         continue;
192                 }
193
194                 dns_query_set_callback(query->query);
195         }
196         if (!idx) {
197                 /*
198                  * There were no queries in the set;
199                  * therefore all queries are "completed".
200                  * Invoke the final callback.
201                  */
202                 query_set->callback(query_set);
203                 ao2_cleanup(query_set->user_data);
204                 query_set->user_data = NULL;
205         }
206         ao2_ref(query_set, -1);
207 }
208
209 /*! \brief Structure used for signaling back for synchronous resolution completion */
210 struct dns_synchronous_resolve {
211         /*! \brief Lock used for signaling */
212         ast_mutex_t lock;
213         /*! \brief Condition used for signaling */
214         ast_cond_t cond;
215         /*! \brief Whether the query has completed */
216         unsigned int completed;
217 };
218
219 /*! \brief Destructor for synchronous resolution structure */
220 static void dns_synchronous_resolve_destroy(void *data)
221 {
222         struct dns_synchronous_resolve *synchronous = data;
223
224         ast_mutex_destroy(&synchronous->lock);
225         ast_cond_destroy(&synchronous->cond);
226 }
227
228 /*! \brief Callback used to implement synchronous resolution */
229 static void dns_synchronous_resolve_callback(const struct ast_dns_query_set *query_set)
230 {
231         struct dns_synchronous_resolve *synchronous = ast_dns_query_set_get_data(query_set);
232
233         ast_mutex_lock(&synchronous->lock);
234         synchronous->completed = 1;
235         ast_cond_signal(&synchronous->cond);
236         ast_mutex_unlock(&synchronous->lock);
237 }
238
239 int ast_query_set_resolve(struct ast_dns_query_set *query_set)
240 {
241         struct dns_synchronous_resolve *synchronous;
242
243         synchronous = ao2_alloc_options(sizeof(*synchronous), dns_synchronous_resolve_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
244         if (!synchronous) {
245                 return -1;
246         }
247
248         ast_mutex_init(&synchronous->lock);
249         ast_cond_init(&synchronous->cond, NULL);
250
251         ast_dns_query_set_resolve_async(query_set, dns_synchronous_resolve_callback, synchronous);
252
253         /* Wait for resolution to complete */
254         ast_mutex_lock(&synchronous->lock);
255         while (!synchronous->completed) {
256                 ast_cond_wait(&synchronous->cond, &synchronous->lock);
257         }
258         ast_mutex_unlock(&synchronous->lock);
259
260         ao2_ref(synchronous, -1);
261
262         return 0;
263 }
264
265 int ast_dns_query_set_resolve_cancel(struct ast_dns_query_set *query_set)
266 {
267         int idx;
268         size_t query_count = AST_VECTOR_SIZE(&query_set->queries);
269
270         for (idx = 0; idx < AST_VECTOR_SIZE(&query_set->queries); ++idx) {
271                 struct dns_query_set_query *query = AST_VECTOR_GET_ADDR(&query_set->queries, idx);
272
273                 if (query->started) {
274                         if (!query->query->resolver->cancel(query->query)) {
275                                 query_set->queries_cancelled++;
276                                 dns_query_set_callback(query->query);
277                         }
278                 } else {
279                         query_set->queries_cancelled++;
280                 }
281         }
282
283         return (query_set->queries_cancelled == query_count) ? 0 : -1;
284 }