diff -Nurp trunk/include/asterisk/srv.h patched/include/asterisk/srv.h --- trunk/include/asterisk/srv.h 2007-11-05 19:53:13.000000000 -0700 +++ patched/include/asterisk/srv.h 2009-03-26 22:40:15.000000000 -0600 @@ -23,6 +23,8 @@ #ifndef _ASTERISK_SRV_H #define _ASTERISK_SRV_H +#include "asterisk/linkedlists.h" + /*! \file srv.h \brief Support for DNS SRV records, used in to locate SIP services. @@ -31,6 +33,26 @@ no provisions for retrying or failover between records. */ +struct srv_entry { + unsigned short priority; + unsigned short weight; + unsigned short port; + unsigned int weight_sum; + AST_LIST_ENTRY(srv_entry) list; + char host[1]; +}; + + +struct srv_context { + unsigned int have_weights:1; + AST_LIST_HEAD_NOLOCK(srv_entries, srv_entry) entries; +}; + +/*! \param context The context in which to store the result + \param service The service name to look up +*/ +extern int ast_srv_lookup(struct srv_context *context, const char *service); + /*! 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. */ diff -Nurp trunk/main/srv.c patched/main/srv.c --- trunk/main/srv.c 2009-01-12 16:13:13.000000000 -0700 +++ patched/main/srv.c 2009-03-26 22:23:49.000000000 -0600 @@ -53,20 +53,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi #define T_SRV 33 #endif -struct srv_entry { - unsigned short priority; - unsigned short weight; - unsigned short port; - unsigned int weight_sum; - AST_LIST_ENTRY(srv_entry) list; - char host[1]; -}; - -struct srv_context { - unsigned int have_weights:1; - AST_LIST_HEAD_NOLOCK(srv_entries, srv_entry) entries; -}; - static int parse_srv(unsigned char *answer, int len, unsigned char *msg, struct srv_entry **result) { struct srv { @@ -197,6 +183,18 @@ static void process_weights(struct srv_c AST_LIST_APPEND_LIST(&context->entries, &newlist, list); } +int ast_srv_lookup(struct srv_context *context, const char *service) +{ + int ret; + + ret = ast_search_dns(context, service, C_IN, T_SRV, srv_callback); + + if (context->have_weights) + process_weights(context); + + return ret; +} + int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service) { struct srv_context context = { .entries = AST_LIST_HEAD_NOLOCK_INIT_VALUE }; @@ -206,10 +204,7 @@ int ast_get_srv(struct ast_channel *chan if (chan && ast_autoservice_start(chan) < 0) return -1; - ret = ast_search_dns(&context, service, C_IN, T_SRV, srv_callback); - - if (context.have_weights) - process_weights(&context); + ret = ast_srv_lookup(&context, service); if (chan) ret |= ast_autoservice_stop(chan); diff -Nurp trunk/res/res_agi.c patched/res/res_agi.c --- trunk/res/res_agi.c 2009-03-18 08:32:47.000000000 -0600 +++ patched/res/res_agi.c 2009-03-27 15:00:48.000000000 -0600 @@ -57,10 +57,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi #include "asterisk/speech.h" #include "asterisk/manager.h" #include "asterisk/features.h" +#include "asterisk/srv.h" #define MAX_ARGS 128 #define AGI_NANDFS_RETRY 3 #define AGI_BUF_LEN 2048 +#define SRV_PREFIX "_agi._tcp." static char *app = "AGI"; @@ -523,7 +525,7 @@ static enum agi_result launch_netscript( 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, '/'))) { @@ -602,6 +604,81 @@ static enum agi_result launch_netscript( 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 + * \param efd enhanced AGI flag + * \param opid + * + * Uses SRV lookups to try to connect to a list of AGI 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, int *efd, int *opid) +{ + char *host, *c, *script = ""; + enum agi_result result = AGI_RESULT_FAILURE; + struct srv_context context = { .entries = AST_LIST_HEAD_NOLOCK_INIT_VALUE }; + struct srv_entry *current; + int srv_ret; + char service[256]; + char resolved_uri[1024]; + + if (efd) { + ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n"); + return AGI_RESULT_FAILURE; + } + + /* 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 ((c = strchr(host, '/'))) { + *c = '\0'; + c++; + script = c; + } + if ((c = strchr(host, ':'))) { + ast_log(LOG_WARNING, "Specifying a port number disables SRV lookups: %s\n", agiurl); + return launch_netscript(agiurl+1, argv, fds, efd, opid); /* +1 to strip off leading h from hagi:// */ + } + + memset(service, 0, sizeof(service)); + snprintf(service, sizeof(service), "%s%s", SRV_PREFIX, host); + srv_ret = ast_srv_lookup(&context, service); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&context.entries, current, list) { + memset(resolved_uri, 0, sizeof(resolved_uri)); + snprintf(resolved_uri, sizeof(resolved_uri), "agi://%s:%d/%s", current->host, current->port, script); + result = launch_netscript(resolved_uri, argv, fds, efd, opid); + if (result == AGI_RESULT_FAILURE || result == AGI_RESULT_NOTFOUND) { + ast_log(LOG_WARNING, "AGI request failed for host %s\n", current->host); + } else { + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + while ((current = AST_LIST_REMOVE_HEAD(&context.entries, list))) + ast_free(current); + + return result; +} + static enum agi_result launch_script(struct ast_channel *chan, char *script, char *argv[], int *fds, int *efd, int *opid) { char tmp[256]; @@ -610,6 +687,8 @@ static enum agi_result launch_script(str if (!strncasecmp(script, "agi://", 6)) return launch_netscript(script, argv, fds, efd, opid); + if (!strncasecmp(script, "hagi://", 7)) + return launch_ha_netscript(script, argv, fds, efd, opid); if (!strncasecmp(script, "agi:async", sizeof("agi:async")-1)) return launch_asyncagi(chan, argv, efd);