📄 http.c
字号:
{
lineBuffer[ CRYPT_MAX_TEXTSIZE ] = '\0';
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Invalid HTTP transfer encoding method "
"'%s', expected 'Chunked'",
sanitiseString( lineBuffer ) );
}
/* If it's a chunked encoding, the length is part of the
data and must be read later */
if( seenLength )
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Duplicate 'Content-Length:' header line" );
*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', expected 'Identity'",
sanitiseString( 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 that 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', expected 'Identity' or "
"'Binary'", sanitiseString( lineBuffer ) );
}
break;
case HTTP_HEADER_TRAILER:
/* The body is followed by trailer lines, used with chunked
encodings 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 this is an tunnel being opened via an HTTP proxy, we're done */
if( !( stream->flags & STREAM_NFLAG_ISSERVER ) && \
( stream->flags & STREAM_NFLAG_HTTPTUNNEL ) )
return( CRYPT_OK );
/* 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 an error or 100
Continue response), all that we're interested in is draining the
input, so we don't check any further */
if( *flags & HTTP_FLAG_NOOP )
return( CRYPT_OK );
/* 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" );
}
/* If it's an idempotent read there's no length, just a GET request, so
we can exit now */
if( stream->flags & STREAM_NFLAG_IDEMPOTENT )
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 either 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, need %d bytes but "
"only got %d", minLength, localLength );
if( !expandBuffer && ( localLength > maxLength ) )
{
if( httpErrorStatus != NULL )
*httpErrorStatus = 413;
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Excessive HTTP content data, got %d bytes when "
"maximum was %d", localLength, maxLength );
}
if( contentLength != NULL )
*contentLength = localLength;
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 + 8 ];
const int transportFlag = ( length > 0 ) ? TRANSPORT_FLAG_NONE : \
TRANSPORT_FLAG_FLUSH;
const int hostLen = strlen( stream->host );
int headerLength;
sMemOpen( &headerStream, headerBuffer, HTTP_LINEBUF_SIZE );
if( stream->flags & STREAM_NFLAG_HTTPTUNNEL )
swrite( &headerStream, "CONNECT ", 8 );
else
if( length > 0 )
swrite( &headerStream, "POST ", 5 );
else
swrite( &headerStream, "GET ", 4 );
if( ( stream->flags & STREAM_NFLAG_HTTPPROXY ) || \
( stream->flags & STREAM_NFLAG_HTTPTUNNEL ) )
{
/* If we're going through an HTTP proxy/tunnel, send an absolute URL
rather than just the relative location */
if( stream->flags & STREAM_NFLAG_HTTPPROXY )
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->flags & STREAM_NFLAG_HTTPTUNNEL ) )
{
if( stream->path != NULL && *stream->path != '\0' )
swrite( &headerStream, stream->path, strlen( stream->path ) );
else
sputc( &headerStream, '/' );
}
if( stream->query != NULL && *stream->query != '\0' )
{
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 );
return( sendHTTPData( stream, headerBuffer, headerLength,
transportFlag ) );
}
/* Read an HTTP request header */
static int readRequestHeader( STREAM *stream, int *contentLength,
char *buffer, const int maxLength, int *flags )
{
const char *bufPtr;
const char *reqName = ( stream->flags & STREAM_NFLAG_IDEMPOTENT ) ? \
"GET " : "POST ";
const int reqNameLen = ( stream->flags & STREAM_NFLAG_IDEMPOTENT ) ? \
4 : 5;
int bufMaxLen = maxLength, idempotentReadLength = 0, length;
int httpStatus, status;
assert( stream->flags & STREAM_NFLAG_ISSERVER );
/* Clear return value */
*contentLength = CRYPT_ERROR;
/* Read the header and check for "POST/GET x HTTP/1.x" (=15). 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 = length = readLine( stream, buffer, maxLength );
if( cryptStatusError( status ) )
{
/* If it's an HTTP-level error (e.g. line too long), send back an
error response */
if( status != CRYPT_ERROR_COMPLETE )
sendHTTPError( stream, buffer,
( status == CRYPT_ERROR_OVERFLOW ) ? 414 : 400 );
return( status );
}
if( strCompare( buffer, reqName, reqNameLen ) )
{
char reqNameBuffer[ 16 ];
strcpy( reqNameBuffer, reqName );
reqNameBuffer[ reqNameLen - 1 ] = '\0'; /* Strip trailing space */
sendHTTPError( stream, buffer, 501 );
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Invalid HTTP request, expected '%s'", reqName );
}
bufPtr = buffer + reqNameLen;
/* Process the ' '* * ' '* and check for the HTTP ID */
if( ( bufPtr = skipWhitespace( bufPtr ) ) == NULL )
{
sendHTTPError( stream, buffer, 400 );
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Missing HTTP request URI" );
}
if( stream->flags & STREAM_NFLAG_IDEMPOTENT )
{
/* If it's an indempotent read the client is sending a GET rather
than submitting a POST, process the request details */
status = parseURI( buffer, &idempotentReadLength, bufPtr,
length - ( bufPtr - buffer ) );
if( cryptStatusError( status ) || status < 10 )
{
sendHTTPError( stream, buffer, 400 );
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Invalid HTTP GET request URI" );
}
bufPtr = buffer + status;
/* At this point part of the read buffer contains the data to be
returned to the caller, with the remainder of the buffer
available for processing additional header lines. To handle this
we adjust the maximum buffer size to accomodate the data already
in the buffer */
bufMaxLen = maxLength - idempotentReadLength;
}
else
/* For non-idempotent queries we don't care what the location is
since it's not relevant for anything, this also avoids
complications with absolute vs. relative URLs, character
encoding/escape sequences, and so on */
while( *bufPtr && *bufPtr != ' ' )
bufPtr++;
if( ( bufPtr = skipWhitespace( bufPtr ) ) == NULL )
{
sendHTTPError( stream, buffer, 400 );
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Missing HTTP request ID/version" );
}
status = checkHTTPID( stream, bufPtr, length - ( bufPtr - buffer ) );
if( cryptStatusError( status ) )
{
sendHTTPError( stream, buffer, 505 );
retExtStream( stream, status, "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 */
status = readHeaderLines( stream, buffer + idempotentReadLength,
contentLength, &httpStatus, flags, 32,
bufMaxLen, FALSE );
if( cryptStatusError( status ) )
/* We always (try and) send an HTTP error response once we get to
this stage since chances are it'll be a problem with an HTTP
header rather than a low-level network read problem */
sendHTTPError( stream, buffer, httpStatus );
/* If it's an idempotent read, the content length is the length of the
request data and not the body, since there isn't one */
if( stream->flags & STREAM_NFLAG_IDEMPOTENT )
*contentLength = idempotentReadLength;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -