mirror of
https://github.com/graygnuorg/pound.git
synced 2026-10-28 11:35:07 +03:00
* configure.ac: New subst variables SSL_CPPFLAGS and SSL_LDFLAGS. Avoid modifying CFLAGS directly, unless absolutely necessary. * Makefile.am: Use SSL_CPPFLAGS and SSL_LDFLAGS * config.c (get_subjectaltnames): Remove useless memory allocation. (IMPLEMENT_LHASH_HASH_FN,IMPLEMENT_LHASH_COMP_FN): Not needed for OpenSSL version 1.1 * http.c: Remove declarations of unused variables. * pound.c (l_init, l_id, l_lock): Not used in OpenSSL 1.1. Protect with #if. Remove declarations of unused variables. * pound.h (get_thr_qlen): Use explicit return type. * poundctl.c: Remove declarations of unused variables. * svc.c: Likewise.
1699 lines
64 KiB
C
1699 lines
64 KiB
C
/*
|
|
* Pound - the reverse-proxy load-balancer
|
|
* Copyright (C) 2002-2010 Apsis GmbH
|
|
*
|
|
* This file is part of Pound.
|
|
*
|
|
* Pound 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Pound 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Contact information:
|
|
* Apsis GmbH
|
|
* P.O.Box
|
|
* 8707 Uetikon am See
|
|
* Switzerland
|
|
* EMail: roseg@apsis.ch
|
|
*/
|
|
|
|
#include "pound.h"
|
|
|
|
/* HTTP error replies */
|
|
static char *h500 = "500 Internal Server Error",
|
|
*h501 = "501 Not Implemented",
|
|
*h503 = "503 Service Unavailable",
|
|
*h400 = "Bad Request";
|
|
|
|
static char *err_response = "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\nExpires: now\r\nPragma: no-cache\r\nCache-control: no-cache,no-store\r\n\r\n%s";
|
|
|
|
/*
|
|
* Reply with an error
|
|
*/
|
|
static void
|
|
err_reply(BIO *const c, const char *head, const char *txt)
|
|
{
|
|
BIO_printf(c, err_response, head, strlen(txt), txt);
|
|
BIO_flush(c);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Reply with a redirect
|
|
*/
|
|
static void
|
|
redirect_reply(BIO *const c, const char *url, const int code)
|
|
{
|
|
char rep[MAXBUF], cont[MAXBUF], safe_url[MAXBUF], *code_msg;
|
|
int i, j;
|
|
|
|
switch(code) {
|
|
case 301:
|
|
code_msg = "Moved Permanently";
|
|
break;
|
|
case 307:
|
|
code_msg = "Temporary Redirect";
|
|
break;
|
|
default:
|
|
code_msg = "Found";
|
|
break;
|
|
}
|
|
/*
|
|
* Make sure to return a safe version of the URL (otherwise CSRF becomes a possibility)
|
|
*/
|
|
memset(safe_url, 0, MAXBUF);
|
|
for(i = j = 0; i < MAXBUF && j < MAXBUF && url[i]; i++)
|
|
if(isalnum(url[i]) || url[i] == '_' || url[i] == '.' || url[i] == ':' || url[i] == '/'
|
|
|| url[i] == '?' || url[i] == '&' || url[i] == ';' || url[i] == '-' || url[i] == '=')
|
|
safe_url[j++] = url[i];
|
|
else {
|
|
sprintf(safe_url + j, "%%%02x", url[i]);
|
|
j += 3;
|
|
}
|
|
snprintf(cont, sizeof(cont),
|
|
"<html><head><title>Redirect</title></head><body><h1>Redirect</h1><p>You should go to <a href=\"%s\">%s</a></p></body></html>",
|
|
safe_url, safe_url);
|
|
snprintf(rep, sizeof(rep),
|
|
"HTTP/1.0 %d %s\r\nLocation: %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n",
|
|
code, code_msg, safe_url, (int)strlen(cont));
|
|
BIO_write(c, rep, strlen(rep));
|
|
BIO_write(c, cont, strlen(cont));
|
|
BIO_flush(c);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Read and write some binary data
|
|
*/
|
|
static int
|
|
copy_bin(BIO *const cl, BIO *const be, LONG cont, LONG *res_bytes, const int no_write)
|
|
{
|
|
char buf[MAXBUF];
|
|
int res;
|
|
|
|
while(cont > L0) {
|
|
if((res = BIO_read(cl, buf, cont > MAXBUF? MAXBUF: cont)) < 0)
|
|
return -1;
|
|
else if(res == 0)
|
|
return -2;
|
|
if(!no_write)
|
|
if(BIO_write(be, buf, res) != res)
|
|
return -3;
|
|
cont -= res;
|
|
if(res_bytes)
|
|
*res_bytes += res;
|
|
}
|
|
if(!no_write)
|
|
if(BIO_flush(be) != 1)
|
|
return -4;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get a "line" from a BIO, strip the trailing newline, skip the input stream if buffer too small
|
|
* The result buffer is NULL terminated
|
|
* Return 0 on success
|
|
*/
|
|
static int
|
|
get_line(BIO *const in, char *const buf, const int bufsize)
|
|
{
|
|
char tmp;
|
|
int i, seen_cr;
|
|
|
|
memset(buf, 0, bufsize);
|
|
for(i = 0, seen_cr = 0; i < bufsize - 1; i++)
|
|
switch(BIO_read(in, &tmp, 1)) {
|
|
case -2:
|
|
/* BIO_gets not implemented */
|
|
return -1;
|
|
case 0:
|
|
case -1:
|
|
return 1;
|
|
default:
|
|
if(seen_cr)
|
|
if(tmp != '\n') {
|
|
/* we have CR not followed by NL */
|
|
do {
|
|
if(BIO_read(in, &tmp, 1) < 0)
|
|
return 1;
|
|
} while(tmp != '\n');
|
|
return 1;
|
|
} else {
|
|
buf[i - 1] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
if(!iscntrl(tmp) || tmp == '\t') {
|
|
buf[i] = tmp;
|
|
continue;
|
|
}
|
|
|
|
if(tmp == '\r') {
|
|
seen_cr = 1;
|
|
continue;
|
|
}
|
|
|
|
if(tmp == '\n') {
|
|
/* line ends in NL only (no CR) */
|
|
buf[i] = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* all other control characters cause an error */
|
|
do {
|
|
if(BIO_read(in, &tmp, 1) < 0)
|
|
return 1;
|
|
} while(tmp != '\n');
|
|
return 1;
|
|
}
|
|
|
|
/* line too long */
|
|
do {
|
|
if(BIO_read(in, &tmp, 1) < 0)
|
|
return 1;
|
|
} while(tmp != '\n');
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Strip trailing CRLF
|
|
*/
|
|
static int
|
|
strip_eol(char *lin)
|
|
{
|
|
while(*lin)
|
|
if(*lin == '\n' || (*lin == '\r' && *(lin + 1) == '\n')) {
|
|
*lin = '\0';
|
|
return 1;
|
|
} else
|
|
lin++;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Copy chunked
|
|
*/
|
|
static int
|
|
copy_chunks(BIO *const cl, BIO *const be, LONG *res_bytes, const int no_write, const LONG max_size)
|
|
{
|
|
char buf[MAXBUF];
|
|
LONG cont, tot_size;
|
|
regmatch_t matches[2];
|
|
int res;
|
|
|
|
for(tot_size = 0L;;) {
|
|
if((res = get_line(cl, buf, MAXBUF)) < 0) {
|
|
logmsg(LOG_NOTICE, "(%lx) chunked read error: %s", pthread_self(), strerror(errno));
|
|
return -1;
|
|
} else if(res > 0)
|
|
/* EOF */
|
|
return 0;
|
|
if(!regexec(&CHUNK_HEAD, buf, 2, matches, 0))
|
|
cont = STRTOL(buf, NULL, 16);
|
|
else {
|
|
/* not chunk header */
|
|
logmsg(LOG_NOTICE, "(%lx) bad chunk header <%s>: %s", pthread_self(), buf, strerror(errno));
|
|
return -2;
|
|
}
|
|
if(!no_write)
|
|
if(BIO_printf(be, "%s\r\n", buf) <= 0) {
|
|
logmsg(LOG_NOTICE, "(%lx) error write chunked: %s", pthread_self(), strerror(errno));
|
|
return -3;
|
|
}
|
|
|
|
tot_size += cont;
|
|
if(max_size > L0 && tot_size > max_size) {
|
|
logmsg(LOG_WARNING, "(%lx) chunk content too large", pthread_self);
|
|
return -4;
|
|
}
|
|
|
|
if(cont > L0) {
|
|
if(copy_bin(cl, be, cont, res_bytes, no_write)) {
|
|
if(errno)
|
|
logmsg(LOG_NOTICE, "(%lx) error copy chunk cont: %s", pthread_self(), strerror(errno));
|
|
return -4;
|
|
}
|
|
} else
|
|
break;
|
|
/* final CRLF */
|
|
if((res = get_line(cl, buf, MAXBUF)) < 0) {
|
|
logmsg(LOG_NOTICE, "(%lx) error after chunk: %s", pthread_self(), strerror(errno));
|
|
return -5;
|
|
} else if(res > 0) {
|
|
logmsg(LOG_NOTICE, "(%lx) unexpected EOF after chunk", pthread_self());
|
|
return -5;
|
|
}
|
|
if(buf[0])
|
|
logmsg(LOG_NOTICE, "(%lx) unexpected after chunk \"%s\"", pthread_self(), buf);
|
|
if(!no_write)
|
|
if(BIO_printf(be, "%s\r\n", buf) <= 0) {
|
|
logmsg(LOG_NOTICE, "(%lx) error after chunk write: %s", pthread_self(), strerror(errno));
|
|
return -6;
|
|
}
|
|
}
|
|
/* possibly trailing headers */
|
|
for(;;) {
|
|
if((res = get_line(cl, buf, MAXBUF)) < 0) {
|
|
logmsg(LOG_NOTICE, "(%lx) error post-chunk: %s", pthread_self(), strerror(errno));
|
|
return -7;
|
|
} else if(res > 0)
|
|
break;
|
|
if(!no_write)
|
|
if(BIO_printf(be, "%s\r\n", buf) <= 0) {
|
|
logmsg(LOG_NOTICE, "(%lx) error post-chunk write: %s", pthread_self(), strerror(errno));
|
|
return -8;
|
|
}
|
|
if(!buf[0])
|
|
break;
|
|
}
|
|
if(!no_write)
|
|
if(BIO_flush(be) != 1) {
|
|
logmsg(LOG_NOTICE, "(%lx) copy_chunks flush error: %s", pthread_self(), strerror(errno));
|
|
return -4;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int err_to = -1;
|
|
|
|
typedef struct {
|
|
int timeout;
|
|
RENEG_STATE *reneg_state;
|
|
} BIO_ARG;
|
|
|
|
/*
|
|
* Time-out for client read/gets
|
|
* the SSL manual says not to do it, but it works well enough anyway...
|
|
*/
|
|
static long
|
|
bio_callback(BIO *const bio, const int cmd, const char *argp, int argi, long argl, long ret)
|
|
{
|
|
BIO_ARG *bio_arg;
|
|
struct pollfd p;
|
|
int to, p_res, p_err;
|
|
|
|
if(cmd != BIO_CB_READ && cmd != BIO_CB_WRITE)
|
|
return ret;
|
|
|
|
/* a time-out already occured */
|
|
if((bio_arg = (BIO_ARG*)BIO_get_callback_arg(bio))==NULL)
|
|
return ret;
|
|
if((to = bio_arg->timeout * 1000) < 0) {
|
|
errno = ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
|
|
/* Renegotiations */
|
|
/* logmsg(LOG_NOTICE, "RENEG STATE %d", bio_arg->reneg_state==NULL?-1:*bio_arg->reneg_state); */
|
|
if (bio_arg->reneg_state != NULL && *bio_arg->reneg_state == RENEG_ABORT) {
|
|
logmsg(LOG_NOTICE, "REJECTING renegotiated session");
|
|
errno = ECONNABORTED;
|
|
return -1;
|
|
}
|
|
|
|
if (to == 0)
|
|
return ret;
|
|
|
|
for(;;) {
|
|
memset(&p, 0, sizeof(p));
|
|
BIO_get_fd(bio, &p.fd);
|
|
p.events = (cmd == BIO_CB_READ)? (POLLIN | POLLPRI): POLLOUT;
|
|
p_res = poll(&p, 1, to);
|
|
p_err = errno;
|
|
switch(p_res) {
|
|
case 1:
|
|
if(cmd == BIO_CB_READ) {
|
|
if((p.revents & POLLIN) || (p.revents & POLLPRI))
|
|
/* there is readable data */
|
|
return ret;
|
|
else {
|
|
#ifdef EBUG
|
|
logmsg(LOG_WARNING, "(%lx) CALLBACK read 0x%04x poll: %s",
|
|
pthread_self(), p.revents, strerror(p_err));
|
|
#endif
|
|
errno = EIO;
|
|
}
|
|
} else {
|
|
if(p.revents & POLLOUT)
|
|
/* data can be written */
|
|
return ret;
|
|
else {
|
|
#ifdef EBUG
|
|
logmsg(LOG_WARNING, "(%lx) CALLBACK write 0x%04x poll: %s",
|
|
pthread_self(), p.revents, strerror(p_err));
|
|
#endif
|
|
errno = ECONNRESET;
|
|
}
|
|
}
|
|
return -1;
|
|
case 0:
|
|
/* timeout - mark the BIO as unusable for the future */
|
|
bio_arg->timeout = err_to;
|
|
#ifdef EBUG
|
|
logmsg(LOG_WARNING, "(%lx) CALLBACK timeout poll after %d secs: %s",
|
|
pthread_self(), to / 1000, strerror(p_err));
|
|
#endif
|
|
errno = ETIMEDOUT;
|
|
return 0;
|
|
default:
|
|
/* error */
|
|
if(p_err != EINTR) {
|
|
#ifdef EBUG
|
|
logmsg(LOG_WARNING, "(%lx) CALLBACK bad %d poll: %s",
|
|
pthread_self(), p_res, strerror(p_err));
|
|
#endif
|
|
return -2;
|
|
#ifdef EBUG
|
|
} else
|
|
logmsg(LOG_WARNING, "(%lx) CALLBACK interrupted %d poll: %s",
|
|
pthread_self(), p_res, strerror(p_err));
|
|
#else
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the file underlying a BIO is readable
|
|
*/
|
|
static int
|
|
is_readable(BIO *const bio, const int to_wait)
|
|
{
|
|
struct pollfd p;
|
|
|
|
if(BIO_pending(bio) > 0)
|
|
return 1;
|
|
memset(&p, 0, sizeof(p));
|
|
BIO_get_fd(bio, &p.fd);
|
|
p.events = POLLIN | POLLPRI;
|
|
return (poll(&p, 1, to_wait * 1000) > 0);
|
|
}
|
|
|
|
static void
|
|
free_headers(char **headers)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < MAXHEADERS; i++)
|
|
if(headers[i])
|
|
free(headers[i]);
|
|
free(headers);
|
|
return;
|
|
}
|
|
|
|
static char **
|
|
get_headers(BIO *const in, BIO *const cl, const LISTENER *lstn)
|
|
{
|
|
char **headers, buf[MAXBUF];
|
|
int res, n;
|
|
|
|
/* HTTP/1.1 allows leading CRLF */
|
|
memset(buf, 0, MAXBUF);
|
|
while((res = get_line(in, buf, MAXBUF)) == 0)
|
|
if(buf[0])
|
|
break;
|
|
|
|
if(res < 0) {
|
|
/* this is expected to occur only on client reads */
|
|
/* logmsg(LOG_NOTICE, "headers: bad starting read"); */
|
|
return NULL;
|
|
}
|
|
|
|
if((headers = (char **)calloc(MAXHEADERS, sizeof(char *))) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) e500 headers: out of memory", pthread_self());
|
|
err_reply(cl, h500, lstn->err500);
|
|
return NULL;
|
|
}
|
|
if((headers[0] = (char *)malloc(MAXBUF)) == NULL) {
|
|
free_headers(headers);
|
|
logmsg(LOG_WARNING, "(%lx) e500 header: out of memory", pthread_self());
|
|
err_reply(cl, h500, lstn->err500);
|
|
return NULL;
|
|
}
|
|
memset(headers[0], 0, MAXBUF);
|
|
strncpy(headers[0], buf, MAXBUF - 1);
|
|
|
|
for(n = 1; n < MAXHEADERS; n++) {
|
|
if(get_line(in, buf, MAXBUF)) {
|
|
free_headers(headers);
|
|
/* this is not necessarily an error, EOF/timeout are possible
|
|
logmsg(LOG_WARNING, "(%lx) e500 can't read header", pthread_self());
|
|
err_reply(cl, h500, lstn->err500);
|
|
*/
|
|
return NULL;
|
|
}
|
|
if(!buf[0])
|
|
return headers;
|
|
if((headers[n] = (char *)malloc(MAXBUF)) == NULL) {
|
|
free_headers(headers);
|
|
logmsg(LOG_WARNING, "(%lx) e500 header: out of memory", pthread_self());
|
|
err_reply(cl, h500, lstn->err500);
|
|
return NULL;
|
|
}
|
|
memset(headers[n], 0, MAXBUF);
|
|
strncpy(headers[n], buf, MAXBUF - 1);
|
|
}
|
|
|
|
free_headers(headers);
|
|
logmsg(LOG_NOTICE, "(%lx) e500 too many headers", pthread_self());
|
|
err_reply(cl, h500, lstn->err500);
|
|
return NULL;
|
|
}
|
|
|
|
#define LOG_TIME_SIZE 32
|
|
/*
|
|
* Apache log-file-style time format
|
|
*/
|
|
static void
|
|
log_time(char *res)
|
|
{
|
|
time_t now;
|
|
struct tm *t_now, t_res;
|
|
|
|
now = time(NULL);
|
|
#ifdef HAVE_LOCALTIME_R
|
|
t_now = localtime_r(&now, &t_res);
|
|
#else
|
|
t_now = localtime(&now);
|
|
#endif
|
|
strftime(res, LOG_TIME_SIZE - 1, "%d/%b/%Y:%H:%M:%S %z", t_now);
|
|
return;
|
|
}
|
|
|
|
static double
|
|
cur_time(void)
|
|
{
|
|
#ifdef HAVE_GETTIMEOFDAY
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
int sv_errno;
|
|
|
|
sv_errno = errno;
|
|
gettimeofday(&tv, &tz);
|
|
errno = sv_errno;
|
|
return tv.tv_sec * 1000000.0 + tv.tv_usec;
|
|
#else
|
|
return time(NULL) * 1000000.0;
|
|
#endif
|
|
}
|
|
|
|
#define LOG_BYTES_SIZE 32
|
|
/*
|
|
* Apache log-file-style number format
|
|
*/
|
|
static void
|
|
log_bytes(char *res, const LONG cnt)
|
|
{
|
|
if(cnt > L0)
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
snprintf(res, LOG_BYTES_SIZE - 1, "%lld", cnt);
|
|
#else
|
|
snprintf(res, LOG_BYTES_SIZE - 1, "%ld", cnt);
|
|
#endif
|
|
else
|
|
strcpy(res, "-");
|
|
return;
|
|
}
|
|
|
|
/* Cleanup code. This should really be in the pthread_cleanup_push, except for bugs in some implementations */
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
# define clear_error()
|
|
#elif OPENSSL_VERSION_NUMBER >= 0x10000000L
|
|
# define clear_error() \
|
|
if(ssl != NULL) { ERR_clear_error(); ERR_remove_thread_state(NULL); }
|
|
#else
|
|
# define clear_error() \
|
|
if(ssl != NULL) { ERR_clear_error(); ERR_remove_state(0); }
|
|
#endif
|
|
|
|
#define clean_all() { \
|
|
if(ssl != NULL) { BIO_ssl_shutdown(cl); } \
|
|
if(be != NULL) { BIO_flush(be); BIO_reset(be); BIO_free_all(be); be = NULL; } \
|
|
if(cl != NULL) { BIO_flush(cl); BIO_reset(cl); BIO_free_all(cl); cl = NULL; } \
|
|
if(x509 != NULL) { X509_free(x509); x509 = NULL; } \
|
|
clear_error(); \
|
|
}
|
|
|
|
/*
|
|
* handle an HTTP request
|
|
*/
|
|
void
|
|
do_http(thr_arg *arg)
|
|
{
|
|
int cl_11, be_11, res, chunked, n, sock, no_cont, skip, conn_closed, force_10, sock_proto, is_rpc;
|
|
LISTENER *lstn;
|
|
SERVICE *svc;
|
|
BACKEND *backend, *cur_backend, *old_backend;
|
|
struct addrinfo from_host, z_addr;
|
|
struct sockaddr_storage from_host_addr;
|
|
BIO *cl, *be, *bb, *b64;
|
|
X509 *x509;
|
|
char request[MAXBUF], response[MAXBUF], buf[MAXBUF], url[MAXBUF], loc_path[MAXBUF], **headers,
|
|
headers_ok[MAXHEADERS], v_host[MAXBUF], referer[MAXBUF], u_agent[MAXBUF], u_name[MAXBUF],
|
|
caddr[MAXBUF], req_time[LOG_TIME_SIZE], s_res_bytes[LOG_BYTES_SIZE], *mh;
|
|
SSL *ssl, *be_ssl;
|
|
LONG cont, res_bytes;
|
|
regmatch_t matches[4];
|
|
struct linger l;
|
|
double start_req, end_req;
|
|
RENEG_STATE reneg_state;
|
|
BIO_ARG ba1, ba2;
|
|
|
|
reneg_state = RENEG_INIT;
|
|
ba1.reneg_state = &reneg_state;
|
|
ba2.reneg_state = &reneg_state;
|
|
ba1.timeout = 0;
|
|
ba2.timeout = 0;
|
|
from_host = ((thr_arg *)arg)->from_host;
|
|
memcpy(&from_host_addr, from_host.ai_addr, from_host.ai_addrlen);
|
|
from_host.ai_addr = (struct sockaddr *)&from_host_addr;
|
|
lstn = ((thr_arg *)arg)->lstn;
|
|
sock = ((thr_arg *)arg)->sock;
|
|
free(((thr_arg *)arg)->from_host.ai_addr);
|
|
free(arg);
|
|
|
|
if(lstn->allow_client_reneg)
|
|
reneg_state = RENEG_ALLOW;
|
|
|
|
n = 1;
|
|
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&n, sizeof(n));
|
|
l.l_onoff = 1;
|
|
l.l_linger = 10;
|
|
setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&l, sizeof(l));
|
|
#ifdef TCP_LINGER2
|
|
n = 5;
|
|
setsockopt(sock, SOL_TCP, TCP_LINGER2, (void *)&n, sizeof(n));
|
|
#endif
|
|
n = 1;
|
|
setsockopt(sock, SOL_TCP, TCP_NODELAY, (void *)&n, sizeof(n));
|
|
|
|
cl = NULL;
|
|
be = NULL;
|
|
ssl = NULL;
|
|
x509 = NULL;
|
|
|
|
if((cl = BIO_new_socket(sock, 1)) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) BIO_new_socket failed", pthread_self());
|
|
shutdown(sock, 2);
|
|
close(sock);
|
|
return;
|
|
}
|
|
ba1.timeout = lstn->to;
|
|
BIO_set_callback_arg(cl, (char *)&ba1);
|
|
BIO_set_callback(cl, bio_callback);
|
|
|
|
if(lstn->ctx != NULL) {
|
|
if((ssl = SSL_new(lstn->ctx->ctx)) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) SSL_new: failed", pthread_self());
|
|
BIO_reset(cl);
|
|
BIO_free_all(cl);
|
|
return;
|
|
}
|
|
SSL_set_app_data(ssl, &reneg_state);
|
|
SSL_set_bio(ssl, cl, cl);
|
|
if((bb = BIO_new(BIO_f_ssl())) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) BIO_new(Bio_f_ssl()) failed", pthread_self());
|
|
BIO_reset(cl);
|
|
BIO_free_all(cl);
|
|
return;
|
|
}
|
|
BIO_set_ssl(bb, ssl, BIO_CLOSE);
|
|
BIO_set_ssl_mode(bb, 0);
|
|
cl = bb;
|
|
if(BIO_do_handshake(cl) <= 0) {
|
|
/* no need to log every client without a certificate...
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "BIO_do_handshake with %s failed: %s", caddr,
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
x509 = NULL;
|
|
*/
|
|
BIO_reset(cl);
|
|
BIO_free_all(cl);
|
|
return;
|
|
} else {
|
|
if((x509 = SSL_get_peer_certificate(ssl)) != NULL && lstn->clnt_check < 3
|
|
&& SSL_get_verify_result(ssl) != X509_V_OK) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "Bad certificate from %s", caddr);
|
|
X509_free(x509);
|
|
BIO_reset(cl);
|
|
BIO_free_all(cl);
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
x509 = NULL;
|
|
}
|
|
cur_backend = NULL;
|
|
|
|
if((bb = BIO_new(BIO_f_buffer())) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) BIO_new(buffer) failed", pthread_self());
|
|
if(x509 != NULL)
|
|
X509_free(x509);
|
|
BIO_reset(cl);
|
|
BIO_free_all(cl);
|
|
return;
|
|
}
|
|
BIO_set_close(cl, BIO_CLOSE);
|
|
BIO_set_buffer_size(cl, MAXBUF);
|
|
cl = BIO_push(bb, cl);
|
|
|
|
for(cl_11 = be_11 = 0;;) {
|
|
res_bytes = L0;
|
|
is_rpc = -1;
|
|
v_host[0] = referer[0] = u_agent[0] = u_name[0] = '\0';
|
|
conn_closed = 0;
|
|
for(n = 0; n < MAXHEADERS; n++)
|
|
headers_ok[n] = 1;
|
|
if((headers = get_headers(cl, cl, lstn)) == NULL) {
|
|
if(!cl_11) {
|
|
if(errno) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) error read from %s: %s", pthread_self(), caddr, strerror(errno));
|
|
/* err_reply(cl, h500, lstn->err500); */
|
|
}
|
|
}
|
|
clean_all();
|
|
return;
|
|
}
|
|
memset(req_time, 0, LOG_TIME_SIZE);
|
|
start_req = cur_time();
|
|
log_time(req_time);
|
|
|
|
/* check for correct request */
|
|
strncpy(request, headers[0], MAXBUF);
|
|
if(!regexec(&lstn->verb, request, 3, matches, 0)) {
|
|
no_cont = !strncasecmp(request + matches[1].rm_so, "HEAD", matches[1].rm_eo - matches[1].rm_so);
|
|
if(!strncasecmp(request + matches[1].rm_so, "RPC_IN_DATA", matches[1].rm_eo - matches[1].rm_so))
|
|
is_rpc = 1;
|
|
else if(!strncasecmp(request + matches[1].rm_so, "RPC_OUT_DATA", matches[1].rm_eo - matches[1].rm_so))
|
|
is_rpc = 0;
|
|
} else {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_WARNING, "(%lx) e501 bad request \"%s\" from %s", pthread_self(), request, caddr);
|
|
err_reply(cl, h501, lstn->err501);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
cl_11 = (request[strlen(request) - 1] == '1');
|
|
n = cpURL(url, request + matches[2].rm_so, matches[2].rm_eo - matches[2].rm_so);
|
|
if(n != strlen(url)) {
|
|
/* the URL probably contained a %00 aka NULL - which we don't allow */
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e501 URL \"%s\" (contains NULL) from %s", pthread_self(), url, caddr);
|
|
err_reply(cl, h501, lstn->err501);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
if(lstn->has_pat && regexec(&lstn->url_pat, url, 0, NULL, 0)) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e501 bad URL \"%s\" from %s", pthread_self(), url, caddr);
|
|
err_reply(cl, h501, lstn->err501);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
/* check other headers */
|
|
for(chunked = 0, cont = L_1, n = 1; n < MAXHEADERS && headers[n]; n++) {
|
|
/* no overflow - see check_header for details */
|
|
switch(check_header(headers[n], buf)) {
|
|
case HEADER_HOST:
|
|
strcpy(v_host, buf);
|
|
break;
|
|
case HEADER_REFERER:
|
|
strcpy(referer, buf);
|
|
break;
|
|
case HEADER_USER_AGENT:
|
|
strcpy(u_agent, buf);
|
|
break;
|
|
case HEADER_CONNECTION:
|
|
if(!strcasecmp("close", buf))
|
|
conn_closed = 1;
|
|
break;
|
|
case HEADER_TRANSFER_ENCODING:
|
|
if(!strcasecmp("chunked", buf))
|
|
chunked = 1;
|
|
else {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e400 multiple Transfer-encoding \"%s\" from %s", pthread_self(), url, caddr);
|
|
err_reply(cl, h400, "Bad request: multiple Transfer-encoding values");
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
break;
|
|
case HEADER_CONTENT_LENGTH:
|
|
if(cont != L_1 || strchr(buf, ',')) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e400 multiple Content-length \"%s\" from %s", pthread_self(), url, caddr);
|
|
err_reply(cl, h400, "Bad request: multiple Content-length values");
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
for(mh = buf; *mh; mh++)
|
|
if(!isdigit(*mh)) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e400 Content-length bad value \"%s\" from %s", pthread_self(), url, caddr);
|
|
err_reply(cl, h400, "Bad request: Content-length bad value");
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
if((cont = ATOL(buf)) < 0L)
|
|
headers_ok[n] = 0;
|
|
if(is_rpc == 1 && (cont < 0x20000L || cont > 0x80000000L))
|
|
is_rpc = -1;
|
|
break;
|
|
case HEADER_EXPECT:
|
|
/*
|
|
* we do NOT support the "Expect: 100-continue" headers
|
|
* support may involve severe performance penalties (non-responding back-end, etc)
|
|
* as a stop-gap measure we just skip these headers
|
|
*/
|
|
if(!strcasecmp("100-continue", buf))
|
|
headers_ok[n] = 0;
|
|
break;
|
|
case HEADER_ILLEGAL:
|
|
if(lstn->log_level > 0) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) bad header from %s (%s)", pthread_self(), caddr, headers[n]);
|
|
}
|
|
headers_ok[n] = 0;
|
|
break;
|
|
}
|
|
if(headers_ok[n] && lstn->head_off) {
|
|
/* maybe header to be removed */
|
|
MATCHER *m;
|
|
|
|
for(m = lstn->head_off; m; m = m->next)
|
|
if(!(headers_ok[n] = regexec(&m->pat, headers[n], 0, NULL, 0)))
|
|
break;
|
|
}
|
|
/* get User name */
|
|
if(!regexec(&AUTHORIZATION, headers[n], 2, matches, 0)) {
|
|
int inlen;
|
|
|
|
if((bb = BIO_new(BIO_s_mem())) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) Can't alloc BIO_s_mem", pthread_self());
|
|
continue;
|
|
}
|
|
if((b64 = BIO_new(BIO_f_base64())) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) Can't alloc BIO_f_base64", pthread_self());
|
|
BIO_free(bb);
|
|
continue;
|
|
}
|
|
b64 = BIO_push(b64, bb);
|
|
BIO_write(bb, headers[n] + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
|
|
BIO_write(bb, "\n", 1);
|
|
if((inlen = BIO_read(b64, buf, MAXBUF - 1)) <= 0) {
|
|
logmsg(LOG_WARNING, "(%lx) Can't read BIO_f_base64", pthread_self());
|
|
BIO_free_all(b64);
|
|
continue;
|
|
}
|
|
BIO_free_all(b64);
|
|
if((mh = strchr(buf, ':')) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) Unknown authentication", pthread_self());
|
|
continue;
|
|
}
|
|
*mh = '\0';
|
|
strcpy(u_name, buf);
|
|
}
|
|
}
|
|
|
|
/* check for possible request smuggling attempt */
|
|
if(chunked != 0 && cont != L_1) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e501 Transfer-encoding and Content-length \"%s\" from %s", pthread_self(), url, caddr);
|
|
err_reply(cl, h400, "Bad request: Transfer-encoding and Content-length headers present");
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
/* possibly limited request size */
|
|
if(lstn->max_req > L0 && cont > L0 && cont > lstn->max_req && is_rpc != 1) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e501 request too large (%ld) from %s", pthread_self(), cont, caddr);
|
|
err_reply(cl, h501, lstn->err501);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
if(be != NULL) {
|
|
if(is_readable(be, 0)) {
|
|
/* The only way it's readable is if it's at EOF, so close it! */
|
|
BIO_reset(be);
|
|
BIO_free_all(be);
|
|
be = NULL;
|
|
}
|
|
}
|
|
|
|
/* check that the requested URL still fits the old back-end (if any) */
|
|
if((svc = get_service(lstn, url, &headers[1])) == NULL) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e503 no service \"%s\" from %s %s", pthread_self(), request, caddr, v_host[0]? v_host: "-");
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
if((backend = get_backend(svc, &from_host, url, &headers[1])) == NULL) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e503 no back-end \"%s\" from %s %s", pthread_self(), request, caddr, v_host[0]? v_host: "-");
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
if(be != NULL && backend != cur_backend) {
|
|
BIO_reset(be);
|
|
BIO_free_all(be);
|
|
be = NULL;
|
|
}
|
|
while(be == NULL && backend->be_type == 0) {
|
|
switch(backend->addr.ai_family) {
|
|
case AF_INET:
|
|
sock_proto = PF_INET;
|
|
break;
|
|
case AF_INET6:
|
|
sock_proto = PF_INET6;
|
|
break;
|
|
case AF_UNIX:
|
|
sock_proto = PF_UNIX;
|
|
break;
|
|
default:
|
|
logmsg(LOG_WARNING, "(%lx) e503 backend: unknown family %d", pthread_self(), backend->addr.ai_family);
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
if((sock = socket(sock_proto, SOCK_STREAM, 0)) < 0) {
|
|
str_be(buf, MAXBUF - 1, backend);
|
|
logmsg(LOG_WARNING, "(%lx) e503 backend %s socket create: %s", pthread_self(), buf, strerror(errno));
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
if(connect_nb(sock, &backend->addr, backend->conn_to) < 0) {
|
|
str_be(buf, MAXBUF - 1, backend);
|
|
logmsg(LOG_WARNING, "(%lx) backend %s connect: %s", pthread_self(), buf, strerror(errno));
|
|
shutdown(sock, 2);
|
|
close(sock);
|
|
/*
|
|
* kill the back-end only if no HAport is defined for it
|
|
* otherwise allow the HAport mechanism to do its job
|
|
*/
|
|
memset(&z_addr, 0, sizeof(z_addr));
|
|
if(memcmp(&(backend->ha_addr), &(z_addr), sizeof(z_addr)) == 0)
|
|
kill_be(svc, backend, BE_KILL);
|
|
/*
|
|
* ...but make sure we don't get into a loop with the same back-end
|
|
*/
|
|
old_backend = backend;
|
|
if((backend = get_backend(svc, &from_host, url, &headers[1])) == NULL || backend == old_backend) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e503 no back-end \"%s\" from %s", pthread_self(), request, caddr);
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
continue;
|
|
}
|
|
if(sock_proto == PF_INET || sock_proto == PF_INET6) {
|
|
n = 1;
|
|
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&n, sizeof(n));
|
|
l.l_onoff = 1;
|
|
l.l_linger = 10;
|
|
setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&l, sizeof(l));
|
|
#ifdef TCP_LINGER2
|
|
n = 5;
|
|
setsockopt(sock, SOL_TCP, TCP_LINGER2, (void *)&n, sizeof(n));
|
|
#endif
|
|
n = 1;
|
|
setsockopt(sock, SOL_TCP, TCP_NODELAY, (void *)&n, sizeof(n));
|
|
}
|
|
if((be = BIO_new_socket(sock, 1)) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) e503 BIO_new_socket server failed", pthread_self());
|
|
shutdown(sock, 2);
|
|
close(sock);
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
BIO_set_close(be, BIO_CLOSE);
|
|
if(backend->to > 0) {
|
|
ba2.timeout = backend->to;
|
|
BIO_set_callback_arg(be, (char *)&ba2);
|
|
BIO_set_callback(be, bio_callback);
|
|
}
|
|
if(backend->ctx != NULL) {
|
|
if((be_ssl = SSL_new(backend->ctx)) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) be SSL_new: failed", pthread_self());
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
SSL_set_bio(be_ssl, be, be);
|
|
if((bb = BIO_new(BIO_f_ssl())) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) BIO_new(Bio_f_ssl()) failed", pthread_self());
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
BIO_set_ssl(bb, be_ssl, BIO_CLOSE);
|
|
BIO_set_ssl_mode(bb, 1);
|
|
be = bb;
|
|
if(BIO_do_handshake(be) <= 0) {
|
|
str_be(buf, MAXBUF - 1, backend);
|
|
logmsg(LOG_NOTICE, "BIO_do_handshake with %s failed: %s", buf,
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
if((bb = BIO_new(BIO_f_buffer())) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) e503 BIO_new(buffer) server failed", pthread_self());
|
|
err_reply(cl, h503, lstn->err503);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
BIO_set_buffer_size(bb, MAXBUF);
|
|
BIO_set_close(bb, BIO_CLOSE);
|
|
be = BIO_push(bb, be);
|
|
}
|
|
cur_backend = backend;
|
|
|
|
/* if we have anything but a BACK_END we close the channel */
|
|
if(be != NULL && cur_backend->be_type) {
|
|
BIO_reset(be);
|
|
BIO_free_all(be);
|
|
be = NULL;
|
|
}
|
|
|
|
/* send the request */
|
|
if(cur_backend->be_type == 0) {
|
|
for(n = 0; n < MAXHEADERS && headers[n]; n++) {
|
|
if(!headers_ok[n])
|
|
continue;
|
|
/* this is the earliest we can check for Destination - we had no back-end before */
|
|
if(lstn->rewr_dest && check_header(headers[n], buf) == HEADER_DESTINATION) {
|
|
if(regexec(&LOCATION, buf, 4, matches, 0)) {
|
|
logmsg(LOG_NOTICE, "(%lx) Can't parse Destination %s", pthread_self(), buf);
|
|
break;
|
|
}
|
|
str_be(caddr, MAXBUF - 1, cur_backend);
|
|
strcpy(loc_path, buf + matches[3].rm_so);
|
|
snprintf(buf, MAXBUF, "Destination: http://%s%s", caddr, loc_path);
|
|
free(headers[n]);
|
|
if((headers[n] = strdup(buf)) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) rewrite Destination - out of memory: %s",
|
|
pthread_self(), strerror(errno));
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
if(BIO_printf(be, "%s\r\n", headers[n]) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write to %s/%s: %s (%.3f sec)",
|
|
pthread_self(), buf, request, strerror(errno),
|
|
(end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
/* add header if required */
|
|
if(lstn->add_head != NULL)
|
|
if(BIO_printf(be, "%s\r\n", lstn->add_head) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write AddHeader to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
free_headers(headers);
|
|
|
|
/* if SSL put additional headers for client certificate */
|
|
if(cur_backend->be_type == 0 && ssl != NULL) {
|
|
const SSL_CIPHER *cipher;
|
|
|
|
if((cipher = SSL_get_current_cipher(ssl)) != NULL) {
|
|
SSL_CIPHER_description(cipher, buf, MAXBUF - 1);
|
|
strip_eol(buf);
|
|
if(BIO_printf(be, "X-SSL-cipher: %s/%s\r\n", SSL_get_version(ssl), buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-cipher to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(lstn->clnt_check > 0 && x509 != NULL && (bb = BIO_new(BIO_s_mem())) != NULL) {
|
|
X509_NAME_print_ex(bb, X509_get_subject_name(x509), 8, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
|
|
get_line(bb, buf, MAXBUF);
|
|
if(BIO_printf(be, "X-SSL-Subject: %s\r\n", buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-Subject to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
X509_NAME_print_ex(bb, X509_get_issuer_name(x509), 8, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
|
|
get_line(bb, buf, MAXBUF);
|
|
if(BIO_printf(be, "X-SSL-Issuer: %s\r\n", buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-Issuer to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
ASN1_TIME_print(bb, X509_get_notBefore(x509));
|
|
get_line(bb, buf, MAXBUF);
|
|
if(BIO_printf(be, "X-SSL-notBefore: %s\r\n", buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-notBefore to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
ASN1_TIME_print(bb, X509_get_notAfter(x509));
|
|
get_line(bb, buf, MAXBUF);
|
|
if(BIO_printf(be, "X-SSL-notAfter: %s\r\n", buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-notAfter to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
if(BIO_printf(be, "X-SSL-serial: %ld\r\n", ASN1_INTEGER_get(X509_get_serialNumber(x509))) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-serial to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
#if CERT1L
|
|
PEM_write_bio_X509(bb, x509);
|
|
get_line(bb, buf, MAXBUF);
|
|
if(BIO_printf(be, "X-SSL-certificate: %s", buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
while(get_line(bb, buf, MAXBUF) == 0) {
|
|
if(BIO_printf(be, "%s", buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
if(BIO_printf(be, "%s\r\n", buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
#else
|
|
PEM_write_bio_X509(bb, x509);
|
|
get_line(bb, buf, MAXBUF);
|
|
if(BIO_printf(be, "X-SSL-certificate: %s\r\n", buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
while(get_line(bb, buf, MAXBUF) == 0) {
|
|
if(BIO_printf(be, "\t%s\r\n", buf) <= 0) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)",
|
|
pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
BIO_free_all(bb);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
BIO_free_all(bb);
|
|
}
|
|
}
|
|
/* put additional client IP header */
|
|
if(cur_backend->be_type == 0) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
BIO_printf(be, "X-Forwarded-For: %s\r\n", caddr);
|
|
|
|
/* final CRLF */
|
|
BIO_puts(be, "\r\n");
|
|
}
|
|
|
|
if(cl_11 && chunked) {
|
|
/* had Transfer-encoding: chunked so read/write all the chunks (HTTP/1.1 only) */
|
|
if(copy_chunks(cl, be, NULL, cur_backend->be_type, lstn->max_req)) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e500 for %s copy_chunks to %s/%s (%.3f sec)",
|
|
pthread_self(), caddr, buf, request, (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
clean_all();
|
|
return;
|
|
}
|
|
} else if(cont > L0 && is_rpc != 1) {
|
|
/* had Content-length, so do raw reads/writes for the length */
|
|
if(copy_bin(cl, be, cont, NULL, cur_backend->be_type)) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e500 for %s error copy client cont to %s/%s: %s (%.3f sec)",
|
|
pthread_self(), caddr, buf, request, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
clean_all();
|
|
return;
|
|
}
|
|
} else if(cont > 0L && is_readable(cl, lstn->to)) {
|
|
char one;
|
|
BIO *cl_unbuf;
|
|
/*
|
|
* special mode for RPC_IN_DATA - content until EOF
|
|
* force HTTP/1.0 - client closes connection when done.
|
|
*/
|
|
cl_11 = be_11 = 0;
|
|
|
|
/*
|
|
* first read whatever is already in the input buffer
|
|
*/
|
|
while(BIO_pending(cl)) {
|
|
if(BIO_read(cl, &one, 1) != 1) {
|
|
logmsg(LOG_NOTICE, "(%lx) error read request pending: %s",
|
|
pthread_self(), strerror(errno));
|
|
clean_all();
|
|
pthread_exit(NULL);
|
|
}
|
|
if (++res_bytes > cont) {
|
|
logmsg(LOG_NOTICE, "(%lx) error read request pending: max. RPC length exceeded",
|
|
pthread_self());
|
|
clean_all();
|
|
pthread_exit(NULL);
|
|
}
|
|
if(BIO_write(be, &one, 1) != 1) {
|
|
if(errno)
|
|
logmsg(LOG_NOTICE, "(%lx) error write request pending: %s",
|
|
pthread_self(), strerror(errno));
|
|
clean_all();
|
|
pthread_exit(NULL);
|
|
}
|
|
}
|
|
BIO_flush(be);
|
|
|
|
/*
|
|
* find the socket BIO in the chain
|
|
*/
|
|
if ((cl_unbuf = BIO_find_type(cl, lstn->ctx? BIO_TYPE_SSL : BIO_TYPE_SOCKET)) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) error get unbuffered: %s", pthread_self(), strerror(errno));
|
|
clean_all();
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
/*
|
|
* copy till EOF
|
|
*/
|
|
while((res = BIO_read(cl_unbuf, buf, MAXBUF)) > 0) {
|
|
if((res_bytes += res) > cont) {
|
|
logmsg(LOG_NOTICE, "(%lx) error copy request body: max. RPC length exceeded",
|
|
pthread_self());
|
|
clean_all();
|
|
pthread_exit(NULL);
|
|
}
|
|
if(BIO_write(be, buf, res) != res) {
|
|
if(errno)
|
|
logmsg(LOG_NOTICE, "(%lx) error copy request body: %s",
|
|
pthread_self(), strerror(errno));
|
|
clean_all();
|
|
pthread_exit(NULL);
|
|
} else {
|
|
BIO_flush(be);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* flush to the back-end */
|
|
if(cur_backend->be_type == 0 && BIO_flush(be) != 1) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e500 for %s error flush to %s/%s: %s (%.3f sec)",
|
|
pthread_self(), caddr, buf, request, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* check on no_https_11:
|
|
* - if 0 ignore
|
|
* - if 1 and SSL force HTTP/1.0
|
|
* - if 2 and SSL and MSIE force HTTP/1.0
|
|
*/
|
|
switch(lstn->noHTTPS11) {
|
|
case 1:
|
|
force_10 = (ssl != NULL);
|
|
break;
|
|
case 2:
|
|
force_10 = (ssl != NULL && strstr(u_agent, "MSIE") != NULL);
|
|
break;
|
|
default:
|
|
force_10 = 0;
|
|
break;
|
|
}
|
|
|
|
/* if we have a redirector */
|
|
if(cur_backend->be_type) {
|
|
memset(buf, 0, sizeof(buf));
|
|
if(!cur_backend->redir_req)
|
|
snprintf(buf, sizeof(buf) - 1, "%s%s", cur_backend->url, url);
|
|
else
|
|
strncpy(buf, cur_backend->url, sizeof(buf) - 1);
|
|
redirect_reply(cl, buf, cur_backend->be_type);
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
switch(lstn->log_level) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
logmsg(LOG_INFO, "%s %s - REDIRECT %s", caddr, request, buf);
|
|
break;
|
|
case 3:
|
|
if(v_host[0])
|
|
logmsg(LOG_INFO, "%s %s - %s [%s] \"%s\" %d 0 \"%s\" \"%s\"", v_host, caddr,
|
|
u_name[0]? u_name: "-", req_time, request, cur_backend->be_type, referer, u_agent);
|
|
else
|
|
logmsg(LOG_INFO, "%s - %s [%s] \"%s\" %d 0 \"%s\" \"%s\"", caddr,
|
|
u_name[0]? u_name: "-", req_time, request, cur_backend->be_type, referer, u_agent);
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
logmsg(LOG_INFO, "%s - %s [%s] \"%s\" %d 0 \"%s\" \"%s\"", caddr,
|
|
u_name[0]? u_name: "-", req_time, request, cur_backend->be_type, referer, u_agent);
|
|
break;
|
|
}
|
|
if(!cl_11 || conn_closed || force_10)
|
|
break;
|
|
continue;
|
|
} else if(is_rpc == 1) {
|
|
/* log RPC_IN_DATA */
|
|
end_req = cur_time();
|
|
memset(s_res_bytes, 0, LOG_BYTES_SIZE);
|
|
/* actual request length */
|
|
log_bytes(s_res_bytes, res_bytes);
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
switch(lstn->log_level) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
logmsg(LOG_INFO, "%s %s - -", caddr, request);
|
|
break;
|
|
case 2:
|
|
if(v_host[0])
|
|
logmsg(LOG_INFO, "%s %s - - (%s/%s -> %s) %.3f sec",
|
|
caddr, request, v_host, svc->name[0]? svc->name: "-", buf,
|
|
(end_req - start_req) / 1000000.0);
|
|
else
|
|
logmsg(LOG_INFO, "%s %s - - (%s -> %s) %.3f sec",
|
|
caddr, request, svc->name[0]? svc->name: "-", buf,
|
|
(end_req - start_req) / 1000000.0);
|
|
break;
|
|
case 3:
|
|
logmsg(LOG_INFO, "%s %s - %s [%s] \"%s\" 000 %s \"%s\" \"%s\"",
|
|
v_host[0]? v_host: "-",
|
|
caddr, u_name[0]? u_name: "-", req_time, request,
|
|
s_res_bytes, referer, u_agent);
|
|
break;
|
|
case 4:
|
|
logmsg(LOG_INFO, "%s - %s [%s] \"%s\" 000 %s \"%s\" \"%s\"",
|
|
caddr, u_name[0]? u_name: "-", req_time, request,
|
|
s_res_bytes, referer, u_agent);
|
|
break;
|
|
case 5:
|
|
logmsg(LOG_INFO, "%s %s - %s [%s] \"%s\" 000 %s \"%s\" \"%s\" (%s -> %s) %.3f sec",
|
|
v_host[0]? v_host: "-",
|
|
caddr, u_name[0]? u_name: "-", req_time, request,
|
|
s_res_bytes, referer, u_agent, svc->name[0]? svc->name: "-", buf,
|
|
(end_req - start_req) / 1000000.0);
|
|
break;
|
|
}
|
|
/* no response expected - bail out */
|
|
break;
|
|
}
|
|
|
|
/* get the response */
|
|
for(skip = 1; skip;) {
|
|
if((headers = get_headers(be, cl, lstn)) == NULL) {
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
end_req = cur_time();
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) e500 for %s response error read from %s/%s: %s (%.3f secs)",
|
|
pthread_self(), caddr, buf, request, strerror(errno), (end_req - start_req) / 1000000.0);
|
|
err_reply(cl, h500, lstn->err500);
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
strncpy(response, headers[0], MAXBUF);
|
|
be_11 = (response[7] == '1');
|
|
/* responses with code 100 are never passed back to the client */
|
|
skip = !regexec(&RESP_SKIP, response, 0, NULL, 0);
|
|
/* some response codes (1xx, 204, 304) have no content */
|
|
if(!no_cont && !regexec(&RESP_IGN, response, 0, NULL, 0))
|
|
no_cont = 1;
|
|
|
|
for(chunked = 0, cont = -1L, n = 1; n < MAXHEADERS && headers[n]; n++) {
|
|
switch(check_header(headers[n], buf)) {
|
|
case HEADER_CONNECTION:
|
|
if(!strcasecmp("close", buf))
|
|
conn_closed = 1;
|
|
break;
|
|
case HEADER_TRANSFER_ENCODING:
|
|
if(!strcasecmp("chunked", buf)) {
|
|
chunked = 1;
|
|
no_cont = 0;
|
|
}
|
|
break;
|
|
case HEADER_CONTENT_LENGTH:
|
|
cont = ATOL(buf);
|
|
/* treat RPC_OUT_DATA like reply without content-length */
|
|
if(is_rpc == 0) {
|
|
if(cont >= 0x20000L && cont <= 0x80000000L)
|
|
cont = -1L;
|
|
else
|
|
is_rpc = -1;
|
|
}
|
|
break;
|
|
case HEADER_LOCATION:
|
|
if(v_host[0] && need_rewrite(lstn->rewr_loc, buf, loc_path, v_host, lstn, cur_backend)) {
|
|
snprintf(buf, MAXBUF, "Location: %s://%s/%s",
|
|
(ssl == NULL? "http": "https"), v_host, loc_path);
|
|
free(headers[n]);
|
|
if((headers[n] = strdup(buf)) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) rewrite Location - out of memory: %s",
|
|
pthread_self(), strerror(errno));
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case HEADER_CONTLOCATION:
|
|
if(v_host[0] && need_rewrite(lstn->rewr_loc, buf, loc_path, v_host, lstn, cur_backend)) {
|
|
snprintf(buf, MAXBUF, "Content-location: %s://%s/%s",
|
|
(ssl == NULL? "http": "https"), v_host, loc_path);
|
|
free(headers[n]);
|
|
if((headers[n] = strdup(buf)) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) rewrite Content-location - out of memory: %s",
|
|
pthread_self(), strerror(errno));
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* possibly record session information (only for cookies/header) */
|
|
upd_session(svc, &headers[1], cur_backend);
|
|
|
|
/* send the response */
|
|
if(!skip)
|
|
for(n = 0; n < MAXHEADERS && headers[n]; n++) {
|
|
if(BIO_printf(cl, "%s\r\n", headers[n]) <= 0) {
|
|
if(errno) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) error write to %s: %s", pthread_self(), caddr, strerror(errno));
|
|
}
|
|
free_headers(headers);
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
free_headers(headers);
|
|
|
|
/* final CRLF */
|
|
if(!skip)
|
|
BIO_puts(cl, "\r\n");
|
|
if(BIO_flush(cl) != 1) {
|
|
if(errno) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) error flush headers to %s: %s", pthread_self(), caddr, strerror(errno));
|
|
}
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
if(!no_cont) {
|
|
/* ignore this if request was HEAD or similar */
|
|
if(be_11 && chunked) {
|
|
/* had Transfer-encoding: chunked so read/write all the chunks (HTTP/1.1 only) */
|
|
if(copy_chunks(be, cl, &res_bytes, skip, L0)) {
|
|
/* copy_chunks() has its own error messages */
|
|
clean_all();
|
|
return;
|
|
}
|
|
} else if(cont >= L0) {
|
|
/* may have had Content-length, so do raw reads/writes for the length */
|
|
if(copy_bin(be, cl, cont, &res_bytes, skip)) {
|
|
if(errno)
|
|
logmsg(LOG_NOTICE, "(%lx) error copy server cont: %s", pthread_self(), strerror(errno));
|
|
clean_all();
|
|
return;
|
|
}
|
|
} else if(!skip) {
|
|
if(is_readable(be, cur_backend->to)) {
|
|
char one;
|
|
BIO *be_unbuf;
|
|
/*
|
|
* old-style response - content until EOF
|
|
* also implies the client may not use HTTP/1.1
|
|
*/
|
|
cl_11 = be_11 = 0;
|
|
|
|
/*
|
|
* first read whatever is already in the input buffer
|
|
*/
|
|
while(BIO_pending(be)) {
|
|
if(BIO_read(be, &one, 1) != 1) {
|
|
logmsg(LOG_NOTICE, "(%lx) error read response pending: %s",
|
|
pthread_self(), strerror(errno));
|
|
clean_all();
|
|
return;
|
|
}
|
|
if(BIO_write(cl, &one, 1) != 1) {
|
|
if(errno)
|
|
logmsg(LOG_NOTICE, "(%lx) error write response pending: %s",
|
|
pthread_self(), strerror(errno));
|
|
clean_all();
|
|
return;
|
|
}
|
|
res_bytes++;
|
|
}
|
|
BIO_flush(cl);
|
|
|
|
/*
|
|
* find the socket BIO in the chain
|
|
*/
|
|
if((be_unbuf = BIO_find_type(be, cur_backend->ctx? BIO_TYPE_SSL : BIO_TYPE_SOCKET)) == NULL) {
|
|
logmsg(LOG_WARNING, "(%lx) error get unbuffered: %s", pthread_self(), strerror(errno));
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* copy till EOF
|
|
*/
|
|
while((res = BIO_read(be_unbuf, buf, MAXBUF)) > 0) {
|
|
if(BIO_write(cl, buf, res) != res) {
|
|
if(errno)
|
|
logmsg(LOG_NOTICE, "(%lx) error copy response body: %s",
|
|
pthread_self(), strerror(errno));
|
|
clean_all();
|
|
return;
|
|
} else {
|
|
res_bytes += res;
|
|
BIO_flush(cl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(BIO_flush(cl) != 1) {
|
|
/* client closes RPC_OUT_DATA connection - no error */
|
|
if(is_rpc == 0 && res_bytes > 0L)
|
|
break;
|
|
if(errno) {
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
logmsg(LOG_NOTICE, "(%lx) error final flush to %s: %s", pthread_self(), caddr, strerror(errno));
|
|
}
|
|
clean_all();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
end_req = cur_time();
|
|
|
|
/* log what happened */
|
|
memset(s_res_bytes, 0, LOG_BYTES_SIZE);
|
|
log_bytes(s_res_bytes, res_bytes);
|
|
addr2str(caddr, MAXBUF - 1, &from_host, 1);
|
|
if(anonymise) {
|
|
char *last;
|
|
|
|
if((last = strrchr(caddr, '.')) != NULL || (last = strrchr(caddr, ':')) != NULL)
|
|
strcpy(++last, "0");
|
|
}
|
|
str_be(buf, MAXBUF - 1, cur_backend);
|
|
switch(lstn->log_level) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
logmsg(LOG_INFO, "%s %s - %s", caddr, request, response);
|
|
break;
|
|
case 2:
|
|
if(v_host[0])
|
|
logmsg(LOG_INFO, "%s %s - %s (%s/%s -> %s) %.3f sec",
|
|
caddr, request, response, v_host, svc->name[0]? svc->name: "-", buf,
|
|
(end_req - start_req) / 1000000.0);
|
|
else
|
|
logmsg(LOG_INFO, "%s %s - %s (%s -> %s) %.3f sec",
|
|
caddr, request, response, svc->name[0]? svc->name: "-", buf,
|
|
(end_req - start_req) / 1000000.0);
|
|
break;
|
|
case 3:
|
|
logmsg(LOG_INFO, "%s %s - %s [%s] \"%s\" %c%c%c %s \"%s\" \"%s\"",
|
|
v_host[0]? v_host: "-",
|
|
caddr, u_name[0]? u_name: "-", req_time, request, response[9],
|
|
response[10], response[11], s_res_bytes, referer, u_agent);
|
|
break;
|
|
case 4:
|
|
logmsg(LOG_INFO, "%s - %s [%s] \"%s\" %c%c%c %s \"%s\" \"%s\"",
|
|
caddr, u_name[0]? u_name: "-", req_time, request, response[9], response[10],
|
|
response[11], s_res_bytes, referer, u_agent);
|
|
break;
|
|
case 5:
|
|
logmsg(LOG_INFO, "%s %s - %s [%s] \"%s\" %c%c%c %s \"%s\" \"%s\" (%s -> %s) %.3f sec",
|
|
v_host[0]? v_host: "-",
|
|
caddr, u_name[0]? u_name: "-", req_time, request, response[9], response[10],
|
|
response[11], s_res_bytes, referer, u_agent, svc->name[0]? svc->name: "-", buf,
|
|
(end_req - start_req) / 1000000.0);
|
|
break;
|
|
}
|
|
|
|
if(!be_11) {
|
|
BIO_reset(be);
|
|
BIO_free_all(be);
|
|
be = NULL;
|
|
}
|
|
/*
|
|
* Stop processing if:
|
|
* - client is not HTTP/1.1
|
|
* or
|
|
* - we had a "Connection: closed" header
|
|
* or
|
|
* - this is an SSL connection and we had a NoHTTPS11 directive
|
|
*/
|
|
if(!cl_11 || conn_closed || force_10)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* This may help with some versions of IE with a broken channel shutdown
|
|
*/
|
|
if(ssl != NULL)
|
|
SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
|
|
|
|
clean_all();
|
|
return;
|
|
}
|
|
|
|
void *
|
|
thr_http(void *dummy)
|
|
{
|
|
thr_arg *arg;
|
|
|
|
for(;;) {
|
|
while((arg = get_thr_arg()) == NULL)
|
|
logmsg(LOG_NOTICE, "NULL get_thr_arg");
|
|
do_http(arg);
|
|
}
|
|
}
|