[asterisk-commits] tilghman: trunk r241188 - in /trunk: ./ include/asterisk/ main/ res/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Jan 18 18:28:58 CST 2010


Author: tilghman
Date: Mon Jan 18 18:28:49 2010
New Revision: 241188

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=241188
Log:
Create iterative method for querying SRV results, and use that for finding AGI servers.

(closes issue #14775)
 Reported by: _brent_
 Patches: 
       20091215__issue14775.diff.txt uploaded by tilghman (license 14)
       hagi-5.patch uploaded by brent (license 388)
 Tested by: _brent_

Modified:
    trunk/CHANGES
    trunk/include/asterisk/srv.h
    trunk/main/srv.c
    trunk/res/res_agi.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=241188&r1=241187&r2=241188
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Mon Jan 18 18:28:49 2010
@@ -1325,6 +1325,11 @@
   * If app_stack is loaded, GOSUB is a native AGI command that may be used to
     invoke subroutines in the dialplan.  Note that calling EXEC with Gosub
     does not behave as expected; the native command needs to be used, instead.
+  * Added the ability to perform SRV lookups on fast AGI calls. To use this
+    feature, simply use hagi: instead of agi: as the protocol portion
+    of the URI parameter to the AGI function call in your dial plan. Also note
+    that specifying a port number in the AGI URI will disable SRV lookups,
+    even if you use the hagi: protocol.
 
 Logger changes
 --------------

Modified: trunk/include/asterisk/srv.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/srv.h?view=diff&rev=241188&r1=241187&r2=241188
==============================================================================
--- trunk/include/asterisk/srv.h (original)
+++ trunk/include/asterisk/srv.h Mon Jan 18 18:28:49 2010
@@ -31,6 +31,25 @@
 	no provisions for retrying or failover between records.
 */
 
+/*!\brief An opaque type, for lookup usage */
+struct srv_context;
+
+/*!\brief Retrieve set of SRV lookups, in order
+ * \param[in] context A pointer in which to hold the result
+ * \param[in] service The service name to look up
+ * \param[out] host Result host
+ * \param[out] port Associated TCP portnum
+ * \retval -1 Query failed
+ * \retval 0 Result exists in host and port
+ * \retval 1 No more results
+ */
+extern int ast_srv_lookup(struct srv_context **context, const char *service, const char **host, unsigned short *port);
+
+/*!\brief Cleanup resources associated with ast_srv_lookup
+ * \param context Pointer passed into ast_srv_lookup
+ */
+void ast_srv_cleanup(struct srv_context **context);
+
 /*! Lookup entry in SRV records Returns 1 if found, 0 if not found, -1 on hangup 
 	Only do SRV record lookup if you get a domain without a port. If you get a port #, it's a DNS host name.
 */

Modified: trunk/main/srv.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/srv.c?view=diff&rev=241188&r1=241187&r2=241188
==============================================================================
--- trunk/main/srv.c (original)
+++ trunk/main/srv.c Mon Jan 18 18:28:49 2010
@@ -64,6 +64,7 @@
 
 struct srv_context {
 	unsigned int have_weights:1;
+	struct srv_entry *prev;
 	AST_LIST_HEAD_NOLOCK(srv_entries, srv_entry) entries;
 };
 
@@ -195,6 +196,55 @@
 	   put it in place */
 
 	AST_LIST_APPEND_LIST(&context->entries, &newlist, list);
+}
+
+int ast_srv_lookup(struct srv_context **context, const char *service, const char **host, unsigned short *port)
+{
+	struct srv_entry *cur;
+
+	if (*context == NULL) {
+		if (!(*context = ast_calloc(1, sizeof(struct srv_context)))) {
+			return -1;
+		}
+		AST_LIST_HEAD_INIT_NOLOCK(&(*context)->entries);
+
+		if ((ast_search_dns(*context, service, C_IN, T_SRV, srv_callback)) < 0) {
+			ast_free(*context);
+			*context = NULL;
+			return -1;
+		}
+
+		if ((*context)->have_weights) {
+			process_weights(*context);
+		}
+
+		(*context)->prev = AST_LIST_FIRST(&(*context)->entries);
+		*host = (*context)->prev->host;
+		*port = (*context)->prev->port;
+		return 0;
+	}
+
+	if (((*context)->prev = AST_LIST_NEXT((*context)->prev, list))) {
+		/* Retrieve next item in result */
+		*host = (*context)->prev->host;
+		*port = (*context)->prev->port;
+		return 0;
+	} else {
+		/* No more results */
+		while ((cur = AST_LIST_REMOVE_HEAD(&(*context)->entries, list))) {
+			ast_free(cur);
+		}
+		ast_free(*context);
+		*context = NULL;
+		return 1;
+	}
+}
+
+void ast_srv_cleanup(struct srv_context **context)
+{
+	const char *host;
+	unsigned short port;
+	while (!(ast_srv_lookup(context, NULL, &host, &port)));
 }
 
 int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)

