deb94835fa17ebbfb8375dd4dfc2d6e22819cb26
[asterisk/asterisk.git] / funcs / func_srv.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010 Digium, Inc.
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16
17 /*! \file
18  *
19  * \brief SRV Functions
20  *
21  * \author Mark Michelson <mmichelson@digium.com>
22  *
23  * \ingroup functions
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include "asterisk/module.h"
35 #include "asterisk/srv.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/app.h"
38 #include "asterisk/datastore.h"
39 #include "asterisk/channel.h"
40
41 /*** DOCUMENTATION
42         <function name="SRVQUERY" language="en_US">
43                 <synopsis>
44                         Initiate an SRV query.
45                 </synopsis>
46                 <syntax>
47                         <parameter name="service" required="true">
48                                 <para>The service for which to look up SRV records. An example would be something
49                                 like <literal>_sip._udp.example.com</literal></para>
50                         </parameter>
51                 </syntax>
52                 <description>
53                         <para>This will do an SRV lookup of the given service.</para>
54                 </description>
55         </function>
56         <function name="SRVRESULT" language="en_US">
57                 <synopsis>
58                         Retrieve results from an SRVQUERY.
59                 </synopsis>
60                 <syntax>
61                         <parameter name="id" required="true">
62                                 <para>The identifier returned by the SRVQUERY function.</para>
63                         </parameter>
64                         <parameter name="resultnum" required="true">
65                                 <para>The number of the result that you want to retrieve.</para>
66                                 <para>Results start at <literal>1</literal>. If this argument is specified
67                                 as <literal>getnum</literal>, then it will return the total number of results
68                                 that are available.</para>
69                         </parameter>
70                 </syntax>
71                 <description>
72                         <para>This function will retrieve results from a previous use
73                         of the SRVQUERY function.</para>
74                 </description>
75         </function>
76  ***/
77
78 struct srv_result_datastore {
79         struct srv_context *context;
80         char id[1];
81 };
82
83 static void srds_destroy_cb(void *data)
84 {
85         struct srv_result_datastore *datastore = data;
86         ast_srv_cleanup(&datastore->context);
87         ast_free(datastore);
88 }
89
90 static const struct ast_datastore_info srv_result_datastore_info = {
91         .type = "SRVQUERY",
92         .destroy = srds_destroy_cb,
93 };
94
95 static struct srv_context *srv_datastore_setup(const char *service, struct ast_channel *chan)
96 {
97         struct srv_result_datastore *srds;
98         struct ast_datastore *datastore;
99         const char *host;
100         unsigned short port;
101
102         if (!(srds = ast_calloc(1, sizeof(*srds) + strlen(service)))) {
103                 return NULL;
104         }
105
106         ast_autoservice_start(chan);
107         if (ast_srv_lookup(&srds->context, service, &host, &port) < 0) {
108                 ast_autoservice_stop(chan);
109                 ast_log(LOG_NOTICE, "Error performing lookup of service '%s'\n", service);
110                 ast_free(srds);
111                 return NULL;
112         }
113         ast_autoservice_stop(chan);
114
115         strcpy(srds->id, service);
116
117         if (!(datastore = ast_datastore_alloc(&srv_result_datastore_info, srds->id))) {
118                 ast_srv_cleanup(&srds->context);
119                 ast_free(srds);
120                 return NULL;
121         }
122
123         datastore->data = srds;
124         ast_channel_lock(chan);
125         ast_channel_datastore_add(chan, datastore);
126         ast_channel_unlock(chan);
127         return srds->context;
128 }
129
130 static int srv_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
131 {
132         struct ast_datastore *datastore;
133
134         if (!chan) {
135                 ast_log(LOG_WARNING, "%s cannot be used without a channel\n", cmd);
136                 return -1;
137         }
138
139         if (ast_strlen_zero(data)) {
140                 ast_log(LOG_WARNING, "%s requires a service as an argument\n", cmd);
141                 return -1;
142         }
143         
144         /* If they already called SRVQUERY for this service once,
145          * we need to kill the old datastore.
146          */
147         ast_channel_lock(chan);
148         datastore = ast_channel_datastore_find(chan, &srv_result_datastore_info, data);
149         ast_channel_unlock(chan);
150
151         if (datastore) {
152                 ast_channel_datastore_remove(chan, datastore);
153                 ast_datastore_free(datastore);
154         }
155         
156         if (!srv_datastore_setup(data, chan)) {
157                 return -1;
158         }
159
160         ast_copy_string(buf, data, len);
161
162         return 0;
163 }
164
165 static struct ast_custom_function srv_query_function = {
166         .name = "SRVQUERY",
167         .read = srv_query_read,
168 };
169
170 static int srv_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
171 {
172         struct srv_result_datastore *srds;
173         struct ast_datastore *datastore;
174         struct srv_context *srv_context;
175         char *parse;
176         const char *host;
177         unsigned short port, priority, weight;
178         unsigned int num;
179         AST_DECLARE_APP_ARGS(args,
180                 AST_APP_ARG(id);
181                 AST_APP_ARG(resultnum);
182                 AST_APP_ARG(field);
183         );
184
185         if (!chan) {
186                 ast_log(LOG_WARNING, "%s cannot be used without a channel\n", cmd);
187                 return -1;
188         }
189
190         if (ast_strlen_zero(data)) {
191                 ast_log(LOG_WARNING, "%s requires two arguments (id and resultnum)\n", cmd);
192                 return -1;
193         }
194
195         parse = ast_strdupa(data);
196
197         AST_STANDARD_APP_ARGS(args, parse);
198
199         ast_channel_lock(chan);
200         datastore = ast_channel_datastore_find(chan, &srv_result_datastore_info, args.id);
201         ast_channel_unlock(chan);
202
203         if (!datastore) {
204                 /* They apparently decided to call SRVRESULT without first calling SRVQUERY.
205                  * No problem, we'll do the SRV lookup now.
206                  */
207                 srv_context = srv_datastore_setup(args.id, chan);
208                 if (!srv_context) {
209                         return -1;
210                 }
211         } else {
212                 srds = datastore->data;
213                 srv_context = srds->context;
214         }
215
216         if (!strcasecmp(args.resultnum, "getnum")) {
217                 snprintf(buf, len, "%u", ast_srv_get_record_count(srv_context));
218                 return 0;
219         }
220
221         if (ast_strlen_zero(args.field)) {
222                 ast_log(LOG_ERROR, "A field must be provided when requesting SRV data\n");
223                 return -1;
224         }
225
226         if (sscanf(args.resultnum, "%30u", &num) != 1) {
227                 ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to %s\n", args.resultnum, cmd);
228                 return -1;
229         }
230
231         if (ast_srv_get_nth_record(srv_context, num, &host, &port, &priority, &weight)) {
232                 ast_log(LOG_ERROR, "Failed to get record number %u for %s\n", num, cmd);
233                 return -1;
234         }
235
236         if (!strcasecmp(args.field, "host")) {
237                 ast_copy_string(buf, host, len);
238         } else if (!strcasecmp(args.field, "port")) {
239                 snprintf(buf, len, "%u", port);
240         } else if (!strcasecmp(args.field, "priority")) {
241                 snprintf(buf, len, "%u", priority);
242         } else if (!strcasecmp(args.field, "weight")) {
243                 snprintf(buf, len, "%u", weight);
244         } else {
245                 ast_log(LOG_WARNING, "Unrecognized SRV field '%s'\n", args.field);
246                 return -1;
247         }
248
249         return 0;
250 }
251
252 static struct ast_custom_function srv_result_function = {
253         .name = "SRVRESULT",
254         .read = srv_result_read,
255 };
256
257 static int unload_module(void)
258 {
259         int res = 0;
260
261         res |= ast_custom_function_unregister(&srv_query_function);
262         res |= ast_custom_function_unregister(&srv_result_function);
263
264         return res;
265 }
266
267 static int load_module(void)
268 {
269         int res = ast_custom_function_register(&srv_query_function);
270         if (res < 0) {
271                 return AST_MODULE_LOAD_DECLINE;
272         }
273         res = ast_custom_function_register(&srv_result_function);
274         if (res < 0) {
275                 return AST_MODULE_LOAD_DECLINE;
276         }
277
278         return AST_MODULE_LOAD_SUCCESS;;
279 }
280
281 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SRV related dialplan functions");