📄 http_rd.c
字号:
/* Read the response header */
status = readFirstHeaderLine( stream, lineBuffer, lineBufSize,
&httpStatus );
if( cryptStatusError( status ) )
{
/* Some errors like an HTTP 404 aren't necessarily fatal in the
same way as (say) a CRYPT_ERROR_BADDATA because while the
latter means that the stream has been corrupted and we can't
continue, the former merely means that the requested item
wasn't found but we can still submit further requests. If
the caller has indicated that they want certain errors to be
treated as nonfatal, continue processing the stream and then
report the error later */
if( httpDataInfo->softErrors && httpStatus == 404 )
isSoftError = TRUE;
if( status != OK_SPECIAL && !isSoftError )
return( status );
/* It's a special-case header (e.g. a 100 Continue), turn the
read into a no-op read that drains the input to get to the
real data */
*flags |= HTTP_FLAG_NOOP;
needsSpecialHandling = TRUE;
}
/* Process the remaining header lines. 5 bytes is the minimum-size
object that can be returned from any HTTP-based message which is
exchanged by cryptlib, this being an OCSP response containing a
single-byte status value, i.e. SEQUENCE { ENUM x }.
If the read buffer is dynamically allocated then we allow an
effectively arbitrary content length, otherwise it has to fit
into the fixed-size read buffer. Unfortunately since CRLs can
reach > 100MB in size it's not really possible to provide any
sensible limit on the length for dynamic-buffer reads, however
to avoid DoS issues we limit it to 8MB until someone complains
that they can't read the 150MB CRLs that their CA is issuing
(yes, there are CAs that are issuing 150MB CRLs) */
initHeaderInfo( &headerInfo, 5,
httpDataInfo->bufferResize ? \
min( MAX_INTLENGTH, 8388608L ) : \
httpDataInfo->bufSize,
*flags );
status = readHeaderLines( stream, lineBuffer, lineBufSize,
&headerInfo );
if( cryptStatusError( status ) )
return( status );
/* Copy any status info back to the caller */
*flags = headerInfo.flags & ~HTTP_FLAG_NOOP;
httpDataInfo->bytesAvail = headerInfo.contentLength;
/* If this was a soft error due to not finding the requested item,
pass the status on to the caller. The low-level error
information will still be present from readFirstHeaderLine() */
if( isSoftError )
return( CRYPT_ERROR_NOTFOUND );
/* If it's not something like a redirect that needs special-case
handling, we're done */
if( !needsSpecialHandling )
return( CRYPT_OK );
REQUIRES( httpStatus == 100 || httpStatus == 301 || \
httpStatus == 302 || httpStatus == 307 );
/* If we got a 100 Continue response, try for another header that
follows the first one */
if( httpStatus == 100 )
continue;
/* If we got a 301, 302, or 307 Redirect then in theory we should
proceed roughly as per the code below, however in practice it's
not nearly as simple as this, because what we're in effect doing
is taking a stream and replacing it with a completely new stream
(different host/abs-path/query info, new socket with optional
proxy handling, etc etc). One way to do this would be to read
the new location into the current stream buffer and pass it back
with a special status telling the stream-level code to create a
new stream, clean up the old one, and perform a deep copy of the
new stream over to the old one. We'll leave this for a time when
it's really needed.
In addition the semantics of the following pseudocode don't quite
match those of RFC 2616 because of the HTTP-as-a-substrate use
rather than direct use in a browser. Specifically, anything
other than a GET for a 302 or 307 isn't supposed to perform an
automatic redirect without asking the user, because of concerns
that it'll change the semantics of the request. However since
we're not an interactive web browser there's no way that we can
ask a user for redirect permission, and in any case since we're
merely using HTTP as a substrate for a cryptographically
protected PKI message (and specifically assuming that the HTTP
layer is completely insecure), any problems will be caught by the
crypto protocol layer */
#if 0
if( !*location )
return( CRYPT_ERROR_READ );
netStream->closeSocketFunction( stream );
clFree( "readResponseHeader", netStream->host );
netStream->host = NULL;
status = parseLocation( stream, location );
if( cryptStatusError( status ) )
return( CRYPT_ERROR_READ );
#endif /* 0 */
retExt( CRYPT_ERROR_READ,
( CRYPT_ERROR_READ, NETSTREAM_ERRINFO,
"Unable to process HTTP %d redirect", httpStatus ) );
}
/* We used up our maximum number of retries, bail out */
retExt( CRYPT_ERROR_READ,
( CRYPT_ERROR_READ, NETSTREAM_ERRINFO,
"HTTP retry/redirection loop detected" ) );
}
/****************************************************************************
* *
* HTTP Access Functions *
* *
****************************************************************************/
/* Read data from an HTTP stream */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
static int readFunction( INOUT STREAM *stream,
OUT_BUFFER( maxLength, *length ) void *buffer,
IN_LENGTH_FIXED( sizeof( HTTP_DATA_INFO ) ) \
const int maxLength,
OUT_LENGTH_Z int *length )
{
NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
HTTP_DATA_INFO *httpDataInfo = ( HTTP_DATA_INFO * ) buffer;
char headerBuffer[ HTTP_LINEBUF_SIZE + 8 ];
int flags = HTTP_FLAG_NONE, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( buffer, maxLength ) );
assert( isWritePtr( length, sizeof( int ) ) );
REQUIRES( maxLength == sizeof( HTTP_DATA_INFO ) );
/* Clear return value */
*length = 0;
/* Read the HTTP packet header */
if( netStream->nFlags & STREAM_NFLAG_ISSERVER )
{
assert( !( netStream->nFlags & STREAM_NFLAG_HTTPGET ) || \
isWritePtr( httpDataInfo->reqInfo, \
sizeof( HTTP_URI_INFO ) ) );
status = readRequestHeader( stream, headerBuffer, HTTP_LINEBUF_SIZE,
httpDataInfo, &flags );
}
else
{
status = readResponseHeader( stream, headerBuffer, HTTP_LINEBUF_SIZE,
httpDataInfo, &flags );
if( cryptStatusOK( status ) && \
httpDataInfo->bytesAvail > httpDataInfo->bufSize )
{
void *newBuffer;
/* readResponseHeader() will only allow content larger than the
buffer size if it's marked as a resizeable buffer */
REQUIRES( httpDataInfo->bufferResize );
/* Adjust the read buffer size to handle the extra data and
record the details of the resized buffer */
if( ( newBuffer = clAlloc( "readFunction", \
httpDataInfo->bytesAvail ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
zeroise( httpDataInfo->buffer, httpDataInfo->bufSize );
clFree( "readFunction", httpDataInfo->buffer );
httpDataInfo->buffer = newBuffer;
httpDataInfo->bufSize = httpDataInfo->bytesAvail;
}
else
{
/* We didn't dynically resize the buffer, let the caller know */
httpDataInfo->bufferResize = FALSE;
}
}
if( cryptStatusError( status ) )
return( status );
ENSURES( httpDataInfo->bytesAvail <= httpDataInfo->bufSize );
REQUIRES( !( netStream->nFlags & STREAM_NFLAG_ISSERVER ) || \
( httpDataInfo->reqType != STREAM_HTTPREQTYPE_NONE ) );
/* If we're the server and the client sends us an HTTP GET, all of the
information was contained in the header and we're done */
if( ( netStream->nFlags & STREAM_NFLAG_ISSERVER ) && \
( httpDataInfo->reqType == STREAM_HTTPREQTYPE_GET ) )
{
*length = maxLength;
return( CRYPT_OK );
}
/* Read the payload data from the client/server */
status = netStream->bufferedTransportReadFunction( stream,
httpDataInfo->buffer, httpDataInfo->bytesAvail,
&httpDataInfo->bytesTransferred,
TRANSPORT_FLAG_NONE );
if( cryptStatusError( status ) )
return( status );
if( httpDataInfo->bytesTransferred < httpDataInfo->bytesAvail )
{
/* We timed out before reading all of the data. Usually this will
be reported as a CRYPT_ERROR_TIMEOUT by the lower-level read
routines, however due to the multiple layers of I/O and special
case timeout handling when (for example) a cryptlib transport
session is layered over the network I/O layer, we perform an
explicit check here to make sure that we got everything */
retExt( CRYPT_ERROR_TIMEOUT,
( CRYPT_ERROR_TIMEOUT, NETSTREAM_ERRINFO,
"HTTP read timed out before all data could be read, only "
"got %d of %d bytes", httpDataInfo->bytesTransferred,
httpDataInfo->bytesAvail ) );
}
/* If it's a plain-text error message, return it to the caller */
if( flags & HTTP_FLAG_TEXTMSG )
{
BYTE *byteBufPtr = httpDataInfo->buffer;
/* Usually a body returned as plain text is an error message that
(for some reason) is sent as content rather than an HTTP error,
however in some unusual cases the content will be the requested
object marked as plain text. We try and filter out genuine PKI
data erroneously marked as text by requiring that the request is
over a minimum size (most error messages are quite short) and
that the first bytes match what would be seen in a PKI object
such as a cert or CRL */
if( httpDataInfo->bytesAvail < 256 || ( byteBufPtr[ 0 ] != 0x30 ) || \
!( byteBufPtr[ 1 ] & 0x80 ) || \
( isAlpha( byteBufPtr[ 2 ] ) && isAlpha( byteBufPtr[ 3 ] ) && \
isAlpha( byteBufPtr[ 4 ] ) ) )
{
retExt( CRYPT_ERROR_READ,
( CRYPT_ERROR_READ, NETSTREAM_ERRINFO,
"HTTP server reported: '%s'",
sanitiseString( byteBufPtr, \
httpDataInfo->bufSize,
min( httpDataInfo->bytesTransferred, \
MAX_ERRMSG_SIZE - 32 ) ) ) );
}
}
/* If we're reading chunked data, drain the input by processing the
trailer. The reason why there can be extra header lines at the end
of the chunked data is because it's designed to be an indefinite-
length streamable format that doesn't require buffering the entire
message before emitting it. Since some header information may not be
available until the entire message has been generated, the HTTP spec.
makes provisions for adding further header lines as a trailer. In
theory we should check for the HTTP_FLAG_TRAILER flag before reading
trailer lines rather than just swallowing the last CRLF, however the
"Trailer:" header wasn't added until RFC 2616 (RFC 2068 didn't have
it) so we can't rely on its presence. Normally we wouldn't have to
worry about trailer data, but if it's an HTTP 1.1 persistent
connection we then need to clear the way for the next lot of data */
if( flags & HTTP_FLAG_CHUNKED )
{
status = readTrailerLines( stream, headerBuffer,
HTTP_LINEBUF_SIZE );
if( cryptStatusError( status ) )
return( status );
}
*length = maxLength;
return( CRYPT_OK );
}
STDC_NONNULL_ARG( ( 1 ) ) \
void setStreamLayerHTTP( INOUT NET_STREAM_INFO *netStream )
{
assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
/* Set the access method pointers */
netStream->readFunction = readFunction;
setStreamLayerHTTPwrite( netStream );
/* The default HTTP operation type is POST, since in most cases it's
being used as a substrate by a PKI protocol */
netStream->nFlags |= STREAM_NFLAG_HTTPPOST;
/* HTTP provides its own data-size and flow-control indicators so we
don't want the higher-level code to try and do this for us */
netStream->nFlags |= STREAM_NFLAG_ENCAPS;
}
#endif /* USE_HTTP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -