|
|
/*++
Copyright (c) 1998-2001 Microsoft Corporation
Module Name:
rcvhdrs.cxx
Abstract:
Contains all of the per header handling code for received headers.
Author:
Henry Sanders (henrysa) 11-May-1998
Revision History:
--*/
#include "precomp.h"
#include "rcvhdrs.h"
/*++
Routine Description:
A utility routine, to find the terminating CRLF or LFLF of a header.
Arguments:
pHeader - Header whose end is to be found. HeaderLength - Length of data pointed to by pHeader. TokenLength - Where to return the length of the token.
Return Value:
Length of the header, or 0 if we couldn't find the end.
--*/ NTSTATUS FindHeaderEnd( IN PUCHAR pHeader, IN ULONG HeaderLength, OUT PULONG pBytesTaken ) { UCHAR CurrentChar; ULONG CurrentOffset; BOOLEAN HaveHeader;
//
// Important this is above the label (and our of the for loop)
// to support goto repeats.
//
CurrentOffset = 0;
look_for_crlf:
HaveHeader = FALSE;
//
// Always start from the second character.
//
CurrentOffset++;
//
// While we still have data, loop through looking for a CRLF or LFLF pair.
//
for (; CurrentOffset < HeaderLength; CurrentOffset += 2) { CurrentChar = *(pHeader + CurrentOffset);
//
// Try CR first because if we hit CR we only need to look at the
// character to the right to see if it is LF. The chance of hitting
// either CF or LF is 50/50, but the path length is shorter in the
// case we hit CR so check it first.
//
if (CurrentChar == CR) { //
// Be a bit aggressive to assume we will hit CRLF by adjusting
// CurrentOffset forward. If this is not CRLF, restore it back.
//
CurrentOffset++;
if (CurrentOffset < HeaderLength && *(pHeader + CurrentOffset) == LF) { //
// Cool, we are done so break the loop.
//
HaveHeader = TRUE; break; } else { CurrentOffset--; } }
//
// Now check to see if this is LF. If so, we look backward to see
// if we have a CR, and if that is not the case, look forward to
// see if we have another LF. It is assumed CRLF is more common
// than LFLF so we always look backward first.
//
if (CurrentChar == LF) { if (*(pHeader + CurrentOffset - 1) == CR) { //
// Everything is set so simply break the loop.
//
HaveHeader = TRUE; break; }
//
// It is still possible this can be LFLF so verify it.
//
CurrentOffset++;
if (CurrentOffset < HeaderLength && *(pHeader + CurrentOffset) == LF) { //
// Cool, we are done so break the loop.
//
HaveHeader = TRUE; break; } else { CurrentOffset--; } } }
//
// If we found the termination OK, return the length of the value.
//
if (HaveHeader) { ASSERT(CurrentOffset < HeaderLength);
//
// OK, we found a CRLF or LFLF, peek ahead 1 char to handle header
// value continuation.
//
//
// Skip the LF.
//
CurrentOffset += 1;
if (CurrentOffset == HeaderLength) { //
// Not enough buffer to check, need more.
//
*pBytesTaken = 0; return STATUS_SUCCESS; }
CurrentChar = *(pHeader + CurrentOffset);
//
// Is this a second line of the same header value? Check for
// continuation.
//
if (IS_HTTP_LWS(CurrentChar)) { ASSERT(pHeader[CurrentOffset - 1] == LF); ASSERT(CurrentOffset >= 2);
//
// Replace the CRLF|LFLF with SPSP.
//
pHeader[CurrentOffset - 1] = SP; pHeader[CurrentOffset - 2] = SP;
//
// Skip this WS char.
//
CurrentOffset += 1;
//
// Find the real end of the header value.
//
goto look_for_crlf; }
//
// All done!
//
*pBytesTaken = CurrentOffset; return STATUS_SUCCESS; }
//
// Did not find the end of a header, let's get more buffer.
//
*pBytesTaken = 0; return STATUS_SUCCESS; }
/*++
Routine Description:
A utility routine, to find the terminating CRLF or LFLF of a chunk header.
Arguments:
pHeader - Header whose end is to be found. HeaderLength - Length of data pointed to by pHeader. TokenLength - Where to return the length of the token.
Return Value:
Length of the header, or 0 if we couldn't find the end.
--*/ NTSTATUS FindChunkHeaderEnd( IN PUCHAR pHeader, IN ULONG HeaderLength, OUT PULONG pBytesTaken ) { UCHAR CurrentChar; ULONG CurrentOffset; BOOLEAN HaveCR; BOOLEAN HaveHeader;
CurrentOffset = 0; HaveCR = FALSE; HaveHeader = FALSE;
//
// While we still have data, loop through looking for a CRLF or LFLF pair.
//
for (; CurrentOffset < HeaderLength; CurrentOffset++) { CurrentChar = *(pHeader + CurrentOffset);
//
// If this character is a CR or LF, we may be done.
//
if (CurrentChar == CR || CurrentChar == LF) { //
// If we've already seen a CR (or LF) immediately preceding this,
// see if this is a LF to terminate the line.
//
if (HaveCR) { if (CurrentChar == LF) { // It is a LF, so we're done.
HaveHeader = TRUE;
break; }
// Otherwise, we have a non LF after a CR (or LF). The only
// character this could be is a CR, since we're inside
// the if statement. This could be the start of a CRLF
// sequence, if this is some bizarre LFCRLF or CRCRLF
// sort of thing. Anyway, we don't want to set HaveCR
// to false here.
ASSERT(CurrentChar == CR);
return STATUS_INVALID_DEVICE_REQUEST;
} else { // Otherwise, we haven't seen the start of the terminating pair
// yet, so remember that we now have.
HaveCR = TRUE; } } else if (HaveCR) { //
// it's illegal to have CR|LF and non trailing LF.
//
return STATUS_INVALID_DEVICE_REQUEST; } }
//
// If we found the termination OK, return the length of the value.
//
if (HaveHeader) { ASSERT(CurrentOffset < HeaderLength);
//
// All done!
//
*pBytesTaken = CurrentOffset + 1; return STATUS_SUCCESS; }
// Did not find the end of a header, let's get more buffer..
//
*pBytesTaken = 0; return STATUS_SUCCESS; }
/*++
Routine Description:
Append a header value to an existing HTTP_HEADER entry, allocating a buffer and copying the existing buffer.
Arguments:
pHttpHeader - Pointer to HTTP_HEADER structure to append to. pHeader - Pointer header to be appended. HeaderLength - Length of data pointed to by pHeader.
Return Value:
TRUE if we succeed, FALSE otherwise.
--*/ NTSTATUS UlAppendHeaderValue( PUL_INTERNAL_REQUEST pRequest, PUL_HTTP_HEADER pHttpHeader, PUCHAR pHeader, ULONG HeaderLength ) { PUCHAR pNewHeader, pOldHeader; ULONG OldHeaderLength;
OldHeaderLength = pHttpHeader->HeaderLength;
pNewHeader = UL_ALLOCATE_ARRAY( NonPagedPool, UCHAR, OldHeaderLength + HeaderLength + sizeof(", "), // sizeof gives space
// for the NULL
HEADER_VALUE_POOL_TAG );
if (pNewHeader == NULL) { // Had a failure.
return STATUS_NO_MEMORY; }
//
// Copy the old data into the new header.
//
RtlCopyMemory(pNewHeader, pHttpHeader->pHeader, OldHeaderLength);
// And copy in the new data as well, seperated by a comma.
//
*(pNewHeader + OldHeaderLength) = ','; *(pNewHeader + OldHeaderLength + 1) = ' '; OldHeaderLength += sizeof(", ") - 1;
RtlCopyMemory( pNewHeader + OldHeaderLength, pHeader, HeaderLength);
// Now replace the existing header.
//
pOldHeader = pHttpHeader->pHeader; pHttpHeader->HeaderLength = OldHeaderLength + HeaderLength; pHttpHeader->pHeader = pNewHeader;
// If the old header was our buffer, free it too.
//
if (pHttpHeader->OurBuffer) { UL_FREE_POOL( pOldHeader, HEADER_VALUE_POOL_TAG ); }
pHttpHeader->OurBuffer = 1;
//
// null terminate it
//
pHttpHeader->pHeader[pHttpHeader->HeaderLength] = ANSI_NULL;
pRequest->HeadersAppended = TRUE;
return STATUS_SUCCESS; }
/*++
Routine Description:
The default routine for handling headers. Used when we don't want to do anything with the header but find out if we have the whole thing and save a pointer to it if we do. this does not allow multiple header values to exist for this header. use UlMultipleHeaderHandler for handling that by appending the values together (CSV) .
Arguments:
pHttpConn - HTTP connection on which this header was received. pHeader - Pointer to the header value. HeaderLength - Length of data pointed to by pHeader. HeaderID - ID of the header.
Return Value:
The length of the header value, or 0 if it's not terminated.
--*/ NTSTATUS UlSingleHeaderHandler( IN PUL_INTERNAL_REQUEST pRequest, IN PUCHAR pHeader, IN ULONG HeaderLength, IN HTTP_HEADER_ID HeaderID, OUT PULONG pBytesTaken ) { NTSTATUS Status = STATUS_SUCCESS; ULONG BytesTaken; ULONG HeaderValueLength;
// Find the end of the header value
//
Status = FindHeaderEnd( pHeader, HeaderLength, &BytesTaken );
if (NT_SUCCESS(Status) == FALSE) goto end;
if (BytesTaken > 0) { // Strip of the trailing CRLF from the header value length
//
HeaderValueLength = BytesTaken - CRLF_SIZE;
//
// skip any preceding LWS.
//
while (HeaderValueLength > 0 && IS_HTTP_LWS(*pHeader)) { pHeader++; HeaderValueLength--; }
//
// remove any trailing LWS.
//
while (HeaderValueLength > 0 && IS_HTTP_LWS(pHeader[HeaderValueLength-1])) { HeaderValueLength--; }
// do we have an existing header?
//
if (pRequest->HeaderValid[HeaderID] == FALSE) { // No existing header, just save this pointer for now.
//
pRequest->HeaderIndex[pRequest->HeaderCount] = (UCHAR)HeaderID; pRequest->HeaderCount++; pRequest->HeaderValid[HeaderID] = TRUE; pRequest->Headers[HeaderID].HeaderLength = HeaderValueLength; pRequest->Headers[HeaderID].pHeader = pHeader; pRequest->Headers[HeaderID].OurBuffer = 0;
//
// null terminate it. we have space as all headers end with CRLF.
// we are over-writing the CR
//
pHeader[HeaderValueLength] = ANSI_NULL;
//
// make space for a terminator
//
pRequest->TotalRequestSize += (HeaderValueLength + 1) * sizeof(CHAR);
} else { //
// uh oh. Have an existing header, fail the request.
//
pRequest->ErrorCode = UlErrorHeader; pRequest->ParseState = ParseErrorState;
UlTrace(PARSER, ( "ul!UlSingleHeaderHandler(pRequest = %p, pHeader = %p)\n" " ERROR: multiple headers not allowed.\n", pRequest, pHeader ));
Status = STATUS_INVALID_DEVICE_REQUEST; goto end;
}
}
// Success!
//
*pBytesTaken = BytesTaken;
end: return Status;
} // UlSingleHeaderHandler
/*++
Routine Description:
The default routine for handling headers. Used when we don't want to do anything with the header but find out if we have the whole thing and save a pointer to it if we do. this function handles multiple headers with the same name, and appends the values together seperated by commas.
Arguments:
pHttpConn - HTTP connection on which this header was received. pHeader - Pointer to the header value. HeaderLength - Length of data pointed to by pHeader. HeaderID - ID of the header.
Return Value:
The length of the header value, or 0 if it's not terminated.
--*/ NTSTATUS UlMultipleHeaderHandler( IN PUL_INTERNAL_REQUEST pRequest, IN PUCHAR pHeader, IN ULONG HeaderLength, IN HTTP_HEADER_ID HeaderID, OUT PULONG pBytesTaken ) { NTSTATUS Status = STATUS_SUCCESS; ULONG BytesTaken; ULONG HeaderValueLength;
// Find the end of the header value
//
Status = FindHeaderEnd( pHeader, HeaderLength, &BytesTaken );
if (NT_SUCCESS(Status) == FALSE) goto end;
if (BytesTaken > 0) { // Strip of the trailing CRLF from the header value length
//
HeaderValueLength = BytesTaken - CRLF_SIZE;
//
// skip any preceding LWS.
//
while (HeaderValueLength > 0 && IS_HTTP_LWS(*pHeader)) { pHeader++; HeaderValueLength--; }
//
// remove any trailing LWS.
//
while (HeaderValueLength > 0 && IS_HTTP_LWS(pHeader[HeaderValueLength-1])) { HeaderValueLength--; }
// do we have an existing header?
//
if (pRequest->HeaderValid[HeaderID] == FALSE) { // No existing header, just save this pointer for now.
//
pRequest->HeaderIndex[pRequest->HeaderCount] = (UCHAR)HeaderID; pRequest->HeaderCount++; pRequest->HeaderValid[HeaderID] = TRUE; pRequest->Headers[HeaderID].HeaderLength = HeaderValueLength; pRequest->Headers[HeaderID].pHeader = pHeader; pRequest->Headers[HeaderID].OurBuffer = 0;
//
// null terminate it. we have space as all headers end with CRLF.
// we are over-writing the CR
//
pHeader[HeaderValueLength] = ANSI_NULL;
//
// make space for a terminator
//
pRequest->TotalRequestSize += (HeaderValueLength + 1) * sizeof(CHAR);
} else { ULONG OldHeaderLength;
// Have an existing header, append this one.
OldHeaderLength = pRequest->Headers[HeaderID].HeaderLength;
Status = UlAppendHeaderValue( pRequest, &pRequest->Headers[HeaderID], pHeader, HeaderValueLength );
if (NT_SUCCESS(Status) == FALSE) goto end;
//
// Update total request length for the amount we just added.
// space for the terminator is already in there
//
pRequest->TotalRequestSize += (pRequest->Headers[HeaderID].HeaderLength - OldHeaderLength) * sizeof(CHAR);
}
}
// Success!
//
*pBytesTaken = BytesTaken;
end: return Status;
} // UlMultipleHeaderHandler
/*++
Routine Description:
The routine for handling Accept headers.
Arguments:
pHttpConn - HTTP connection on which this header was received. pHeader - Pointer to the header value. HeaderLength - Length of data pointed to by pHeader. HeaderID - ID of the header.
Return Value:
The length of the header value, or 0 if it's not terminated. Wildcard bit is set in the request if found
--*/ NTSTATUS UlAcceptHeaderHandler( IN PUL_INTERNAL_REQUEST pRequest, IN PUCHAR pHeader, IN ULONG HeaderLength, IN HTTP_HEADER_ID HeaderID, OUT PULONG pBytesTaken ) { NTSTATUS Status = STATUS_SUCCESS; ULONG BytesTaken; ULONG HeaderValueLength;
// Find the end of the header value
//
Status = FindHeaderEnd( pHeader, HeaderLength, &BytesTaken );
if (NT_SUCCESS(Status) == FALSE) goto end;
if (BytesTaken > 0) { // Strip of the trailing CRLF from the header value length
//
HeaderValueLength = BytesTaken - CRLF_SIZE;
//
// skip any preceding LWS.
//
while (HeaderValueLength > 0 && IS_HTTP_LWS(*pHeader)) { pHeader++; HeaderValueLength--; }
//
// remove any trailing LWS.
//
while (HeaderValueLength > 0 && IS_HTTP_LWS(pHeader[HeaderValueLength-1])) { HeaderValueLength--; }
// do we have an existing header?
//
if (pRequest->HeaderValid[HeaderID] == FALSE) { // No existing header, just save this pointer for now.
//
pRequest->HeaderIndex[pRequest->HeaderCount] = (UCHAR)HeaderID; pRequest->HeaderCount++; pRequest->HeaderValid[HeaderID] = TRUE; pRequest->Headers[HeaderID].HeaderLength = HeaderValueLength; pRequest->Headers[HeaderID].pHeader = pHeader; pRequest->Headers[HeaderID].OurBuffer = 0;
//
// null terminate it. we have space as all headers end with CRLF.
// we are over-writing the CR
//
pHeader[HeaderValueLength] = ANSI_NULL;
//
// make space for a terminator
//
pRequest->TotalRequestSize += (HeaderValueLength + 1) * sizeof(CHAR);
if (HeaderValueLength > WILDCARD_SIZE) {
//
// for the fast path, we'll check only */* at the end
//
if ( (*(UNALIGNED64 ULONG *) (&pHeader[HeaderValueLength - WILDCARD_SIZE]) == WILDCARD_SPACE) || (*(UNALIGNED64 ULONG *) (&pHeader[HeaderValueLength - WILDCARD_SIZE]) == WILDCARD_COMMA) ) { pRequest->AcceptWildcard = TRUE; } }
} else { ULONG OldHeaderLength;
// Have an existing header, append this one.
OldHeaderLength = pRequest->Headers[HeaderID].HeaderLength;
Status = UlAppendHeaderValue( pRequest, &pRequest->Headers[HeaderID], pHeader, HeaderValueLength );
if (NT_SUCCESS(Status) == FALSE) goto end;
//
// Update total request length for the amount we just added.
// space for the terminator is already in there
//
pRequest->TotalRequestSize += (pRequest->Headers[HeaderID].HeaderLength - OldHeaderLength) * sizeof(CHAR);
}
}
// Success!
//
*pBytesTaken = BytesTaken;
end: return Status;
} // UlAcceptHeaderHandler
|