|
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 |
}
|