mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 18:55:19 +00:00 
			
		
		
		
	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:
		
				
					committed by
					
						 Friendly Automation
						Friendly Automation
					
				
			
			
				
	
			
			
			
						parent
						
							584118c7f0
						
					
				
				
					commit
					935b8cb04e
				
			| @@ -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: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user