Modified: trunk/res/res_agi.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_agi.c?view=diff&rev=241188&r1=241187&r2=241188
==============================================================================
--- trunk/res/res_agi.c (original)
+++ trunk/res/res_agi.c Mon Jan 18 18:28:49 2010
@@ -60,6 +60,7 @@
 #include "asterisk/features.h"
 #include "asterisk/term.h"
 #include "asterisk/xmldoc.h"
+#include "asterisk/srv.h"
 
 #define AST_API_MODULE
 #include "asterisk/agi.h"
@@ -897,6 +898,7 @@
 #define MAX_CMD_LEN 80
 #define AGI_NANDFS_RETRY 3
 #define AGI_BUF_LEN 2048
+#define SRV_PREFIX "_agi._tcp."
 
 static char *app = "AGI";
 
@@ -1339,31 +1341,27 @@
 
 /* launch_netscript: The fastagi handler.
 	FastAGI defaults to port 4573 */
-static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
+static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds)
 {
 	int s, flags, res, port = AGI_PORT;
 	struct pollfd pfds[1];
-	char *host, *c, *script = "";
+	char *host, *c, *script;
 	struct sockaddr_in addr_in;
 	struct hostent *hp;
 	struct ast_hostent ahp;
 
-	/* agiusl is "agi://host.domain[:port][/script/name]" */
+	/* agiurl is "agi://host.domain[:port][/script/name]" */
 	host = ast_strdupa(agiurl + 6);	/* Remove agi:// */
 	/* Strip off any script name */
-	if ((c = strchr(host, '/'))) {
-		*c = '\0';
-		c++;
-		script = c;
-	}
+	if ((script = strchr(host, '/'))) {
+		*script++ = '\0';
+	} else {
+		script = "";
+	}
+
 	if ((c = strchr(host, ':'))) {
-		*c = '\0';
-		c++;
+		*c++ = '\0';
 		port = atoi(c);
-	}
-	if (efd) {
-		ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
-		return -1;
 	}
 	if (!(hp = ast_gethostbyname(host, &ahp))) {
 		ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
@@ -1423,8 +1421,75 @@
 	ast_debug(4, "Wow, connected!\n");
 	fds[0] = s;
 	fds[1] = s;
-	*opid = -1;
 	return AGI_RESULT_SUCCESS_FAST;
+}
+
+/*!
+ * \internal
+ * \brief The HA fastagi handler.
+ * \param agiurl The request URL as passed to Agi() in the dial plan
+ * \param argv The parameters after the URL passed to Agi() in the dial plan
+ * \param fds Input/output file descriptors
+ *
+ * Uses SRV lookups to try to connect to a list of FastAGI servers. The hostname in
+ * the URI is prefixed with _agi._tcp. prior to the DNS resolution. For
+ * example, if you specify the URI \a hagi://agi.example.com/foo.agi the DNS
+ * query would be for \a _agi._tcp.agi.example.com and you'll need to make sure
+ * this resolves.
+ *
+ * This function parses the URI, resolves the SRV service name, forms new URIs
+ * with the results of the DNS lookup, and then calls launch_netscript on the
+ * new URIs until one succeeds.
+ *
+ * \return the result of the AGI operation.
+ */
+static enum agi_result launch_ha_netscript(char *agiurl, char *argv[], int *fds)
+{
+	char *host, *script;
+	enum agi_result result = AGI_RESULT_FAILURE;
+	struct srv_context *context = NULL;
+	int srv_ret;
+	char service[256];
+	char resolved_uri[1024];
+	const char *srvhost;
+	unsigned short srvport;
+
+	/* format of agiurl is "hagi://host.domain[:port][/script/name]" */
+	if (!(host = ast_strdupa(agiurl + 7))) { /* Remove hagi:// */
+		ast_log(LOG_WARNING, "An error occurred parsing the AGI URI: %s", agiurl);
+		return AGI_RESULT_FAILURE;
+	}
+
+	/* Strip off any script name */
+	if ((script = strchr(host, '/'))) {
+		*script++ = '\0';
+	} else {
+		script = "";
+	}
+
+	if (strchr(host, ':')) {
+		ast_log(LOG_WARNING, "Specifying a port number disables SRV lookups: %s\n", agiurl);
+		return launch_netscript(agiurl + 1, argv, fds); /* +1 to strip off leading h from hagi:// */
+	}
+
+	snprintf(service, sizeof(service), "%s%s", SRV_PREFIX, host);
+
+	while (!(srv_ret = ast_srv_lookup(&context, service, &srvhost, &srvport))) {
+		snprintf(resolved_uri, sizeof(resolved_uri), "agi://%s:%d/%s", srvhost, srvport, script);
+		result = launch_netscript(resolved_uri, argv, fds);
+		if (result == AGI_RESULT_FAILURE || result == AGI_RESULT_NOTFOUND) {
+			ast_log(LOG_WARNING, "AGI request failed for host '%s' (%s:%d)\n", host, srvhost, srvport);
+		} else {
+			break;
+		}
+	}
+	if (srv_ret < 0) {
+		ast_log(LOG_WARNING, "SRV lookup failed for %s\n", agiurl);
+	} else {
+        ast_srv_cleanup(&context);
+    }
+
+	return result;
 }
 
 static enum agi_result launch_script(struct ast_channel *chan, char *script, char *argv[], int *fds, int *efd, int *opid)
@@ -1433,10 +1498,15 @@
 	int pid, toast[2], fromast[2], audio[2], res;
 	struct stat st;
 
-	if (!strncasecmp(script, "agi://", 6))
-		return launch_netscript(script, argv, fds, efd, opid);
-	if (!strncasecmp(script, "agi:async", sizeof("agi:async")-1))
+	if (!strncasecmp(script, "agi://", 6)) {
+		return (efd == NULL) ? launch_netscript(script, argv, fds) : AGI_RESULT_FAILURE;
+	}
+	if (!strncasecmp(script, "hagi://", 7)) {
+		return (efd == NULL) ? launch_ha_netscript(script, argv, fds) : AGI_RESULT_FAILURE;
+	}
+	if (!strncasecmp(script, "agi:async", sizeof("agi:async") - 1)) {
 		return launch_asyncagi(chan, argv, efd);
+	}
 
 	if (script[0] != '/') {
 		snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_AGI_DIR, script);
@@ -3590,7 +3660,7 @@
 {
 	enum agi_result res;
 	char *buf;
-	int fds[2], efd = -1, pid;
+	int fds[2], efd = -1, pid = -1;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(arg)[MAX_ARGS];
 	);




More information about the asterisk-commits mailing list