Blame genhash/layer4.c

Packit c22fc9
/*
Packit c22fc9
 * Soft:        Perform a GET query to a remote HTTP/HTTPS server.
Packit c22fc9
 *              Set a timer to compute global remote server response
Packit c22fc9
 *              time.
Packit c22fc9
 *
Packit c22fc9
 * Part:        Layer4 asynchronous primitives.
Packit c22fc9
 *
Packit c22fc9
 * Authors:     Alexandre Cassen, <acassen@linux-vs.org>
Packit c22fc9
 *
Packit c22fc9
 *              This program is distributed in the hope that it will be useful,
Packit c22fc9
 *              but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit c22fc9
 *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Packit c22fc9
 *              See the GNU General Public License for more details.
Packit c22fc9
 *
Packit c22fc9
 *              This program is free software; you can redistribute it and/or
Packit c22fc9
 *              modify it under the terms of the GNU General Public License
Packit c22fc9
 *              as published by the Free Software Foundation; either version
Packit c22fc9
 *              2 of the License, or (at your option) any later version.
Packit c22fc9
 *
Packit c22fc9
 * Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
Packit c22fc9
 */
Packit c22fc9
Packit c22fc9
#include "config.h"
Packit c22fc9
Packit Service dfccb1
#include <stdio.h>
Packit c22fc9
#include <fcntl.h>
Packit c22fc9
Packit c22fc9
/* genhash includes */
Packit c22fc9
#include "include/layer4.h"
Packit c22fc9
Packit c22fc9
static enum connect_result
Packit c22fc9
tcp_connect(int fd, REQ * req_obj)
Packit c22fc9
{
Packit c22fc9
	struct linger li;
Packit c22fc9
	socklen_t long_inet;
Packit c22fc9
	struct sockaddr_in adr_serv;
Packit c22fc9
	struct sockaddr_in6 adr_serv6;
Packit c22fc9
	int ret;
Packit c22fc9
Packit Service dfccb1
	/* free the tcp port after closing the socket descriptor, but allow
Packit Service dfccb1
	 * time for a proper shutdown. */
Packit c22fc9
	li.l_onoff = 1;
Packit Service dfccb1
	li.l_linger = 5;
Packit Service dfccb1
	if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (struct linger)))
Packit Service dfccb1
		fprintf(stderr, "Error setting SO_LINGER on socket %d\n", fd);
Packit c22fc9
Packit c22fc9
#ifdef _WITH_SO_MARK_
Packit c22fc9
	if (req->mark) {
Packit c22fc9
		if (setsockopt (fd, SOL_SOCKET, SO_MARK, &req->mark, sizeof req->mark)) {
Packit c22fc9
			fprintf(stderr, "Error setting fwmark %u to socket: %s\n",
Packit c22fc9
					req->mark, strerror(errno));
Packit c22fc9
			return connect_error;
Packit c22fc9
		}
Packit c22fc9
	}
