/* * Soft: Perform a GET query to a remote HTTP/HTTPS server. * Set a timer to compute global remote server response * time. * * Part: HTTP asynchronous engine. * * Version: $Id: http.c,v 1.1.1.1 2005/02/14 15:36:28 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 #include #include "memory.h" #include "http.h" #include "layer4.h" #include "main.h" #include "utils.h" #include "html.h" #include "timer.h" /* * The global design of this checker is the following : * * - All the actions are done asynchronously. * - All the actions handle timeout connection. * - All the actions handle error from low layer to upper * layers. * * The global synopsis of the inter-thread-call is : * * http_request_thread (send SSL GET request) * v * http_response_thread (initialize read stream step) * / \ * / \ * v v * http_read_thread ssl_read_thread (perform HTTP|SSL stream) * v v * ------------------------------ * finalize / epilog */ /* free allocated pieces */ static void free_all(thread * thread_obj) { SOCK *sock_obj = THREAD_ARG(thread_obj); DBG("Total read size read = %d Bytes, fd:%d\n", sock_obj->total_size, sock_obj->fd); if (sock_obj->buffer) FREE(sock_obj->buffer); /* * Decrement the current global get number. * => free the reserved thread */ req->response_time = timer_tol(timer_now()); thread_add_terminate_event(thread_obj->master); } /* Simple epilog functions. */ int epilog(thread * thread_obj) { DBG("Timeout on URL : [%s]\n", req->url); free_all(thread_obj); return 0; } /* Simple finalization function */ int finalize(thread * thread_obj) { SOCK *sock_obj = THREAD_ARG(thread_obj); unsigned char digest[16]; int i; /* Compute final MD5 digest */ MD5_Final(digest, &sock_obj->context); if (req->verbose) { printf("\n"); printf(HTML_MD5); dump_buffer(digest, 16); printf(HTML_MD5_FINAL); } printf("MD5SUM = "); for (i = 0; i < 16; i++) printf("%02x", digest[i]); printf("\n\n"); DBG("Finalize : [%s]\n", req->url); free_all(thread_obj); return 0; } /* Dump HTTP header */ static void http_dump_header(char *buffer, int size) { int r; dump_buffer(buffer, size); printf(HTTP_HEADER_ASCII); for (r = 0; r < size; r++) printf("%c", buffer[r]); printf("\n"); } /* Process incoming stream */ int http_process_stream(SOCK * sock_obj, int r) { sock_obj->size += r; sock_obj->total_size += r; if (!sock_obj->extracted) { if (req->verbose) printf(HTTP_HEADER_HEXA); if ((sock_obj->extracted = extract_html(sock_obj->buffer, sock_obj->size))) { if (req->verbose) http_dump_header(sock_obj->buffer, sock_obj->extracted - sock_obj->buffer); r = sock_obj->size - (sock_obj->extracted - sock_obj->buffer); if (r) { if (req->verbose) { printf(HTML_HEADER_HEXA); dump_buffer(sock_obj->extracted, r); } memmove(sock_obj->buffer, sock_obj->extracted, r); MD5_Update(&sock_obj->context, sock_obj->buffer, r); r = 0; } sock_obj->size = r; } else { if (req->verbose) http_dump_header(sock_obj->buffer, sock_obj->size); /* minimize buffer using no 2*CR/LF found yet */ if (sock_obj->size > 4) { memmove(sock_obj->buffer, sock_obj->buffer + sock_obj->size - 4, 4); sock_obj->size = 4; } } } else if (sock_obj->size) { if (req->verbose) dump_buffer(sock_obj->buffer, r); MD5_Update(&sock_obj->context, sock_obj->buffer, sock_obj->size); sock_obj->size = 0; } return 0; } /* Asynchronous HTTP stream reader */ int http_read_thread(thread * thread_obj) { SOCK *sock_obj = THREAD_ARG(thread_obj); int r = 0; /* Handle read timeout */ if (thread_obj->type == THREAD_READ_TIMEOUT) return epilog(thread_obj); /* read the HTTP stream */ memset(sock_obj->buffer, 0, MAX_BUFFER_LENGTH); r = read(thread_obj->u.fd, sock_obj->buffer + sock_obj->size, MAX_BUFFER_LENGTH - sock_obj->size); DBG(" [l:%d,fd:%d]\n", r, sock_obj->fd); if (r == -1 || r == 0) { /* -1:error , 0:EOF */ if (r == -1) { /* We have encourred a real read error */ DBG("Read error with server [%s:%d]: %s\n", inet_ntop2(req->addr_ip), ntohs(req->addr_port), strerror(errno)); return epilog(thread_obj); } /* All the HTTP stream has been parsed */ finalize(thread_obj); } else { /* Handle the response stream */ http_process_stream(sock_obj, r); /* * Register next http stream reader. * Register itself to not perturbe global I/O multiplexer. */ thread_add_read(thread_obj->master, http_read_thread, sock_obj, thread_obj->u.fd, HTTP_CNX_TIMEOUT); } return 0; } /* * Read get result from the remote web server. * Apply trigger check to this result. */ int http_response_thread(thread * thread_obj) { SOCK *sock_obj = THREAD_ARG(thread_obj); /* Handle read timeout */ if (thread_obj->type == THREAD_READ_TIMEOUT) return epilog(thread_obj); /* Allocate & clean the get buffer */ sock_obj->buffer = (char *) MALLOC(MAX_BUFFER_LENGTH); /* Initalize the MD5 context */ MD5_Init(&sock_obj->context); /* Register asynchronous http/ssl read thread */ if (req->ssl) thread_add_read(thread_obj->master, ssl_read_thread, sock_obj, thread_obj->u.fd, HTTP_CNX_TIMEOUT); else thread_add_read(thread_obj->master, http_read_thread, sock_obj, thread_obj->u.fd, HTTP_CNX_TIMEOUT); return 0; } /* remote Web server is connected, send it the get url query. */ int http_request_thread(thread * thread_obj) { SOCK *sock_obj = THREAD_ARG(thread_obj); char *str_request; int ret = 0; /* Handle read timeout */ if (thread_obj->type == THREAD_WRITE_TIMEOUT) return epilog(thread_obj); /* Allocate & clean the GET string */ str_request = (char *) MALLOC(GET_BUFFER_LENGTH); memset(str_request, 0, GET_BUFFER_LENGTH); snprintf(str_request, GET_BUFFER_LENGTH, REQUEST_TEMPLATE, req->url, (req->vhost) ? req->vhost : inet_ntop2(req->addr_ip) , ntohs(req->addr_port)); /* Send the GET request to remote Web server */ DBG("Sending GET request [%s] on fd:%d\n", req->url, sock_obj->fd); if (req->ssl) ret = ssl_send_request(sock_obj->ssl, str_request, strlen(str_request)); else ret = (send(sock_obj->fd, str_request, strlen(str_request), 0) != -1) ? 1 : 0; FREE(str_request); if (!ret) { fprintf(stderr, "Cannot send get request to [%s:%d].\n", inet_ntop2(req->addr_ip) , ntohs(req->addr_port)); return epilog(thread_obj); } /* Register read timeouted thread */ thread_add_read(thread_obj->master, http_response_thread, sock_obj, sock_obj->fd, HTTP_CNX_TIMEOUT); return 1; }