#include "apr.h"
#include "apr_strings.h"
#include "apr_lib.h"
#include "apr_fnmatch.h"
#include "apr_hash.h"
#include "apr_thread_proc.h"
#include "apr_hooks.h"
#define APR_WANT_IOVEC
#define APR_WANT_STRFUNC
#define APR_WANT_MEMFUNC
#include "apr_want.h"
#define CORE_PRIVATE
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_vhost.h"
#include "http_main.h"
#include "http_log.h"
#include "util_md5.h"
#include "http_connection.h"
#include "apr_buckets.h"
#include "util_filter.h"
#include "util_ebcdic.h"
#include "mpm.h"
#include "mpm_common.h"
#include "scoreboard.h"
#include "mod_core.h"
#include "mod_proxy.h"
#include "ap_listen.h"
#include "mod_so.h"
#define AP_MIN_SENDFILE_BYTES (256)
#define BRIGADE_NORMALIZE(b) \
do { \
apr_bucket *e = APR_BRIGADE_FIRST(b); \
do { \
if (e->length == 0 && !APR_BUCKET_IS_METADATA(e)) { \
apr_bucket *d; \
d = APR_BUCKET_NEXT(e); \
apr_bucket_delete(e); \
e = d; \
} \
else { \
e = APR_BUCKET_NEXT(e); \
} \
} while (!APR_BRIGADE_EMPTY(b) && (e != APR_BRIGADE_SENTINEL(b))); \
} while (0)
static void brigade_move(apr_bucket_brigade *b, apr_bucket_brigade *a,
apr_bucket *e)
{
apr_bucket *f;
if (e != APR_BRIGADE_SENTINEL(b)) {
f = APR_RING_LAST(&b->list);
APR_RING_UNSPLICE(e, f, link);
APR_RING_SPLICE_HEAD(&a->list, e, f, apr_bucket, link);
}
APR_BRIGADE_CHECK_CONSISTENCY(a);
APR_BRIGADE_CHECK_CONSISTENCY(b);
}
int ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
ap_input_mode_t mode, apr_read_type_e block,
apr_off_t readbytes)
{
apr_bucket *e;
apr_status_t rv;
core_net_rec *net = f->ctx;
core_ctx_t *ctx = net->in_ctx;
const char *str;
apr_size_t len;
if (mode == AP_MODE_INIT) {
return APR_SUCCESS;
}
if (!ctx)
{
ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
ctx->b = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
ctx->tmpbb = apr_brigade_create(ctx->b->p, ctx->b->bucket_alloc);
e = apr_bucket_socket_create(net->client_socket, f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(ctx->b, e);
net->in_ctx = ctx;
}
else if (APR_BRIGADE_EMPTY(ctx->b)) {
return APR_EOF;
}
BRIGADE_NORMALIZE(ctx->b);
if (APR_BRIGADE_EMPTY(ctx->b)) {
return APR_EOF;
}
if (mode == AP_MODE_GETLINE) {
rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN);
if (APR_STATUS_IS_EAGAIN(rv)) {
rv = APR_SUCCESS;
}
return rv;
}
if (mode == AP_MODE_EATCRLF) {
apr_bucket *e;
const char *c;
while (1) {
if (APR_BRIGADE_EMPTY(ctx->b))
return APR_EOF;
e = APR_BRIGADE_FIRST(ctx->b);
rv = apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ);
if (rv != APR_SUCCESS)
return rv;
c = str;
while (c < str + len) {
if (*c == APR_ASCII_LF)
c++;
else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF)
c += 2;
else
return APR_SUCCESS;
}
apr_bucket_delete(e);
}
return APR_SUCCESS;
}
if (mode == AP_MODE_EXHAUSTIVE) {
apr_bucket *e;
APR_BRIGADE_CONCAT(b, ctx->b);
e = apr_bucket_eos_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(b, e);
return APR_SUCCESS;
}
if (mode == AP_MODE_READBYTES || mode == AP_MODE_SPECULATIVE) {
apr_bucket *e;
AP_DEBUG_ASSERT(readbytes > 0);
e = APR_BRIGADE_FIRST(ctx->b);
rv = apr_bucket_read(e, &str, &len, block);
if (APR_STATUS_IS_EAGAIN(rv)) {
return APR_SUCCESS;
}
else if (rv != APR_SUCCESS) {
return rv;
}
else if (block == APR_BLOCK_READ && len == 0) {
apr_bucket_delete(e);
if (mode == AP_MODE_READBYTES) {
e = apr_bucket_eos_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(b, e);
}
return APR_SUCCESS;
}
if (len < readbytes) {
readbytes = len;
}
rv = apr_brigade_partition(ctx->b, readbytes, &e);
if (rv != APR_SUCCESS) {
return rv;
}
brigade_move(ctx->b, ctx->tmpbb, e);
if (mode == AP_MODE_READBYTES) {
APR_BRIGADE_CONCAT(b, ctx->b);
}
else if (mode == AP_MODE_SPECULATIVE) {
apr_bucket *copy_bucket;
for (e = APR_BRIGADE_FIRST(ctx->b);
e != APR_BRIGADE_SENTINEL(ctx->b);
e = APR_BUCKET_NEXT(e))
{
rv = apr_bucket_copy(e, ©_bucket);
if (rv != APR_SUCCESS) {
return rv;
}
APR_BRIGADE_INSERT_TAIL(b, copy_bucket);
}
}
APR_BRIGADE_CONCAT(ctx->b, ctx->tmpbb);
}
return APR_SUCCESS;
}
static apr_status_t writev_it_all(apr_socket_t *s,
struct iovec *vec, int nvec,
apr_size_t len, apr_size_t *nbytes)
{
apr_size_t bytes_written = 0;
apr_status_t rv;
apr_size_t n = len;
int i = 0;
*nbytes = 0;
while (bytes_written != len) {
rv = apr_socket_sendv(s, vec + i, nvec - i, &n);
*nbytes += n;
bytes_written += n;
if (rv != APR_SUCCESS)
return rv;
if (bytes_written < len) {
apr_size_t cnt = vec[i].iov_len;
while (n >= cnt && i + 1 < nvec) {
i++;
cnt += vec[i].iov_len;
}
if (n < cnt) {
vec[i].iov_base = (char *) vec[i].iov_base +
(vec[i].iov_len - (cnt - n));
vec[i].iov_len = cnt -n;
}
}
n = len - bytes_written;
}
return APR_SUCCESS;
}
#if APR_HAS_SENDFILE
static apr_status_t sendfile_it_all(core_net_rec *c,
apr_file_t *fd,
apr_hdtr_t *hdtr,
apr_off_t file_offset,
apr_size_t file_bytes_left,
apr_size_t total_bytes_left,
apr_size_t *bytes_sent,
apr_int32_t flags)
{
apr_status_t rv;
#ifdef AP_DEBUG
apr_interval_time_t timeout = 0;
#endif
AP_DEBUG_ASSERT((apr_socket_timeout_get(c->client_socket, &timeout)
== APR_SUCCESS)
&& timeout > 0);
*bytes_sent = 0;
do {
apr_size_t tmplen = file_bytes_left;
rv = apr_socket_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
flags);
*bytes_sent += tmplen;
total_bytes_left -= tmplen;
if (!total_bytes_left || rv != APR_SUCCESS) {
return rv;
}
AP_DEBUG_ASSERT(total_bytes_left > 0 && tmplen > 0);
while (tmplen && hdtr->numheaders) {
if (tmplen >= hdtr->headers[0].iov_len) {
tmplen -= hdtr->headers[0].iov_len;
--hdtr->numheaders;
++hdtr->headers;
}
else {
char *iov_base = (char *)hdtr->headers[0].iov_base;
hdtr->headers[0].iov_len -= tmplen;
iov_base += tmplen;
hdtr->headers[0].iov_base = iov_base;
tmplen = 0;
}
}
if (tmplen <= file_bytes_left) {
file_offset += tmplen;
file_bytes_left -= tmplen;
continue;
}
tmplen -= file_bytes_left;
file_bytes_left = 0;
file_offset = 0;
while (tmplen && hdtr->numtrailers) {
if (tmplen >= hdtr->trailers[0].iov_len) {
tmplen -= hdtr->trailers[0].iov_len;
--hdtr->numtrailers;
++hdtr->trailers;
}
else {
char *iov_base = (char *)hdtr->trailers[0].iov_base;
hdtr->trailers[0].iov_len -= tmplen;
iov_base += tmplen;
hdtr->trailers[0].iov_base = iov_base;
tmplen = 0;
}
}
} while (1);
}
#endif
static apr_status_t emulate_sendfile(core_net_rec *c, apr_file_t *fd,
apr_hdtr_t *hdtr, apr_off_t offset,
apr_size_t length, apr_size_t *nbytes)
{
apr_status_t rv = APR_SUCCESS;
apr_size_t togo;
apr_size_t sendlen = 0;
apr_size_t bytes_sent;
apr_int32_t i;
apr_off_t o;
char buffer[8192];
*nbytes = 0;
if (hdtr && hdtr->numheaders > 0 ) {
for (i = 0; i < hdtr->numheaders; i++) {
sendlen += hdtr->headers[i].iov_len;
}
rv = writev_it_all(c->client_socket, hdtr->headers, hdtr->numheaders,
sendlen, &bytes_sent);
*nbytes += bytes_sent;
}
if (offset >= 0 && rv == APR_SUCCESS) {
rv = apr_file_seek(fd, APR_SET, &offset);
}
togo = length;
while (rv == APR_SUCCESS && togo) {
sendlen = togo > sizeof(buffer) ? sizeof(buffer) : togo;
o = 0;
rv = apr_file_read(fd, buffer, &sendlen);
while (rv == APR_SUCCESS && sendlen) {
bytes_sent = sendlen;
rv = apr_socket_send(c->client_socket, &buffer[o], &bytes_sent);
*nbytes += bytes_sent;
if (rv == APR_SUCCESS) {
sendlen -= bytes_sent;
o += bytes_sent;
togo -= bytes_sent;
}
}
}
sendlen = 0;
if ( rv == APR_SUCCESS && hdtr && hdtr->numtrailers > 0 ) {
for (i = 0; i < hdtr->numtrailers; i++) {
sendlen += hdtr->trailers[i].iov_len;
}
rv = writev_it_all(c->client_socket, hdtr->trailers, hdtr->numtrailers,
sendlen, &bytes_sent);
*nbytes += bytes_sent;
}
return rv;
}
#ifndef APR_MAX_IOVEC_SIZE
#define MAX_IOVEC_TO_WRITE 16
#else
#if APR_MAX_IOVEC_SIZE > 16
#define MAX_IOVEC_TO_WRITE 16
#else
#define MAX_IOVEC_TO_WRITE APR_MAX_IOVEC_SIZE
#endif
#endif
extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *logio_add_bytes_out;
apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
{
apr_status_t rv;
apr_bucket_brigade *more;
conn_rec *c = f->c;
core_net_rec *net = f->ctx;
core_output_filter_ctx_t *ctx = net->out_ctx;
apr_read_type_e eblock = APR_NONBLOCK_READ;
apr_pool_t *input_pool = b->p;
if (c->aborted) {
apr_brigade_cleanup(b);
return APR_ECONNABORTED;
}
if (ctx == NULL) {
ctx = apr_pcalloc(c->pool, sizeof(*ctx));
net->out_ctx = ctx;
}
if (ctx->b) {
APR_BRIGADE_CONCAT(ctx->b, b);
b = ctx->b;
ctx->b = NULL;
}
while (b && !APR_BRIGADE_EMPTY(b)) {
apr_size_t nbytes = 0;
apr_bucket *last_e = NULL;
apr_bucket *e;
apr_size_t nvec = 0;
apr_size_t nvec_trailers = 0;
struct iovec vec[MAX_IOVEC_TO_WRITE];
struct iovec vec_trailers[MAX_IOVEC_TO_WRITE];
apr_file_t *fd = NULL;
apr_size_t flen = 0;
apr_off_t foffset = 0;
apr_bucket *last_merged_bucket = NULL;
more = NULL;
for (e = APR_BRIGADE_FIRST(b);
e != APR_BRIGADE_SENTINEL(b);
e = APR_BUCKET_NEXT(e))
{
last_e = e;
if (APR_BUCKET_IS_EOS(e) || AP_BUCKET_IS_EOC(e)) {
break;
}
else if (APR_BUCKET_IS_FLUSH(e)) {
if (e != APR_BRIGADE_LAST(b)) {
more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
}
break;
}
else if (APR_BUCKET_IS_FILE(e)
&& (e->length >= AP_MIN_SENDFILE_BYTES)) {
apr_bucket_file *a = e->data;
if (fd) {
more = apr_brigade_split(b, e);
break;
}
fd = a->fd;
flen = e->length;
foffset = e->start;
}
else {
const char *str;
apr_size_t n;
rv = apr_bucket_read(e, &str, &n, eblock);
if (APR_STATUS_IS_EAGAIN(rv)) {
more = apr_brigade_split(b, e);
eblock = APR_BLOCK_READ;
break;
}
eblock = APR_NONBLOCK_READ;
if (n) {
if (!fd) {
if (nvec == MAX_IOVEC_TO_WRITE) {
apr_bucket *temp, *next;
apr_bucket_brigade *temp_brig;
if (nbytes >= AP_MIN_BYTES_TO_WRITE) {
more = apr_brigade_split(b, e);
break;
}
temp_brig = apr_brigade_create(f->c->pool,
f->c->bucket_alloc);
if (last_merged_bucket) {
APR_BRIGADE_PREPEND(b, temp_brig);
brigade_move(temp_brig, b, APR_BUCKET_NEXT(last_merged_bucket));
}
temp = APR_BRIGADE_FIRST(b);
while (temp != e) {
apr_bucket *d;
rv = apr_bucket_read(temp, &str, &n, APR_BLOCK_READ);
apr_brigade_write(temp_brig, NULL, NULL, str, n);
d = temp;
temp = APR_BUCKET_NEXT(temp);
apr_bucket_delete(d);
}
nvec = 0;
nbytes = 0;
temp = APR_BRIGADE_FIRST(temp_brig);
APR_BUCKET_REMOVE(temp);
APR_BRIGADE_INSERT_HEAD(b, temp);
apr_bucket_read(temp, &str, &n, APR_BLOCK_READ);
vec[nvec].iov_base = (char*) str;
vec[nvec].iov_len = n;
nvec++;
for (next = APR_BRIGADE_FIRST(temp_brig);
next != APR_BRIGADE_SENTINEL(temp_brig);
next = APR_BRIGADE_FIRST(temp_brig)) {
APR_BUCKET_REMOVE(next);
APR_BUCKET_INSERT_AFTER(temp, next);
temp = next;
apr_bucket_read(next, &str, &n,
APR_BLOCK_READ);
vec[nvec].iov_base = (char*) str;
vec[nvec].iov_len = n;
nvec++;
}
apr_brigade_destroy(temp_brig);
last_merged_bucket = temp;
e = temp;
last_e = e;
}
else {
vec[nvec].iov_base = (char*) str;
vec[nvec].iov_len = n;
nvec++;
}
}
else {
if (nvec_trailers == MAX_IOVEC_TO_WRITE) {
more = apr_brigade_split(b, e);
break;
}
vec_trailers[nvec_trailers].iov_base = (char*) str;
vec_trailers[nvec_trailers].iov_len = n;
nvec_trailers++;
}
nbytes += n;
}
}
}
if (nbytes + flen < AP_MIN_BYTES_TO_WRITE
&& !AP_BUCKET_IS_EOC(last_e)
&& ((!fd && !more && !APR_BUCKET_IS_FLUSH(last_e))
|| (APR_BUCKET_IS_EOS(last_e)
&& c->keepalive == AP_CONN_KEEPALIVE))) {
if (APR_BUCKET_IS_EOS(last_e)) {
apr_bucket *bucket;
int file_bucket_saved = 0;
apr_bucket_delete(last_e);
for (bucket = APR_BRIGADE_FIRST(b);
bucket != APR_BRIGADE_SENTINEL(b);
bucket = APR_BUCKET_NEXT(bucket)) {
if (APR_BUCKET_IS_FILE(bucket) && !file_bucket_saved) {
file_bucket_saved = 1;
}
else {
const char *buf;
apr_size_t len = 0;
rv = apr_bucket_read(bucket, &buf, &len,
APR_BLOCK_READ);
if (rv != APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv,
c, "core_output_filter:"
" Error reading from bucket.");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
}
}
if (!ctx->deferred_write_pool) {
apr_pool_create(&ctx->deferred_write_pool, c->pool);
apr_pool_tag(ctx->deferred_write_pool, "deferred_write");
}
ap_save_brigade(f, &ctx->b, &b, ctx->deferred_write_pool);
return APR_SUCCESS;
}
if (fd) {
apr_hdtr_t hdtr;
apr_size_t bytes_sent;
#if APR_HAS_SENDFILE
apr_int32_t flags = 0;
#endif
memset(&hdtr, '\0', sizeof(hdtr));
if (nvec) {
hdtr.numheaders = nvec;
hdtr.headers = vec;
}
if (nvec_trailers) {
hdtr.numtrailers = nvec_trailers;
hdtr.trailers = vec_trailers;
}
#if APR_HAS_SENDFILE
if (apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) {
if (c->keepalive == AP_CONN_CLOSE && APR_BUCKET_IS_EOS(last_e)) {
flags |= APR_SENDFILE_DISCONNECT_SOCKET;
}
rv = sendfile_it_all(net,
fd,
&hdtr,
foffset,
flen,
nbytes + flen,
&bytes_sent,
flags);
}
else
#endif
{
rv = emulate_sendfile(net, fd, &hdtr, foffset, flen,
&bytes_sent);
}
if (logio_add_bytes_out && bytes_sent > 0)
logio_add_bytes_out(c, bytes_sent);
fd = NULL;
}
else {
apr_size_t bytes_sent;
rv = writev_it_all(net->client_socket,
vec, nvec,
nbytes, &bytes_sent);
if (logio_add_bytes_out && bytes_sent > 0)
logio_add_bytes_out(c, bytes_sent);
}
apr_brigade_cleanup(b);
if (ctx->deferred_write_pool) {
if (more && more->p == ctx->deferred_write_pool) {
if (APR_BRIGADE_EMPTY(more)) {
more = NULL;
}
else {
apr_bucket_brigade *tmp_more = more;
more = NULL;
ap_save_brigade(f, &more, &tmp_more, input_pool);
}
}
apr_pool_clear(ctx->deferred_write_pool);
}
if (rv != APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, rv, c,
"core_output_filter: writing data to the network");
if (more)
apr_brigade_cleanup(more);
if (!APR_STATUS_IS_EAGAIN(rv)) {
c->aborted = 1;
return APR_ECONNABORTED;
}
return APR_SUCCESS;
}
b = more;
more = NULL;
}
return APR_SUCCESS;
}