Packit c22fc9
#endif
Packit c22fc9
Packit c22fc9
	if(req_obj->dst && req_obj->dst->ai_family == AF_INET6) {
Packit c22fc9
		long_inet = sizeof (struct sockaddr_in6);
Packit c22fc9
		memset(&adr_serv6, 0, long_inet);
Packit c22fc9
		adr_serv6.sin6_family = AF_INET6;
Packit c22fc9
		adr_serv6.sin6_port = req_obj->addr_port;
Packit c22fc9
		inet_pton(AF_INET6, req_obj->ipaddress, &adr_serv6.sin6_addr);
Packit c22fc9
Packit c22fc9
		/* Call connect function. */
Packit c22fc9
		ret = connect(fd, (struct sockaddr *) &adr_serv6, long_inet);
Packit c22fc9
	} else {
Packit c22fc9
		long_inet = sizeof (struct sockaddr_in);
Packit c22fc9
		memset(&adr_serv, 0, long_inet);
Packit c22fc9
		adr_serv.sin_family = AF_INET;
Packit c22fc9
		adr_serv.sin_port = req_obj->addr_port;
Packit c22fc9
		inet_pton(AF_INET, req_obj->ipaddress, &adr_serv.sin_addr);
Packit c22fc9
Packit c22fc9
		/* Call connect function. */
Packit c22fc9
		ret = connect(fd, (struct sockaddr *) &adr_serv, long_inet);
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* Immediate success */
Packit Service dfccb1
	if (ret == 0)
Packit c22fc9
		return connect_success;
Packit c22fc9
Packit Service dfccb1
	/* If connect is in progress then return connect_in_progress else it's real error. */
Packit Service dfccb1
	if (errno != EINPROGRESS)
Packit Service dfccb1
		return connect_error;
Packit c22fc9
Packit c22fc9
	/* restore previous fd args */
Packit c22fc9
	return connect_in_progress;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
static enum connect_result
Packit Service dfccb1
tcp_socket_state(thread_ref_t thread, thread_func_t func)
Packit c22fc9
{
Packit c22fc9
	int status;
Packit c22fc9
	socklen_t slen;
Packit c22fc9
	int ret = 0;
Packit c22fc9
	timeval_t timer_min;
Packit c22fc9
Packit c22fc9
	/* Handle connection timeout */
Packit c22fc9
	if (thread->type == THREAD_WRITE_TIMEOUT) {
Packit Service dfccb1
#ifdef _GENHASH_DEBUG_
Packit Service dfccb1
		fprintf(stderr, "TCP connection timeout to [%s]:%d.\n",
Packit c22fc9
		    req->ipaddress, ntohs(req->addr_port));
Packit Service dfccb1
#endif
Packit c22fc9
		thread_close_fd(thread);
Packit c22fc9
		return connect_timeout;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* Check file descriptor */
Packit c22fc9
	slen = sizeof (status);
Packit c22fc9
	if (getsockopt
Packit Service dfccb1
	    (thread->u.f.fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen) < 0)
Packit c22fc9
		ret = errno;
Packit c22fc9
Packit c22fc9
	/* Connection failed !!! */
Packit c22fc9
	if (ret) {
Packit Service dfccb1
#ifdef _GENHASH_DEBUG_
Packit Service dfccb1
		fprintf(stderr, "TCP getsockopt() failed to [%s]:%d.\n",
Packit c22fc9
		    req->ipaddress, ntohs(req->addr_port));
Packit Service dfccb1
#endif
Packit c22fc9
		thread_close_fd(thread);
Packit c22fc9
		return connect_error;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* If status = 0, TCP connection to remote host is established.
Packit c22fc9
	 * Otherwise register checker thread to handle connection in progress,
Packit c22fc9
	 * and other error code until connection is established.
Packit c22fc9
	 * Recompute the write timeout (or pending connection).
Packit c22fc9
	 */
Packit Service dfccb1
	if (status == EINPROGRESS) {
Packit Service dfccb1
#ifdef _GENHASH_DEBUG_
Packit Service dfccb1
		fprintf(stderr, "TCP connection to [%s]:%d still IN_PROGRESS.\n",
Packit c22fc9
		    req->ipaddress, ntohs(req->addr_port));
Packit Service dfccb1
#endif
Packit c22fc9
Packit c22fc9
		timer_min = timer_sub_now(thread->sands);
Packit c22fc9
		thread_add_write(thread->master, func, THREAD_ARG(thread)
Packit Service dfccb1
				 , thread->u.f.fd, timer_long(timer_min), true);
Packit c22fc9
		return connect_in_progress;
Packit Service dfccb1
	} else if (status) {
Packit Service dfccb1
#ifdef _GENHASH_DEBUG_
Packit Service dfccb1
		fprintf(stderr, "TCP connection failed to [%s]:%d.\n",
Packit Service dfccb1
		    req->ipaddress, ntohs(req->addr_port));
Packit Service dfccb1
#endif
Packit Service dfccb1
		thread_close_fd(thread);
Packit Service dfccb1
		return connect_error;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	return connect_success;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
static void
Packit Service dfccb1
tcp_connection_state(int fd, enum connect_result status, thread_ref_t thread
Packit Service dfccb1
		     , thread_func_t func
Packit c22fc9
		     , unsigned long timeout)
Packit c22fc9
{
Packit c22fc9
	switch (status) {
Packit c22fc9
	case connect_error:
Packit c22fc9
		close(fd);
Packit c22fc9
		thread_add_terminate_event(thread->master);
Packit c22fc9
		break;
Packit c22fc9
Packit c22fc9
	case connect_success:
Packit c22fc9
		thread_add_write(thread->master, func, THREAD_ARG(thread),
Packit Service dfccb1
				 fd, timeout, true);
Packit c22fc9
		break;
Packit c22fc9
Packit c22fc9
		/* Checking non-blocking connect, we wait until socket is writable */
Packit c22fc9
	case connect_in_progress:
Packit c22fc9
		thread_add_write(thread->master, func, THREAD_ARG(thread),
Packit Service dfccb1
				 fd, timeout, true);
Packit c22fc9
		break;
Packit c22fc9
Packit c22fc9
	default:
Packit c22fc9
		break;
Packit c22fc9
	}
Packit c22fc9
}
Packit c22fc9
Packit Service dfccb1
static void
Packit Service dfccb1
tcp_check_thread(thread_ref_t thread)
Packit c22fc9
{
Packit c22fc9
	SOCK *sock_obj = THREAD_ARG(thread);
Packit c22fc9
	int ret = 1;
Packit c22fc9
Packit c22fc9
	sock_obj->status = tcp_socket_state(thread, tcp_check_thread);
Packit c22fc9
	switch (sock_obj->status) {
Packit c22fc9
	case connect_error:
Packit Service dfccb1
#ifdef _GENHASH_DEBUG_
Packit Service dfccb1
		fprintf(stderr, "Error connecting server [%s]:%d.\n",
Packit c22fc9
		    req->ipaddress, ntohs(req->addr_port));
Packit Service dfccb1
#endif
Packit c22fc9
		thread_add_terminate_event(thread->master);
Packit Service dfccb1
		return;
Packit c22fc9
		break;
Packit c22fc9
Packit c22fc9
	case connect_timeout:
Packit Service dfccb1
#ifdef _GENHASH_DEBUG_
Packit Service dfccb1
		fprintf(stderr, "Timeout connecting server [%s]:%d.\n",
Packit c22fc9
		    req->ipaddress, ntohs(req->addr_port));
Packit Service dfccb1
#endif
Packit c22fc9
		thread_add_terminate_event(thread->master);
Packit Service dfccb1
		return;
Packit c22fc9
		break;
Packit c22fc9
Packit c22fc9
	case connect_success:{
Packit c22fc9
			if (req->ssl)
Packit c22fc9
				ret = ssl_connect(thread);
Packit c22fc9
Packit c22fc9
			if (ret) {
Packit Service dfccb1
				/* SSL connections manage their own threads for SSL_connect */
Packit Service dfccb1
				if (req->ssl)
Packit Service dfccb1
					return;
Packit Service dfccb1
Packit c22fc9
				/* Remote WEB server is connected.
Packit c22fc9
				 * Unlock eventual locked socket.
Packit c22fc9
				 */
Packit c22fc9
				sock_obj->lock = 0;
Packit c22fc9
				thread_add_event(thread->master,
Packit c22fc9
						 http_request_thread, sock_obj, 0);
Packit c22fc9
				thread_del_write(thread);
Packit c22fc9
			} else {
Packit Service dfccb1
#ifdef _GENHASH_DEBUG_
Packit Service dfccb1
				fprintf(stderr, "Connection trouble to: [%s]:%d.\n",
Packit c22fc9
				    req->ipaddress,
Packit c22fc9
				    ntohs(req->addr_port));
Packit Service dfccb1
#endif
Packit c22fc9
				sock_obj->status = connect_error;
Packit Service dfccb1
				thread_add_terminate_event(thread->master);
Packit Service dfccb1
				return;
Packit c22fc9
			}
Packit c22fc9
		}
Packit c22fc9
		break;
Packit c22fc9
	}
Packit c22fc9
}
Packit c22fc9
Packit Service dfccb1
void
Packit Service dfccb1
tcp_connect_thread(thread_ref_t thread)
Packit c22fc9
{
Packit c22fc9
	SOCK *sock_obj = THREAD_ARG(thread);
Packit c22fc9
Packit c22fc9
	if ((sock_obj->fd = socket((req->dst && req->dst->ai_family == AF_INET6) ? AF_INET6 : AF_INET,
Packit Service dfccb1
				   SOCK_STREAM | SOCK_NONBLOCK
Packit c22fc9
#ifdef SOCK_CLOEXEC
Packit c22fc9
					       | SOCK_CLOEXEC
Packit c22fc9
#endif
Packit c22fc9
							     , IPPROTO_TCP)) == -1) {
Packit Service dfccb1
#ifdef _GENHASH_DEBUG_
Packit Service dfccb1
		fprintf(stderr, "WEB connection fail to create socket.\n");
Packit Service dfccb1
#endif
Packit Service dfccb1
		return;
Packit c22fc9
	}
Packit c22fc9
Packit Service dfccb1
#if !HAVE_DECL_SOCK_NONBLOCK
Packit Service dfccb1
	if (fcntl(sock_obj->fd, F_SETFL, fcntl(sock_obj->fd, F_GETFL) | O_NONBLOCK))
Packit Service dfccb1
		fprintf(stderr, "Unable to set socket non blocking\n");
Packit c22fc9
#endif
Packit c22fc9
Packit c22fc9
	sock->status = tcp_connect(sock_obj->fd, req);
Packit c22fc9
Packit c22fc9
	/* handle tcp connection status & register check worker thread */
Packit c22fc9
	tcp_connection_state(sock_obj->fd, sock_obj->status, thread, tcp_check_thread,
Packit Service dfccb1
			     req->timeout);
Packit c22fc9
}