tcptls.c: refactor client connection to be more robust
The current TCP client connect code, blocks and does not handle EINTR error case. This patch makes the client socket non-blocking while connecting, ensures a connect does not immediately fail due to EINTR "errors", and adds a connect timeout option. The original client start call sets the new timeout option to "infinite", thus making sure old, orginal behavior is retained. ASTERISK-29746 #close Change-Id: I907571843a83e43c0742b95a64785f4411f02671
This commit is contained in:
parent
f7c4a3800c
commit
1ddaedeaf5
|
@ -164,8 +164,30 @@ struct ast_tcptls_session_instance {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief attempts to connect and start tcptls session, on error the tcptls_session's
|
* \brief Attempt to connect and start a tcptls session within the given timeout
|
||||||
* ref count is decremented, fd and file are closed, and NULL is returned.
|
*
|
||||||
|
* \note On error the tcptls_session's ref count is decremented, fd and file
|
||||||
|
* are closed, and NULL is returned.
|
||||||
|
*
|
||||||
|
* \param tcptls_session The session instance to connect and start
|
||||||
|
* \param timeout How long (in milliseconds) to attempt to connect (-1 equals infinite)
|
||||||
|
*
|
||||||
|
* \return The tcptls_session, or NULL on error
|
||||||
|
*/
|
||||||
|
struct ast_tcptls_session_instance *ast_tcptls_client_start_timeout(
|
||||||
|
struct ast_tcptls_session_instance *tcptls_session, int timeout);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Attempt to connect and start a tcptls session
|
||||||
|
*
|
||||||
|
* Blocks until a connection is established, or an error occurs.
|
||||||
|
*
|
||||||
|
* \note On error the tcptls_session's ref count is decremented, fd and file
|
||||||
|
* are closed, and NULL is returned.
|
||||||
|
*
|
||||||
|
* \param tcptls_session The session instance to connect and start
|
||||||
|
*
|
||||||
|
* \return The tcptls_session, or NULL on error
|
||||||
*/
|
*/
|
||||||
struct ast_tcptls_session_instance *ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session);
|
struct ast_tcptls_session_instance *ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session);
|
||||||
|
|
||||||
|
|
|
@ -582,20 +582,82 @@ void ast_ssl_teardown(struct ast_tls_config *cfg)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ast_tcptls_session_instance *ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session)
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Connect a socket
|
||||||
|
*
|
||||||
|
* Attempt to connect to a given address for up to 'timeout' milliseconds. A negative
|
||||||
|
* timeout value equates to an infinite wait time.
|
||||||
|
*
|
||||||
|
* A -1 is returned on error, and an appropriate errno value is set based on the
|
||||||
|
* type of error.
|
||||||
|
*
|
||||||
|
* \param sockfd The socket file descriptor
|
||||||
|
* \param addr The address to connect to
|
||||||
|
* \param timeout How long, in milliseconds, to attempt to connect
|
||||||
|
*
|
||||||
|
* \return 0 if successfully connected, -1 otherwise
|
||||||
|
*/
|
||||||
|
static int socket_connect(int sockfd, const struct ast_sockaddr *addr, int timeout)
|
||||||
|
{
|
||||||
|
int optval = 0;
|
||||||
|
socklen_t optlen = sizeof(int);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
if (ast_connect(sockfd, addr)) {
|
||||||
|
int res;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A connect failure could mean things are still in progress.
|
||||||
|
* If so wait for it to complete.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (errno != EINPROGRESS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((res = ast_wait_for_output(sockfd, timeout)) != 1) {
|
||||||
|
if (res == 0) {
|
||||||
|
errno = ETIMEDOUT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno != EINTR) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the status to ensure it actually connected successfully */
|
||||||
|
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optval) {
|
||||||
|
errno = optval;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ast_tcptls_session_instance *ast_tcptls_client_start_timeout(
|
||||||
|
struct ast_tcptls_session_instance *tcptls_session, int timeout)
|
||||||
{
|
{
|
||||||
struct ast_tcptls_session_args *desc;
|
struct ast_tcptls_session_args *desc;
|
||||||
|
|
||||||
if (!(desc = tcptls_session->parent)) {
|
if (!(desc = tcptls_session->parent)) {
|
||||||
goto client_start_error;
|
ao2_ref(tcptls_session, -1);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ast_connect(desc->accept_fd, &desc->remote_address)) {
|
if (socket_connect(desc->accept_fd, &desc->remote_address, timeout)) {
|
||||||
ast_log(LOG_ERROR, "Unable to connect %s to %s: %s\n",
|
ast_log(LOG_WARNING, "Unable to connect %s to %s: %s\n", desc->name,
|
||||||
desc->name,
|
ast_sockaddr_stringify(&desc->remote_address), strerror(errno));
|
||||||
ast_sockaddr_stringify(&desc->remote_address),
|
|
||||||
strerror(errno));
|
ao2_ref(tcptls_session, -1);
|
||||||
goto client_start_error;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_fd_clear_flags(desc->accept_fd, O_NONBLOCK);
|
ast_fd_clear_flags(desc->accept_fd, O_NONBLOCK);
|
||||||
|
@ -606,10 +668,11 @@ struct ast_tcptls_session_instance *ast_tcptls_client_start(struct ast_tcptls_se
|
||||||
}
|
}
|
||||||
|
|
||||||
return handle_tcptls_connection(tcptls_session);
|
return handle_tcptls_connection(tcptls_session);
|
||||||
|
}
|
||||||
|
|
||||||
client_start_error:
|
struct ast_tcptls_session_instance *ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session)
|
||||||
ao2_ref(tcptls_session, -1);
|
{
|
||||||
return NULL;
|
return ast_tcptls_client_start_timeout(tcptls_session, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_session_args *desc)
|
struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_session_args *desc)
|
||||||
|
@ -626,7 +689,7 @@ struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_s
|
||||||
/* If we return early, there is no connection */
|
/* If we return early, there is no connection */
|
||||||
ast_sockaddr_setnull(&desc->old_address);
|
ast_sockaddr_setnull(&desc->old_address);
|
||||||
|
|
||||||
fd = desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->remote_address) ?
|
fd = desc->accept_fd = ast_socket_nonblock(ast_sockaddr_is_ipv6(&desc->remote_address) ?
|
||||||
AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
if (desc->accept_fd < 0) {
|
if (desc->accept_fd < 0) {
|
||||||
ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n",
|
ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n",
|
||||||
|
@ -673,6 +736,7 @@ struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_s
|
||||||
|
|
||||||
/* Set current info */
|
/* Set current info */
|
||||||
ast_sockaddr_copy(&desc->old_address, &desc->remote_address);
|
ast_sockaddr_copy(&desc->old_address, &desc->remote_address);
|
||||||
|
|
||||||
return tcptls_session;
|
return tcptls_session;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|
Loading…
Reference in New Issue