Fix FastAGI To Properly Check For A Connection
When IPv6 support was added to FastAGI, the intent was to have the ability to check all addresses resolved for a host since we might receive an IPv4 address and an IPv6 address. The problem with the current code, is that, since we are doing O_NONBLOCK, we get EINPROGRESS when calling ast_connect() but are ignoring this instead of handling it. We break out of the loop and continue on. When we later call ast_poll(), it succeeds but we never check if we have a connection or not on the socket level. We then attempt to send data to the host address that we think is setup and it fails. We then check the errno and see that we have "connection refused" and then return with agi failed. This patch does the following: * Handles EINPROGRESS by creating the function handle_connection() - ast_poll() was moved into this function - This function checks the results of the connection on the socket level after calling ast_poll() * Continues to the next address if the above fails to create a connection * Once all addresses resolved are tried and we still are unable to establish a connection, then we return that the FastAGI call failed (closes issue ASTERISK-21065) Reported by: Jeremy Kister Tested by: Jeremy Kister, Michael L. Young Patches: asterisk-21065_poll_correctly_v4.diff Michael L. Young (license 5026) Review: https://reviewboard.asterisk.org/r/2330/ ........ Merged revisions 381893 from http://svn.asterisk.org/svn/asterisk/branches/11 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@381894 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
parent
abfb23df6b
commit
e9bcf9826a
|
@ -1539,12 +1539,61 @@ async_agi_abort:
|
|||
#undef AMI_BUF_SIZE
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Handle the connection that was started by launch_netscript.
|
||||
*
|
||||
* \param agiurl Url that we are trying to connect to.
|
||||
* \param addr Address that host was resolved to.
|
||||
* \param netsockfd File descriptor of socket.
|
||||
*
|
||||
* \retval 0 when connection is succesful.
|
||||
* \retval 1 when there is an error.
|
||||
*/
|
||||
static int handle_connection(const char *agiurl, const struct ast_sockaddr addr, const int netsockfd)
|
||||
{
|
||||
struct pollfd pfds[1];
|
||||
int res, conresult;
|
||||
socklen_t reslen;
|
||||
|
||||
reslen = sizeof(conresult);
|
||||
|
||||
pfds[0].fd = netsockfd;
|
||||
pfds[0].events = POLLOUT;
|
||||
|
||||
while ((res = ast_poll(pfds, 1, MAX_AGI_CONNECT)) != 1) {
|
||||
if (errno != EINTR) {
|
||||
if (!res) {
|
||||
ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n",
|
||||
agiurl, MAX_AGI_CONNECT);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (getsockopt(pfds[0].fd, SOL_SOCKET, SO_ERROR, &conresult, &reslen) < 0) {
|
||||
ast_log(LOG_WARNING, "Connection to %s failed with error: %s\n",
|
||||
ast_sockaddr_stringify(&addr), strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (conresult) {
|
||||
ast_log(LOG_WARNING, "Connecting to '%s' failed for url '%s': %s\n",
|
||||
ast_sockaddr_stringify(&addr), agiurl, strerror(conresult));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* launch_netscript: The fastagi handler.
|
||||
FastAGI defaults to port 4573 */
|
||||
static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds)
|
||||
{
|
||||
int s = 0, flags, res;
|
||||
struct pollfd pfds[1];
|
||||
int s = 0, flags;
|
||||
char *host, *script;
|
||||
int num_addrs = 0, i = 0;
|
||||
struct ast_sockaddr *addrs;
|
||||
|
@ -1586,12 +1635,16 @@ static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ast_connect(s, &addrs[i]) && (errno != EINPROGRESS)) {
|
||||
if (ast_connect(s, &addrs[i]) && errno == EINPROGRESS) {
|
||||
|
||||
if (handle_connection(agiurl, addrs[i], s)) {
|
||||
close(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Connection to %s failed with unexpected error: %s\n",
|
||||
ast_sockaddr_stringify(&addrs[i]),
|
||||
strerror(errno));
|
||||
close(s);
|
||||
continue;
|
||||
ast_sockaddr_stringify(&addrs[i]), strerror(errno));
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -1604,20 +1657,6 @@ static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds)
|
|||
return AGI_RESULT_FAILURE;
|
||||
}
|
||||
|
||||
pfds[0].fd = s;
|
||||
pfds[0].events = POLLOUT;
|
||||
while ((res = ast_poll(pfds, 1, MAX_AGI_CONNECT)) != 1) {
|
||||
if (errno != EINTR) {
|
||||
if (!res) {
|
||||
ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n",
|
||||
agiurl, MAX_AGI_CONNECT);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
|
||||
close(s);
|
||||
return AGI_RESULT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_agi_send(s, NULL, "agi_network: yes\n") < 0) {
|
||||
if (errno != EINTR) {
|
||||
ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
|
||||
|
@ -1628,8 +1667,9 @@ static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds)
|
|||
|
||||
/* If we have a script parameter, relay it to the fastagi server */
|
||||
/* Script parameters take the form of: AGI(agi://my.example.com/?extension=${EXTEN}) */
|
||||
if (!ast_strlen_zero(script))
|
||||
if (!ast_strlen_zero(script)) {
|
||||
ast_agi_send(s, NULL, "agi_network_script: %s\n", script);
|
||||
}
|
||||
|
||||
ast_debug(4, "Wow, connected!\n");
|
||||
fds[0] = s;
|
||||
|
|
Loading…
Reference in New Issue