/* * Soft: Perform a GET query to a remote HTTP/HTTPS server. * Set a timer to compute global remote server response * time. * * Part: Layer4 asynchronous primitives. * * Version: $Id: layer4.c,v 1.2 2005/08/20 16:38:55 clement Exp $ * * Authors: Alexandre Cassen, * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Copyright (C) 2001-2005 Alexandre Cassen, */ #include "layer4.h" #include "utils.h" #include "main.h" #include "sock.h" #include "http.h" #include "ssl.h" #ifdef _FreeBSD_ //#include "FreeBSD_compat.h" #include #include #include #include #include #endif enum connect_result tcp_connect(int fd, uint32_t addr_ip, uint16_t addr_port) { struct linger li = { 0 }; int long_inet; struct sockaddr_in adr_serv; int ret; int val; /* free the tcp port after closing the socket descriptor */ li.l_onoff = 1; li.l_linger = 0; setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (struct linger)); long_inet = sizeof (struct sockaddr_in); memset(&adr_serv, 0, long_inet); adr_serv.sin_family = AF_INET; adr_serv.sin_port = addr_port; adr_serv.sin_addr.s_addr = addr_ip; /* Make socket non-block. */ val = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, val | O_NONBLOCK); /* Call connect function. */ ret = connect(fd, (struct sockaddr *) &adr_serv, long_inet); /* Immediate success */ if (ret == 0) { fcntl(fd, F_SETFL, val); return connect_success; } /* If connect is in progress then return 1 else it's real error. */ if (ret < 0) { if (errno != EINPROGRESS) return connect_error; } /* restore previous fd args */ fcntl(fd, F_SETFL, val); return connect_in_progress; } enum connect_result tcp_socket_state(int fd, thread * thread_obj, uint32_t addr_ip, uint16_t addr_port, int (*func) (struct _thread *)) { int status; int slen; int ret = 0; TIMEVAL timer_min; /* Handle connection timeout */ if (thread_obj->type == THREAD_WRITE_TIMEOUT) { DBG("TCP connection timeout to [%s:%d].\n", inet_ntop2(addr_ip), ntohs(addr_port)); close(thread_obj->u.fd); return connect_timeout; } /* Check file descriptor */ slen = sizeof (status); if (getsockopt (thread_obj->u.fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen) < 0) ret = errno; /* Connection failed !!! */ if (ret) { DBG("TCP connection failed to [%s:%d].\n", inet_ntop2(addr_ip), ntohs(addr_port)); close(thread_obj->u.fd); return connect_error; } /* If status = 0, TCP connection to remote host is established. * Otherwise register checker thread to handle connection in progress, * and other error code until connection is established. * Recompute the write timeout (or pending connection). */ if (status != 0) { DBG("TCP connection to [%s:%d] still IN_PROGRESS.\n", inet_ntop2(addr_ip), ntohs(addr_port)); timer_min = timer_sub_now(thread_obj->sands); thread_add_write(thread_obj->master, func, THREAD_ARG(thread_obj) , thread_obj->u.fd, TIMER_LONG(timer_min)); return connect_in_progress; } return connect_success; } void tcp_connection_state(int fd, enum connect_result status, thread * thread_obj, int (*func) (struct _thread *) , long timeout) { switch (status) { case connect_error: close(fd); break; case connect_success: thread_add_write(thread_obj->master, func, THREAD_ARG(thread_obj), fd, timeout); break; /* Checking non-blocking connect, we wait until socket is writable */ case connect_in_progress: thread_add_write(thread_obj->master, func, THREAD_ARG(thread_obj), fd, timeout); break; default: break; } } int tcp_check_thread(thread * thread_obj) { SOCK *sock_obj = THREAD_ARG(thread_obj); int ret = 1; sock_obj->status = tcp_socket_state(thread_obj->u.fd, thread_obj, req->addr_ip, req->addr_port, tcp_check_thread); switch (sock_obj->status) { case connect_error: DBG("Error connecting server [%s:%d].\n", inet_ntop2(req->addr_ip), ntohs(req->addr_port)); thread_add_terminate_event(thread_obj->master); return -1; break; case connect_timeout: DBG("Timeout connecting server [%s:%d].\n", inet_ntop2(req->addr_ip), ntohs(req->addr_port)); thread_add_terminate_event(thread_obj->master); return -1; break; case connect_success:{ if (req->ssl) ret = ssl_connect(thread_obj); if (ret) { /* Remote WEB server is connected. * Unlock eventual locked socket. */ sock_obj->lock = 0; thread_add_event(thread_obj->master, http_request_thread, sock_obj, 0); } else { DBG("Connection trouble to: [%s:%d].\n", inet_ntop2(req->addr_ip), ntohs(req->addr_port)); if (req->ssl) ssl_printerr(SSL_get_error (sock_obj->ssl, ret)); sock_obj->status = connect_error; return -1; } } break; } return 1; } int tcp_connect_thread(thread * thread_obj) { SOCK *sock_obj = THREAD_ARG(thread_obj); if ((sock_obj->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { DBG("WEB connection fail to create socket.\n"); return 0; } sock->status = tcp_connect(sock_obj->fd, req->addr_ip, req->addr_port); /* handle tcp connection status & register check worker thread */ tcp_connection_state(sock_obj->fd, sock_obj->status, thread_obj, tcp_check_thread, HTTP_CNX_TIMEOUT); return 0; }