| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2010-04-13 16:15:36 +00:00
										 |  |  |  * Copyright (C) 1999 - 2010 Digium, Inc. | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * See http://www.asterisk.org for more information about
 | 
					
						
							|  |  |  |  * the Asterisk project. Please do not directly contact | 
					
						
							|  |  |  |  * any of the maintainers of this project for assistance; | 
					
						
							|  |  |  |  * the project provides a web site, mailing lists and IRC | 
					
						
							|  |  |  |  * channels for your use. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software, distributed under the terms of | 
					
						
							|  |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \file
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \brief SRV Functions | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \author Mark Michelson <mmichelson@digium.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \ingroup functions | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-14 20:28:54 +00:00
										 |  |  | /*** MODULEINFO
 | 
					
						
							|  |  |  | 	<support_level>core</support_level> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/srv.h"
 | 
					
						
							|  |  |  | #include "asterisk/pbx.h"
 | 
					
						
							|  |  |  | #include "asterisk/app.h"
 | 
					
						
							| 
									
										
										
										
											2010-04-09 15:56:55 +00:00
										 |  |  | #include "asterisk/datastore.h"
 | 
					
						
							|  |  |  | #include "asterisk/channel.h"
 | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*** DOCUMENTATION
 | 
					
						
							|  |  |  | 	<function name="SRVQUERY" language="en_US"> | 
					
						
							|  |  |  | 		<synopsis> | 
					
						
							|  |  |  | 			Initiate an SRV query. | 
					
						
							|  |  |  | 		</synopsis> | 
					
						
							|  |  |  | 		<syntax> | 
					
						
							|  |  |  | 			<parameter name="service" required="true"> | 
					
						
							|  |  |  | 				<para>The service for which to look up SRV records. An example would be something | 
					
						
							|  |  |  | 				like <literal>_sip._udp.example.com</literal></para> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							|  |  |  | 		</syntax> | 
					
						
							|  |  |  | 		<description> | 
					
						
							|  |  |  | 			<para>This will do an SRV lookup of the given service.</para> | 
					
						
							|  |  |  | 		</description> | 
					
						
							|  |  |  | 	</function> | 
					
						
							|  |  |  | 	<function name="SRVRESULT" language="en_US"> | 
					
						
							|  |  |  | 		<synopsis> | 
					
						
							|  |  |  | 			Retrieve results from an SRVQUERY. | 
					
						
							|  |  |  | 		</synopsis> | 
					
						
							|  |  |  | 		<syntax> | 
					
						
							|  |  |  | 			<parameter name="id" required="true"> | 
					
						
							|  |  |  | 				<para>The identifier returned by the SRVQUERY function.</para> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							|  |  |  | 			<parameter name="resultnum" required="true"> | 
					
						
							|  |  |  | 				<para>The number of the result that you want to retrieve.</para> | 
					
						
							|  |  |  | 				<para>Results start at <literal>1</literal>. If this argument is specified | 
					
						
							|  |  |  | 				as <literal>getnum</literal>, then it will return the total number of results | 
					
						
							|  |  |  | 				that are available.</para> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							|  |  |  | 		</syntax> | 
					
						
							|  |  |  | 		<description> | 
					
						
							|  |  |  | 			<para>This function will retrieve results from a previous use | 
					
						
							|  |  |  | 			of the SRVQUERY function.</para> | 
					
						
							|  |  |  | 		</description> | 
					
						
							|  |  |  | 	</function> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct srv_result_datastore { | 
					
						
							|  |  |  | 	struct srv_context *context; | 
					
						
							|  |  |  | 	char id[1]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void srds_destroy_cb(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct srv_result_datastore *datastore = data; | 
					
						
							|  |  |  | 	ast_srv_cleanup(&datastore->context); | 
					
						
							|  |  |  | 	ast_free(datastore); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct ast_datastore_info srv_result_datastore_info = { | 
					
						
							|  |  |  | 	.type = "SRVQUERY", | 
					
						
							|  |  |  | 	.destroy = srds_destroy_cb, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct srv_context *srv_datastore_setup(const char *service, struct ast_channel *chan) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct srv_result_datastore *srds; | 
					
						
							|  |  |  | 	struct ast_datastore *datastore; | 
					
						
							|  |  |  | 	const char *host; | 
					
						
							|  |  |  | 	unsigned short port; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(srds = ast_calloc(1, sizeof(*srds) + strlen(service)))) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-13 16:15:36 +00:00
										 |  |  | 	ast_autoservice_start(chan); | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 	if (ast_srv_lookup(&srds->context, service, &host, &port) < 0) { | 
					
						
							| 
									
										
										
										
											2010-04-13 16:15:36 +00:00
										 |  |  | 		ast_autoservice_stop(chan); | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 		ast_log(LOG_NOTICE, "Error performing lookup of service '%s'\n", service); | 
					
						
							|  |  |  | 		ast_free(srds); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-04-13 16:15:36 +00:00
										 |  |  | 	ast_autoservice_stop(chan); | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	strcpy(srds->id, service); | 
					
						
							| 
									
										
										
										
											2010-04-13 16:15:36 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 	if (!(datastore = ast_datastore_alloc(&srv_result_datastore_info, srds->id))) { | 
					
						
							|  |  |  | 		ast_srv_cleanup(&srds->context); | 
					
						
							|  |  |  | 		ast_free(srds); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	datastore->data = srds; | 
					
						
							|  |  |  | 	ast_channel_lock(chan); | 
					
						
							|  |  |  | 	ast_channel_datastore_add(chan, datastore); | 
					
						
							|  |  |  | 	ast_channel_unlock(chan); | 
					
						
							|  |  |  | 	return srds->context; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int srv_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-04-19 18:42:31 +00:00
										 |  |  | 	struct ast_datastore *datastore; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 	if (!chan) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "%s cannot be used without a channel\n", cmd); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(data)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "%s requires a service as an argument\n", cmd); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-12-22 09:23:22 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-19 18:42:31 +00:00
										 |  |  | 	/* If they already called SRVQUERY for this service once,
 | 
					
						
							|  |  |  | 	 * we need to kill the old datastore. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ast_channel_lock(chan); | 
					
						
							|  |  |  | 	datastore = ast_channel_datastore_find(chan, &srv_result_datastore_info, data); | 
					
						
							|  |  |  | 	ast_channel_unlock(chan); | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-19 18:42:31 +00:00
										 |  |  | 	if (datastore) { | 
					
						
							|  |  |  | 		ast_channel_datastore_remove(chan, datastore); | 
					
						
							|  |  |  | 		ast_datastore_free(datastore); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-12-22 09:23:22 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 	if (!srv_datastore_setup(data, chan)) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_copy_string(buf, data, len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_custom_function srv_query_function = { | 
					
						
							|  |  |  | 	.name = "SRVQUERY", | 
					
						
							|  |  |  | 	.read = srv_query_read, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int srv_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct srv_result_datastore *srds; | 
					
						
							|  |  |  | 	struct ast_datastore *datastore; | 
					
						
							|  |  |  | 	struct srv_context *srv_context; | 
					
						
							|  |  |  | 	char *parse; | 
					
						
							|  |  |  | 	const char *host; | 
					
						
							|  |  |  | 	unsigned short port, priority, weight; | 
					
						
							|  |  |  | 	unsigned int num; | 
					
						
							|  |  |  | 	AST_DECLARE_APP_ARGS(args, | 
					
						
							|  |  |  | 		AST_APP_ARG(id); | 
					
						
							|  |  |  | 		AST_APP_ARG(resultnum); | 
					
						
							|  |  |  | 		AST_APP_ARG(field); | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!chan) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "%s cannot be used without a channel\n", cmd); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(data)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "%s requires two arguments (id and resultnum)\n", cmd); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	parse = ast_strdupa(data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	AST_STANDARD_APP_ARGS(args, parse); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_channel_lock(chan); | 
					
						
							|  |  |  | 	datastore = ast_channel_datastore_find(chan, &srv_result_datastore_info, args.id); | 
					
						
							|  |  |  | 	ast_channel_unlock(chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!datastore) { | 
					
						
							|  |  |  | 		/* They apparently decided to call SRVRESULT without first calling SRVQUERY.
 | 
					
						
							|  |  |  | 		 * No problem, we'll do the SRV lookup now. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		srv_context = srv_datastore_setup(args.id, chan); | 
					
						
							|  |  |  | 		if (!srv_context) { | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		srds = datastore->data; | 
					
						
							|  |  |  | 		srv_context = srds->context; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!strcasecmp(args.resultnum, "getnum")) { | 
					
						
							|  |  |  | 		snprintf(buf, len, "%u", ast_srv_get_record_count(srv_context)); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(args.field)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "A field must be provided when requesting SRV data\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (sscanf(args.resultnum, "%30u", &num) != 1) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to %s\n", args.resultnum, cmd); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_srv_get_nth_record(srv_context, num, &host, &port, &priority, &weight)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Failed to get record number %u for %s\n", num, cmd); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!strcasecmp(args.field, "host")) { | 
					
						
							|  |  |  | 		ast_copy_string(buf, host, len); | 
					
						
							|  |  |  | 	} else if (!strcasecmp(args.field, "port")) { | 
					
						
							| 
									
										
										
										
											2014-05-09 22:49:26 +00:00
										 |  |  | 		snprintf(buf, len, "%d", port); | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 	} else if (!strcasecmp(args.field, "priority")) { | 
					
						
							| 
									
										
										
										
											2014-05-09 22:49:26 +00:00
										 |  |  | 		snprintf(buf, len, "%d", priority); | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 	} else if (!strcasecmp(args.field, "weight")) { | 
					
						
							| 
									
										
										
										
											2014-05-09 22:49:26 +00:00
										 |  |  | 		snprintf(buf, len, "%d", weight); | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unrecognized SRV field '%s'\n", args.field); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_custom_function srv_result_function = { | 
					
						
							|  |  |  | 	.name = "SRVRESULT", | 
					
						
							|  |  |  | 	.read = srv_result_read, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int unload_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int res = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res |= ast_custom_function_unregister(&srv_query_function); | 
					
						
							|  |  |  | 	res |= ast_custom_function_unregister(&srv_result_function); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int load_module(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-04-13 16:15:36 +00:00
										 |  |  | 	int res = ast_custom_function_register(&srv_query_function); | 
					
						
							|  |  |  | 	if (res < 0) { | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	res = ast_custom_function_register(&srv_result_function); | 
					
						
							|  |  |  | 	if (res < 0) { | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-13 16:15:36 +00:00
										 |  |  | 	return AST_MODULE_LOAD_SUCCESS;; | 
					
						
							| 
									
										
										
										
											2010-04-09 14:37:50 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SRV related dialplan functions"); |