📄 net_http.c
字号:
i++ );
httpStatusPtr = &httpStatusInfo[ i ];
if( httpStatus != NULL )
*httpStatus = aToI( lineBufPtr );
if( httpStatusPtr->status == OK_SPECIAL )
/* It's a special-case condition such as a redirect, tell the caller
to handle it specially */
return( OK_SPECIAL );
if( httpStatusPtr->status != CRYPT_OK )
{
/* It's an error condition, return extended error info */
assert( httpStatusPtr->httpStatusString != NULL );
/* Catch oddball errors in debug version */
retExtStream( stream, httpStatusPtr->status, "HTTP status: %s",
httpStatusPtr->httpErrorString );
}
return( CRYPT_OK );
}
/* Process an HTTP header line looking for anything that we can handle */
static int checkHeaderLine( char **lineBufPtrPtr,
HTTP_HEADER_TYPE *headerType, void *stream )
{
const HTTP_HEADER_INFO *headerInfoPtr;
const char *lineBufPtr = *lineBufPtrPtr;
const char firstChar = toUpper( *lineBufPtr );
const int lineLength = strlen( lineBufPtr );
int i;
/* Clear return value */
*headerType = HTTP_HEADER_NONE;
/* Look for a header line that we recognise */
for( i = 0;
httpHeaderInfo[ i ].headerString != NULL && \
( httpHeaderInfo[ i ].headerString[ 0 ] != firstChar || \
lineLength < httpHeaderInfo[ i ].headerStringLen || \
strCompare( lineBufPtr, httpHeaderInfo[ i ].headerString, \
httpHeaderInfo[ i ].headerStringLen ) );
i++ );
headerInfoPtr = &httpHeaderInfo[ i ];
if( headerInfoPtr->headerString == NULL )
/* It's nothing that we can handle, exit */
return( CRYPT_OK );
/* Make sure that there's a token present */
lineBufPtr = skipWhitespace( lineBufPtr + headerInfoPtr->headerStringLen );
if( lineBufPtr == NULL )
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Missing HTTP header token for '%s'",
headerInfoPtr->headerString );
/* Tell the caller what we found */
*lineBufPtrPtr = ( char * ) lineBufPtr;
*headerType = headerInfoPtr->headerType;
return( CRYPT_OK );
}
/* Read the first line in an HTTP response header */
static int readFirstHeaderLine( STREAM *stream, int *httpStatus,
char *lineBuffer, const int maxLength )
{
int status;
*httpStatus = CRYPT_OK;
/* Read the header and check for an HTTP ID */
status = readLine( stream, lineBuffer, maxLength );
if( cryptStatusError( status ) )
return( status );
status = checkHTTPID( stream, lineBuffer, status );
if( cryptStatusError( status ) )
retExtStream( stream, status, "Invalid HTTP ID/version" );
/* Read the HTTP status info */
return( readHTTPStatus( stream, httpStatus, lineBuffer + status ) );
}
/* Read the remaining HTTP header lines after the first one */
static int readHeaderLines( STREAM *stream, char *lineBuffer,
int *contentLength, int *httpErrorStatus,
int *flags, const int minLength,
const int maxLength, const BOOLEAN expandBuffer )
{
BOOLEAN seenHost = FALSE, seenLength = FALSE;
int localLength = 0, lineCount, status;
/* Clear return value */
if( httpErrorStatus != NULL )
*httpErrorStatus = 0;
if( contentLength != NULL )
*contentLength = 0;
/* Read each line in the header checking for any fields that we need to
handle */
for( lineCount = 0; lineCount < MAX_HEADER_LINES; lineCount++ )
{
HTTP_HEADER_TYPE headerType;
char *lineBufPtr = lineBuffer;
status = readLine( stream, lineBuffer, maxLength );
if( cryptStatusError( status ) )
return( status );
if( status == 0 )
/* End of input, exit */
break;
status = checkHeaderLine( &lineBufPtr, &headerType, stream );
if( cryptStatusError( status ) )
return( status );
switch( headerType )
{
case HTTP_HEADER_HOST:
/* Remember that we've seen a Host: line, to meet the HTTP
1.1 requirements */
seenHost = TRUE;
break;
case HTTP_HEADER_CONTENT_LENGTH:
/* Get the content length. At this point all we do is a
general sanity check that the length looks OK, a specific
check against the caller-supplied minimum allowable
length is performed later since the content length may
also be provided as a chunked encoding length */
localLength = aToI( lineBufPtr );
if( localLength <= 0 )
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Invalid HTTP content length %d",
localLength );
seenLength = TRUE;
break;
case HTTP_HEADER_CONTENT_TYPE:
/* Sometimes if there's an error it'll be returned at the
HTTP level rather than at the tunneled-over-HTTP protocol
level. The easiest way to check for this would be to
make sure that the content-type matches the expected type
and report anything else as an error. Unfortunately due
to the hit-and-miss handling of content-types by PKI
software using HTTP as a substrate it's not safe to do
this, so we have to default to allow-all rather than deny-
all, treating only straight text as a problem type
(although there are probably also apps out there
somewhere that send their PKI messages marked as plain
text) */
if( !strCompare( lineBufPtr, "text/", 5 ) )
*flags |= HTTP_FLAG_ERRORMSG;
break;
case HTTP_HEADER_TRANSFER_ENCODING:
if( !strCompare( lineBuffer, "Chunked", 7 ) )
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Invalid HTTP transfer encoding method, "
"expected 'Chunked'" );
/* If it's a chunked encoding, the length is part of the
data and must be read later */
*flags |= HTTP_FLAG_CHUNKED;
break;
case HTTP_HEADER_CONTENT_ENCODING:
/* We can't handle any type of content encoding (e.g. gzip,
compress, deflate) except the no-op identity encoding */
if( !strCompare( lineBuffer, "Identity", 8 ) )
{
if( httpErrorStatus != NULL )
*httpErrorStatus = 415;
lineBuffer[ CRYPT_MAX_TEXTSIZE ] = '\0';
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Invalid HTTP content encoding method '%s'",
lineBuffer );
}
break;
case HTTP_HEADER_CONTENT_TRANSFER_ENCODING:
/* HTTP uses Transfer-Encoding, not the MIME Content-
Transfer-Encoding types such as base64 or quoted-
printable. If any implementations use a C-T-E, we make
sure it's something that we can handle */
if( !strCompare( lineBuffer, "Identity", 8 ) && \
!strCompare( lineBuffer, "Binary", 6 ) )
{
if( httpErrorStatus != NULL )
*httpErrorStatus = 415;
lineBuffer[ CRYPT_MAX_TEXTSIZE ] = '\0';
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Invalid HTTP content transfer encoding "
"method '%s'", lineBuffer );
}
break;
case HTTP_HEADER_TRAILER:
/* The body is followed by trailer lines, used with chunked
encoding where some header lines can't be produced until
the entire body has been generated. This wasn't added
until RFC 2616, since many implementations are based on
RFC 2068 and don't produce this header we don't do
anything with it (the trailer can be auto-detected
anyway, it's only present to tell the receiver to perform
certain actions such as creating an MD5 hash of the data
as it arrives) */
*flags |= HTTP_FLAG_TRAILER;
break;
case HTTP_HEADER_CONNECTION:
/* If the other side has indicated that it's going to close
the connection, remember that the stream is now no longer
usable */
if( !strCompare( lineBuffer, "Close", 5 ) )
sioctl( stream, STREAM_IOCTL_CONNSTATE, NULL, FALSE );
break;
case HTTP_HEADER_WARNING:
/* Read the HTTP status info from the warning, discarding any
error status since this isn't an error */
readHTTPStatus( stream, NULL, lineBufPtr );
break;
case HTTP_HEADER_EXPECT:
/* If the other side wants the go-ahead to continue, give it
to them. We do this automatically because we're merely
using HTTP as a substrate, the real decision will be made
at the higher-level protocol layer. In theory we could
at least check the content type, but see the comment in
the content-type handler for why we don't do this */
if( !strCompare( lineBuffer, "100-Continue", 12 ) )
sendHTTPError( stream, lineBuffer, 100 );
break;
case HTTP_HEADER_NONE:
/* It's something that we don't know/care about, skip it */
break;
default:
assert( NOTREACHED );
}
}
if( lineCount >= MAX_HEADER_LINES )
retExtStream( stream, CRYPT_ERROR_OVERFLOW,
"Too many HTTP header lines" );
/* If it's a chunked encoding for which the length is kludged on before
the data as a hex string, decode the length value */
if( *flags & HTTP_FLAG_CHUNKED )
{
status = readLine( stream, lineBuffer, maxLength );
if( cryptStatusError( status ) )
return( status );
status = localLength = getChunkLength( lineBuffer, status );
if( cryptStatusError( status ) )
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Invalid length for HTTP chunked encoding" );
seenLength = TRUE;
}
/* If this is a no-op read (for example lines following a 100 Continue
response), all we're interested in is draining the input, so we don't
check any further */
if( *flags & HTTP_FLAG_NOOP )
return( CRYPT_OK );
/* Make sure that we've been given a length. In theory a server could
indicate the length implicitly by closing the connection once it's
sent the last byte, but this isn't allowed for PKI messages. The
client can't use this option in any case since that would make it
impossible for us to send back the response */
if( !seenLength )
{
if( httpErrorStatus != NULL )
*httpErrorStatus = 411;
retExtStream( stream, CRYPT_ERROR_BADDATA, "Missing HTTP length" );
}
/* Make sure that the length is sensible */
if( localLength < minLength )
retExtStream( stream, CRYPT_ERROR_UNDERFLOW,
"Insufficient HTTP content data" );
if( !expandBuffer && ( localLength > maxLength ) )
{
if( httpErrorStatus != NULL )
*httpErrorStatus = 413;
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Excessive HTTP content data" );
}
if( contentLength != NULL )
*contentLength = localLength;
/* If we're a server talking HTTP 1.1 and we haven't seen a Host: header
from the client, return an error */
if( ( stream->flags & STREAM_NFLAG_ISSERVER ) && \
!isHTTP10( stream ) && !seenHost )
{
if( httpErrorStatus != NULL )
*httpErrorStatus = 400;
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Missing HTTP host header" );
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* Read/write Request Header *
* *
****************************************************************************/
/* Write an HTTP request header */
static int writeRequestHeader( STREAM *stream, const int length )
{
STREAM headerStream;
char headerBuffer[ HTTP_LINEBUF_SIZE ];
const int transportFlag = length ? TRANSPORT_FLAG_NONE : \
TRANSPORT_FLAG_FLUSH;
const int hostLen = strlen( stream->host );
int headerLength;
sMemOpen( &headerStream, headerBuffer, HTTP_LINEBUF_SIZE );
if( length > 0 )
swrite( &headerStream, "POST ", 5 );
else
swrite( &headerStream, "GET ", 4 );
if( stream->flags & STREAM_NFLAG_HTTPPROXY )
{
/* If we're going through an HTTP proxy, send an absolute URL rather
than just the relative location */
swrite( &headerStream, "http://", 7 );
swrite( &headerStream, stream->host, hostLen );
if( stream->port != 80 )
{
char portString[ 16 ];
int portStringLength;
portStringLength = sprintf( portString, ":%d", stream->port );
swrite( &headerStream, portString, portStringLength );
}
}
if( stream->path != NULL )
swrite( &headerStream, stream->path, strlen( stream->path ) );
else
sputc( &headerStream, '/' );
if( stream->query != NULL )
{
sputc( &headerStream, '?' );
encodeRFC1866( &headerStream, stream->query );
}
if( isHTTP10( stream ) )
swrite( &headerStream, " HTTP/1.0\r\n", 11 );
else
{
swrite( &headerStream, " HTTP/1.1\r\nHost: ", 17 );
swrite( &headerStream, stream->host, hostLen );
swrite( &headerStream, "\r\n", 2 );
if( stream->flags & STREAM_NFLAG_LASTMSG )
swrite( &headerStream, "Connection: close\r\n", 19 );
}
if( length > 0 )
{
char lengthString[ 16 ];
int lengthStringLength;
swrite( &headerStream, "Content-Type: ", 14 );
swrite( &headerStream, stream->contentType,
strlen( stream->contentType ) );
swrite( &headerStream, "\r\nContent-Length: ", 18 );
lengthStringLength = sprintf( lengthString, "%d", length );
swrite( &headerStream, lengthString, lengthStringLength );
swrite( &headerStream, "\r\nCache-Control: no-cache\r\n", 27 );
}
swrite( &headerStream, "\r\n", 2 );
headerLength = stell( &headerStream );
assert( sStatusOK( &headerStream ) );
sMemDisconnect( &headerStream );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -