/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org>
*
* 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 1, or (at your option)
* any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: request.c,v 1.112.2.3 2002/07/24 03:03:59 jnelson Exp $*/
#include "boa.h"
#include <stddef.h> /* for offsetof */
#include <syslog.h>
#if USE_SSL
#include <sys/poll.h>
#include <sys/epoll.h>
#endif
int total_connections;
struct status status;
#if USE_SSL
extern SSL_CTX *g_sslctx;
#endif
static int sockbufsize = SOCKETBUF_SIZE;
/* function prototypes located in this file only */
static void free_request(request ** list_head_addr, request * req);
/*
* Name: new_request
* Description: Obtains a request struct off the free list, or if the
* free list is empty, allocates memory
*
* Return value: pointer to initialized request
*/
request *new_request(void)
{
request *req;
if (request_free) {
req = request_free; /* first on free list */
dequeue(&request_free, request_free); /* dequeue the head */
} else {
req = (request *) malloc(sizeof (request));
if (!req) {
log_error_time();
perror("malloc for new request");
return NULL;
}
}
memset(req, 0, offsetof(request, buffer) + 1);
#if USE_AUTH
req->authorization = NULL;
#endif
#if USE_SSL
req->ssl = NULL;
#endif
return req;
}
/*
* Name: get_request
*
* Description: Polls the server socket for a request. If one exists,
* does some basic initialization and adds it to the ready queue;.
*/
void get_request(int server_s)
{
int fd; /* socket */
struct SOCKADDR remote_addr; /* address */
struct SOCKADDR salocal;
int remote_addrlen = sizeof (struct SOCKADDR);
request *conn; /* connection */
size_t len;
static int system_bufsize = 0; /* Default size of SNDBUF given by system */
remote_addr.S_FAMILY = 0xdead;
fd = accept(server_s, (struct sockaddr *) &remote_addr,
&remote_addrlen);
if (fd == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK)
/* abnormal error */
WARN("accept");
else
/* no requests */
pending_requests = 0;
return;
}
if (fd >= FD_SETSIZE) {
WARN("Got fd >= FD_SETSIZE.");
close(fd);
return;
}
#ifdef DEBUGNONINET
/* This shows up due to race conditions in some Linux kernels
when the client closes the socket sometime between
the select() and accept() syscalls.
Code and description by Larry Doolittle <ldoolitt@boa.org>
*/
#define HEX(x) (((x)>9)?(('a'-10)+(x)):('0'+(x)))
if (remote_addr.sin_family != AF_INET) {
struct sockaddr *bogus = (struct sockaddr *) &remote_addr;
char *ap, ablock[44];
int i;
close(fd);
log_error_time();
for (ap = ablock, i = 0; i < remote_addrlen && i < 14; i++) {
*ap++ = ' ';
*ap++ = HEX((bogus->sa_data[i] >> 4) & 0x0f);
*ap++ = HEX(bogus->sa_data[i] & 0x0f);
}
*ap = '\0';
fprintf(stderr, "non-INET connection attempt: socket %d, "
"sa_family = %hu, sa_data[%d] = %s\n",
fd, bogus->sa_family, remote_addrlen, ablock);
return;
}
#endif
/* XXX Either delete this, or document why it's needed */
/* Pointed out 3-Oct-1999 by Paul Saab <paul@mu.org> */
#ifdef REUSE_EACH_CLIENT_CONNECTION_SOCKET
if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &sock_opt,
sizeof (sock_opt))) == -1) {
DIE("setsockopt: unable to set SO_REUSEADDR");
}
#endif
len = sizeof(salocal);
if (getsockname(fd, (struct sockaddr *) &salocal, &len) != 0) {
WARN("getsockname");
close(fd);
return;
}
conn = new_request();
if (!conn) {
close(fd);
return;
}
conn->fd = fd;
conn->status = READ_HEADER;
conn->header_line = conn->client_stream;
conn->time_last = current_time;
conn->kacount = ka_max;
ascii_sockaddr(&salocal, conn->local_ip_addr, NI_MAXHOST);
/* nonblocking socket */
if (set_nonblock_fd(conn->fd) == -1)
WARN("fcntl: unable to set new socket to non-block");
/* set close on exec to true */
if (fcntl(conn->fd, F_SETFD, 1) == -1)
WARN("fctnl: unable to set close-on-exec for new socket");
/* Increase buffer size if we have to.
* Only ask the system the buffer size on the first request,
* and assume all subsequent sockets have the same size.
*/
if (system_bufsize == 0) {
len = sizeof (system_bufsize);
if (getsockopt
(conn->fd, SOL_SOCKET, SO_SNDBUF, &system_bufsize, &len) == 0
&& len == sizeof (system_bufsize)) {
/*
fprintf(stderr, "%sgetsockopt reports SNDBUF %d\n",
get_commonlog_time(), system_bufsize);
*/
;
} else {
WARN("getsockopt(SNDBUF)");
system_bufsize = 1;
}
}
if (system_bufsize < sockbufsize) {
if (setsockopt
(conn->fd, SOL_SOCKET, SO_SNDBUF, (void *) &sockbufsize,
sizeof (sockbufsize)) == -1) {
WARN("setsockopt: unable to set socket buffer size");
#ifdef DIE_ON_ERROR_TUNING_SNDBUF
exit(errno);
#endif
}
}
/* for log file and possible use by CGI programs */
ascii_sockaddr(&remote_addr, conn->remote_ip_addr, NI_MAXHOST);
/* for possible use by CGI programs */
conn->remote_port = net_port(&remote_addr);
status.requests++;
#ifdef USE_TCPNODELAY
/* Thanks to Jef Poskanzer <jef@acme.com> for this tweak */
{
int one = 1;
if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY,
(void *) &one, sizeof (one)) == -1) {
DIE("setsockopt: unable to set TCP_NODELAY");
}
}
#endif
#ifndef NO_RATE_LIMIT
if (conn->fd > max_connections) {
send_r_service_unavailable(conn);
conn->status = DONE;
pending_requests = 0;
}
#endif /* NO_RATE_LIMIT */
#if USE_SSL
conn->ssl = SSL_new(g_sslctx);
if (!conn->ssl) {
conn->status = DEAD;
}
if (!SSL_set_fd(conn->ssl, conn->fd)) {
SSL_free(conn->ssl);
conn->ssl = NULL;
conn->status = DEAD;
}
if (conn->ssl) {
SSL_set_accept_state(conn->ssl);
/* blocking handshake */
SSL_do_handshake(conn->ssl);
/* nonblocking handshake*/
/*
int ret = 0;
int events = POLLIN | POLLOUT | POLLERR;
while ((ret = SSL_do_handshake(conn->ssl)) != 1) {
int err = SSL_get_error(conn->ssl, ret);
if (err == SSL_ERROR_WANT_WRITE) {
events |= POLLOUT;
events &= ~POLLIN;
} else if (err == SSL_ERROR_WANT_READ) {
events |= POLLIN;
events &= ~POLLOUT;
} else {
//ERR_print_errors_fp(stderr);
}
struct pollfd pfd;
pfd.fd = fd;
pfd.events = events;
do {
ret = poll(&pfd, 1, 50);
} while (ret == 0);
}
*/
//fprintf(stderr, "ssl accepted\n");
}
#endif
total_connections++;
enqueue(&request_ready, conn);
}
/*
* Name: free_request
*
* Description: Deallocates memory f
评论4
最新资源