in server/protocol.c [213:526]
static apr_status_t ap_fgetline_core(char **s, apr_size_t n,
apr_size_t *read, ap_filter_t *f,
int flags, apr_bucket_brigade *bb,
apr_pool_t *p)
{
apr_status_t rv;
apr_bucket *e;
apr_size_t bytes_handled = 0, current_alloc = 0;
char *pos, *last_char = *s;
int do_alloc = (*s == NULL), saw_eos = 0;
int fold = flags & AP_GETLINE_FOLD;
int crlf = flags & AP_GETLINE_CRLF;
int nospc_eol = flags & AP_GETLINE_NOSPC_EOL;
int saw_eol = 0, saw_nospc = 0;
apr_read_type_e block;
if (!n) {
/* Needs room for NUL byte at least */
*read = 0;
return APR_BADARG;
}
block = (flags & AP_GETLINE_NONBLOCK) ? APR_NONBLOCK_READ
: APR_BLOCK_READ;
/*
* Initialize last_char as otherwise a random value will be compared
* against APR_ASCII_LF at the end of the loop if bb only contains
* zero-length buckets.
*/
if (last_char)
*last_char = '\0';
do {
apr_brigade_cleanup(bb);
rv = ap_get_brigade(f, bb, AP_MODE_GETLINE, block, 0);
if (rv != APR_SUCCESS) {
goto cleanup;
}
/* Something horribly wrong happened. Someone didn't block!
* (this also happens at the end of each keepalive connection)
* (this also happens when non-blocking is asked too, not that wrong)
*/
if (APR_BRIGADE_EMPTY(bb)) {
if (block != APR_NONBLOCK_READ) {
rv = APR_EGENERAL;
}
else {
rv = APR_EAGAIN;
}
goto cleanup;
}
for (e = APR_BRIGADE_FIRST(bb);
e != APR_BRIGADE_SENTINEL(bb);
e = APR_BUCKET_NEXT(e))
{
const char *str;
apr_size_t len;
/* If we see an EOS, don't bother doing anything more. */
if (APR_BUCKET_IS_EOS(e)) {
saw_eos = 1;
break;
}
rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
if (rv != APR_SUCCESS) {
goto cleanup;
}
if (len == 0) {
/* no use attempting a zero-byte alloc (hurts when
* using --with-efence --enable-pool-debug) or
* doing any of the other logic either
*/
continue;
}
/* Would this overrun our buffer? If so, we'll die. */
if (n < bytes_handled + len) {
/* Before we die, let's fill the buffer up to its limit (i.e.
* fall through with the remaining length, if any), setting
* saw_eol on LF to stop the outer loop appropriately; we may
* come back here once the buffer is filled (no LF seen), and
* either be done at that time or continue to wait for LF here
* if nospc_eol is set.
*
* But there is also a corner cases which we want to address,
* namely if the buffer is overrun by the final LF only (i.e.
* the CR fits in); this is not really an overrun since we'll
* strip the CR finally (and use it for NUL byte), but anyway
* we have to handle the case so that it's not returned to the
* caller as part of the truncated line (it's not!). This is
* easier to consider that LF is out of counting and thus fall
* through with no error (saw_eol is set to 2 so that we later
* ignore LF handling already done here), while folding and
* nospc_eol logics continue to work (or fail) appropriately.
*/
saw_eol = (str[len - 1] == APR_ASCII_LF);
if (/* First time around */
saw_eol && !saw_nospc
/* Single LF completing the buffered CR, */
&& ((len == 1 && ((*s)[bytes_handled - 1] == APR_ASCII_CR))
/* or trailing CRLF overuns by LF only */
|| (len > 1 && str[len - 2] == APR_ASCII_CR
&& n - bytes_handled + 1 == len))) {
/* In both cases *last_char is (to be) the CR stripped by
* later 'bytes_handled = last_char - *s'.
*/
saw_eol = 2;
}
else {
/* In any other case we'd lose data. */
rv = APR_ENOSPC;
saw_nospc = 1;
}
len = n - bytes_handled;
if (!len) {
if (saw_eol) {
break;
}
if (nospc_eol) {
continue;
}
goto cleanup;
}
}
/* Do we have to handle the allocation ourselves? */
if (do_alloc) {
/* We'll assume the common case where one bucket is enough. */
if (!*s) {
current_alloc = len;
*s = apr_palloc(p, current_alloc + 1);
}
else if (bytes_handled + len > current_alloc) {
/* Increase the buffer size */
apr_size_t new_size = current_alloc * 2;
char *new_buffer;
if (bytes_handled + len > new_size) {
new_size = (bytes_handled + len) * 2;
}
new_buffer = apr_palloc(p, new_size + 1);
/* Copy what we already had. */
memcpy(new_buffer, *s, bytes_handled);
current_alloc = new_size;
*s = new_buffer;
}
}
/* Just copy the rest of the data to the end of the old buffer. */
pos = *s + bytes_handled;
memcpy(pos, str, len);
last_char = pos + len - 1;
/* We've now processed that new data - update accordingly. */
bytes_handled += len;
}
/* If we got a full line of input, stop reading */
if (last_char && (*last_char == APR_ASCII_LF)) {
saw_eol = 1;
}
} while (!saw_eol);
if (rv != APR_SUCCESS) {
/* End of line after APR_ENOSPC above */
goto cleanup;
}
/* Now terminate the string at the end of the line;
* if the last-but-one character is a CR, terminate there.
* LF is handled above (not accounted) when saw_eol == 2,
* the last char is CR to terminate at still.
*/
if (saw_eol < 2) {
if (last_char > *s && last_char[-1] == APR_ASCII_CR) {
last_char--;
}
else if (crlf) {
rv = APR_EINVAL;
goto cleanup;
}
}
bytes_handled = last_char - *s;
/* If we're folding, we have more work to do.
*
* Note that if an EOS was seen, we know we can't have another line.
*/
if (fold && bytes_handled && !saw_eos) {
for (;;) {
const char *str;
apr_size_t len;
char c;
/* Clear the temp brigade for this filter read. */
apr_brigade_cleanup(bb);
/* We only care about the first byte. */
rv = ap_get_brigade(f, bb, AP_MODE_SPECULATIVE, block, 1);
if (rv != APR_SUCCESS) {
goto cleanup;
}
if (APR_BRIGADE_EMPTY(bb)) {
break;
}
e = APR_BRIGADE_FIRST(bb);
/* If we see an EOS, don't bother doing anything more. */
if (APR_BUCKET_IS_EOS(e)) {
break;
}
rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
if (rv != APR_SUCCESS) {
apr_brigade_cleanup(bb);
goto cleanup;
}
/* Found one, so call ourselves again to get the next line.
*
* FIXME: If the folding line is completely blank, should we
* stop folding? Does that require also looking at the next
* char?
*/
/* When we call destroy, the buckets are deleted, so save that
* one character we need. This simplifies our execution paths
* at the cost of one character read.
*/
c = *str;
if (c == APR_ASCII_BLANK || c == APR_ASCII_TAB) {
/* Do we have enough space? We may be full now. */
if (bytes_handled >= n) {
rv = APR_ENOSPC;
goto cleanup;
}
else {
apr_size_t next_size, next_len;
char *tmp;
/* If we're doing the allocations for them, we have to
* give ourselves a NULL and copy it on return.
*/
if (do_alloc) {
tmp = NULL;
}
else {
tmp = last_char;
}
next_size = n - bytes_handled;
rv = ap_fgetline_core(&tmp, next_size, &next_len, f,
flags & ~AP_GETLINE_FOLD, bb, p);
if (rv != APR_SUCCESS) {
goto cleanup;
}
if (do_alloc && next_len > 0) {
char *new_buffer;
apr_size_t new_size = bytes_handled + next_len + 1;
/* we need to alloc an extra byte for a null */
new_buffer = apr_palloc(p, new_size);
/* Copy what we already had. */
memcpy(new_buffer, *s, bytes_handled);
/* copy the new line, including the trailing null */
memcpy(new_buffer + bytes_handled, tmp, next_len);
*s = new_buffer;
}
last_char += next_len;
bytes_handled += next_len;
}
}
else { /* next character is not tab or space */
break;
}
}
}
cleanup:
if (bytes_handled >= n) {
bytes_handled = n - 1;
}
*read = bytes_handled;
if (*s) {
/* ensure the string is NUL terminated */
(*s)[*read] = '\0';
/* PR#43039: We shouldn't accept NULL bytes within the line */
bytes_handled = strlen(*s);
if (bytes_handled < *read) {
ap_log_data(APLOG_MARK, APLOG_DEBUG, ap_server_conf,
"NULL bytes in header", *s, *read, 0);
*read = bytes_handled;
if (rv == APR_SUCCESS) {
rv = APR_EINVAL;
}
}
}
return rv;
}