📄 http_rd.c
字号:
/****************************************************************************
* *
* cryptlib HTTP Read Routines *
* Copyright Peter Gutmann 1998-2007 *
* *
****************************************************************************/
#include <ctype.h>
#include <stdio.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "http.h"
#include "misc_rw.h"
#else
#include "crypt.h"
#include "io/http.h"
#include "misc/misc_rw.h"
#endif /* Compiler-specific includes */
#ifdef USE_HTTP
/* HTTP request line parsing information */
typedef struct {
BUFFER_FIXED( reqNameLen ) \
const char *reqName; /* Request name */
int reqNameLen; /* Length of request name */
STREAM_HTTPREQTYPE_TYPE reqType; /* Request type */
int reqTypeFlag; /* Stream flag for this request type */
} HTTP_REQUEST_INFO;
static const HTTP_REQUEST_INFO FAR_BSS httpReqInfo[] = {
{ "GET", 3, STREAM_HTTPREQTYPE_GET, STREAM_NFLAG_HTTPGET },
{ "POST", 4, STREAM_HTTPREQTYPE_POST, STREAM_NFLAG_HTTPPOST },
{ NULL, 0, 0 }, { NULL, 0, 0 }
};
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Callback function used by readTextLine() to read characters from a
stream. When reading text data over a network we don't know how much
more data is to come so we have to read a byte at a time looking for an
EOL. In addition we can't use the simple optimisation of reading two
bytes at a time because some servers only send a LF even though the spec
requires a CRLF. This is horribly inefficient but is pretty much
eliminated through the use of opportunistic read-ahead buffering */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int readCharFunction( INOUT TYPECAST( STREAM * ) void *streamPtr )
{
STREAM *stream = streamPtr;
NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
BYTE ch;
int length, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
status = netStream->bufferedTransportReadFunction( stream, &ch, 1,
&length,
TRANSPORT_FLAG_NONE );
return( cryptStatusError( status ) ? status : ch );
}
/****************************************************************************
* *
* Read Request Header *
* *
****************************************************************************/
/* Read an HTTP request header */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
static int readRequestHeader( INOUT STREAM *stream,
OUT_BUFFER_FIXED( lineBufSize ) char *lineBuffer,
IN_LENGTH_SHORT_MIN( 256 ) const int lineBufSize,
INOUT HTTP_DATA_INFO *httpDataInfo,
OUT_FLAGS_Z( HTTP ) int *flags )
{
NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
HTTP_HEADER_INFO headerInfo;
HTTP_URI_INFO *uriInfo = httpDataInfo->reqInfo;
STREAM_HTTPREQTYPE_TYPE reqType = STREAM_HTTPREQTYPE_NONE;
BOOLEAN isTextDataError;
char *bufPtr;
int length, offset, reqNameLen = DUMMY_INIT, i, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( lineBuffer, lineBufSize ) );
assert( isWritePtr( httpDataInfo, sizeof( HTTP_DATA_INFO ) ) );
assert( isWritePtr( flags, sizeof( int ) ) );
REQUIRES( netStream->nFlags & STREAM_NFLAG_ISSERVER );
REQUIRES( lineBufSize >= 256 && lineBufSize < MAX_INTLENGTH_SHORT );
/* Clear return value */
*flags = HTTP_FLAG_NONE;
/* Read the header and check for "POST/GET x HTTP/1.x". In theory this
could be a bit risky because the original CERN server required an
extra (spurious) CRLF after a POST, so that various early clients sent
an extra CRLF that isn't included in the Content-Length header and
ends up preceding the start of the next load of data. We don't check
for this because it only applies to very old pure-HTTP (rather than
HTTP-as-a-transport-layer) clients, which are unlikely to be hitting a
PKI responder */
status = readTextLine( readCharFunction, stream, lineBuffer,
lineBufSize, &length, &isTextDataError );
if( cryptStatusError( status ) )
{
/* If it's an HTTP-level error (e.g. line too long), send back an
HTTP-level error response */
if( status != CRYPT_ERROR_COMPLETE )
sendHTTPError( stream, lineBuffer, lineBufSize,
( status == CRYPT_ERROR_OVERFLOW ) ? 414 : 400 );
return( retTextLineError( stream, status, isTextDataError,
"Invalid HTTP request header line 1: ",
0 ) );
}
for( i = 0; httpReqInfo[ i ].reqName != NULL && \
i < FAILSAFE_ARRAYSIZE( httpReqInfo, HTTP_REQUEST_INFO );
i++ )
{
const HTTP_REQUEST_INFO *reqInfoPtr = &httpReqInfo[ i ];
if( ( reqInfoPtr->reqTypeFlag & netStream->nFlags ) && \
length >= reqInfoPtr->reqNameLen && \
!strCompare( lineBuffer, reqInfoPtr->reqName, \
reqInfoPtr->reqNameLen ) )
{
reqType = reqInfoPtr->reqType;
reqNameLen = reqInfoPtr->reqNameLen;
break;
}
}
ENSURES( i < FAILSAFE_ARRAYSIZE( httpReqInfo, HTTP_REQUEST_INFO ) );
if( reqType == STREAM_HTTPREQTYPE_NONE )
{
char reqNameBuffer[ 16 + 8 ];
/* Return the extended error information */
if( ( offset = strSkipNonWhitespace( lineBuffer, length ) ) > 0 )
length = offset;
memcpy( reqNameBuffer, lineBuffer, min( 16, length ) );
sendHTTPError( stream, lineBuffer, lineBufSize, 501 );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Invalid HTTP request '%s'",
sanitiseString( reqNameBuffer, 16, length ) ) );
}
bufPtr = lineBuffer + reqNameLen;
length -= reqNameLen;
/* Process the ' '* * ' '* and check for the HTTP ID */
if( length <= 0 || ( offset = strSkipWhitespace( bufPtr, length ) ) < 0 )
{
sendHTTPError( stream, lineBuffer, lineBufSize, 400 );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Missing HTTP request URI" ) );
}
bufPtr += offset;
length -= offset;
if( reqType == STREAM_HTTPREQTYPE_GET )
{
/* Safety check, make sure that we can handle the HTTP GET */
REQUIRES( uriInfo != NULL );
/* If it's an indempotent read the client is sending a GET rather
than submitting a POST, process the request details. This
performs in-place decoding of (possibly encoded) data, so it
returns two length values, the new length after the in-place
decoding has occurred, and the offset of the next character of
data as usual */
offset = parseUriInfo( bufPtr, length, &length, uriInfo );
}
else
{
/* For non-idempotent queries we don't care what the location is
since it's not relevant for anything, so we just skip the URI.
This also avoids complications with absolute vs. relative URLs,
character encoding/escape sequences, and so on */
offset = strSkipNonWhitespace( bufPtr, length );
}
if( cryptStatusError( offset ) )
{
sendHTTPError( stream, lineBuffer, lineBufSize, 400 );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Invalid HTTP GET request URI" ) );
}
bufPtr += offset;
length -= offset;
if( length <= 0 || ( offset = strSkipWhitespace( bufPtr, length ) ) < 0 )
{
sendHTTPError( stream, lineBuffer, lineBufSize, 400 );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Missing HTTP request ID/version" ) );
}
bufPtr += offset;
length -= offset;
if( length <= 0 || \
cryptStatusError( checkHTTPID( bufPtr, length, stream ) ) )
{
sendHTTPError( stream, lineBuffer, lineBufSize, 505 );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO,
"Invalid HTTP request ID/version" ) );
}
/* Process the remaining header lines. ~32 bytes is the minimum-size
object that can be returned from any HTTP-based message which is
exchanged by cryptlib, this being a TSP request */
initHeaderInfo( &headerInfo, 32, httpDataInfo->bufSize, *flags );
if( reqType == STREAM_HTTPREQTYPE_GET )
{
/* It's an HTTP get, make sure that we don't try and read a body */
headerInfo.flags |= HTTP_FLAG_GET;
}
status = readHeaderLines( stream, lineBuffer, lineBufSize,
&headerInfo );
if( cryptStatusError( status ) )
{
/* We always (try and) send an HTTP error response once we get to
this stage since chances are that it'll be a problem with an
HTTP header rather than a low-level network read problem */
sendHTTPError( stream, lineBuffer, lineBufSize,
headerInfo.httpStatus );
return( status );
}
/* Copy any status info back to the caller */
httpDataInfo->reqType = reqType;
if( reqType != STREAM_HTTPREQTYPE_GET )
httpDataInfo->bytesAvail = headerInfo.contentLength;
*flags = headerInfo.flags;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Read Response Header *
* *
****************************************************************************/
/* Read an HTTP response header */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
static int readResponseHeader( INOUT STREAM *stream,
OUT_BUFFER_FIXED( lineBufSize ) char *lineBuffer,
IN_LENGTH_SHORT_MIN( 256 ) const int lineBufSize,
INOUT HTTP_DATA_INFO *httpDataInfo,
OUT_FLAGS_Z( HTTP ) int *flags )
{
NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
int repeatCount, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( lineBuffer, lineBufSize ) );
assert( isWritePtr( httpDataInfo, sizeof( HTTP_DATA_INFO ) ) );
assert( isWritePtr( flags, sizeof( int ) ) );
REQUIRES( lineBufSize >= 256 && lineBufSize < MAX_INTLENGTH_SHORT );
/* Clear return value */
*flags = HTTP_FLAG_NONE;
/* If it's a stateless HTTP read, we need to first send the initiating
HTTP GET request before we can read anything back */
if( netStream->nFlags & STREAM_NFLAG_HTTPGET )
{
status = writeRequestHeader( stream, httpDataInfo->reqInfo,
NULL, 0, 0 );
if( cryptStatusError( status ) )
return( status );
}
/* Read the returned response header from the server, taking various
special-case conditions into account. In theory we could also handle
the 503 "Retry-After" status, but there's no sensible reason why
anyone should send us this, and even if they do it'll screw up a lot
of the PKI protocols, which have timeliness constraints built in */
for( repeatCount = 0; repeatCount < FAILSAFE_ITERATIONS_SMALL; \
repeatCount++ )
{
HTTP_HEADER_INFO headerInfo;
BOOLEAN needsSpecialHandling = FALSE, isSoftError = FALSE;
int httpStatus;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -