mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +00:00
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_ Reviewboard: https://reviewboard.asterisk.org/r/378/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@241188 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
5
CHANGES
5
CHANGES
@@ -1325,6 +1325,11 @@ AGI Changes
|
||||
* 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
|
||||
--------------
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
50
main/srv.c
50
main/srv.c
@@ -64,6 +64,7 @@ struct srv_entry {
|
||||
|
||||
struct srv_context {
|
||||
unsigned int have_weights:1;
|
||||
struct srv_entry *prev;
|
||||
AST_LIST_HEAD_NOLOCK(srv_entries, srv_entry) entries;
|
||||
};
|
||||
|
||||
@@ -197,6 +198,55 @@ static void process_weights(struct srv_context *context)
|
||||
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)
|
||||
{
|
||||
struct srv_context context = { .entries = AST_LIST_HEAD_NOLOCK_INIT_VALUE };
|
||||
|
106
res/res_agi.c
106
res/res_agi.c
@@ -60,6 +60,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#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 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#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,32 +1341,28 @@ quit:
|
||||
|
||||
/* 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);
|
||||
return -1;
|
||||
@@ -1423,20 +1421,92 @@ static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, in
|
||||
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)
|
||||
{
|
||||
char tmp[256];
|
||||
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 @@ static int agi_exec_full(struct ast_channel *chan, const char *data, int enhance
|
||||
{
|
||||
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];
|
||||
);
|
||||
|
Reference in New Issue
Block a user