📄 net_http.c
字号:
return( stream->bufferedTransportWriteFunction( 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 *lineBufPtr;
int httpStatus, status;
assert( stream->flags & STREAM_NFLAG_ISSERVER );
/* Clear return value */
*contentLength = CRYPT_ERROR;
/* Read the POST header and check for "POST 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 = 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, "POST", 4 ) )
{
sendHTTPError( stream, buffer, 501 );
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Invalid HTTP request, expected 'POST'" );
}
lineBufPtr = buffer + 4;
/* Skip ' '* * ' '* and check for the HTTP ID. 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 */
if( ( lineBufPtr = skipWhitespace( lineBufPtr ) ) == NULL )
{
sendHTTPError( stream, buffer, 400 );
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Missing HTTP URI" );
}
while( *lineBufPtr && *lineBufPtr != ' ' )
lineBufPtr++;
if( ( lineBufPtr = skipWhitespace( lineBufPtr ) ) == NULL )
{
sendHTTPError( stream, buffer, 400 );
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Missing HTTP ID/version" );
}
status = checkHTTPID( stream, lineBufPtr, strlen( lineBufPtr ) );
if( cryptStatusError( status ) )
{
sendHTTPError( stream, buffer, 505 );
retExtStream( stream, status, "Invalid HTTP 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, contentLength, &httpStatus,
flags, 32, maxLength, 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 );
return( status );
}
/****************************************************************************
* *
* Read/write Response Header *
* *
****************************************************************************/
/* Write an HTTP response header */
static int writeResponseHeader( STREAM *stream, const int length )
{
char headerBuffer[ HTTP_LINEBUF_SIZE ];
int headerPos;
/* We don't use a stream to encode the header lines for responses since
all of the lines are quite short and can't overflow the buffer */
if( isHTTP10( stream ) )
strcpy( headerBuffer, "HTTP/1.0 200 OK\r\n" );
else
{
strcpy( headerBuffer, "HTTP/1.1 200 OK\r\n" );
if( stream->flags & STREAM_NFLAG_LASTMSG )
strcat( headerBuffer, "Connection: close\r\n" );
}
headerPos = strlen( headerBuffer );
strcat( headerBuffer + headerPos, "Content-Type: " );
strcat( headerBuffer + headerPos, stream->contentType );
strcat( headerBuffer + headerPos, "\r\nContent-Length: " );
headerPos += strlen( headerBuffer + headerPos );
sPrintf( headerBuffer + headerPos,
"%d\r\nCache-Control: no-cache\r\n", length );
if( isHTTP10( stream ) )
strcat( headerBuffer + headerPos, "Pragma: no-cache\r\n" );
strcat( headerBuffer + headerPos, "\r\n" );
return( stream->bufferedTransportWriteFunction( stream, headerBuffer,
strlen( headerBuffer ),
TRANSPORT_FLAG_NONE ) );
}
/* Read an HTTP response header */
static int readResponseHeader( STREAM *stream, int *contentLength, char *buffer,
const int maxLength,
const BOOLEAN expandBuffer, int *flags )
{
int repeatCount, status;
/* Clear return value */
*contentLength = CRYPT_ERROR;
/* If it's a stateless HTTP read, we need to send the fetch request
before we can read anything back */
if( stream->protocol == STREAM_PROTOCOL_HTTP )
{
assert( !*stream->contentType );
status = writeRequestHeader( stream, 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 < MAX_RETRY_COUNT; repeatCount++ )
{
BOOLEAN needsSpecialHandling = FALSE;
int httpStatus;
/* Read the response header */
status = readFirstHeaderLine( stream, &httpStatus, buffer, maxLength );
if( status == OK_SPECIAL )
{
/* If 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;
}
else
if( cryptStatusError( status ) )
return( status );
/* 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 } */
status = readHeaderLines( stream, buffer, contentLength, NULL,
flags, 5, maxLength, expandBuffer );
*flags &= ~HTTP_FLAG_NOOP;
if( cryptStatusError( status ) )
return( status );
/* If it's not something like a redirect that needs special-case
handling, we're done */
if( !needsSpecialHandling )
return( CRYPT_OK );
assert( 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 don't quite follow
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 );
stream->closeSocketFunction( stream );
clFree( "readResponseHeader", stream->host );
stream->host = NULL;
status = parseLocation( stream, location );
if( cryptStatusError( status ) )
return( CRYPT_ERROR_READ );
#endif /* 0 */
retExtStream( stream, CRYPT_ERROR_READ,
"Unable to process HTTP 301/302 redirect" );
}
/* We used up our maximum number of retries, bail out */
retExtStream( stream, CRYPT_ERROR_READ,
"HTTP retry/redirection loop detected" );
}
/****************************************************************************
* *
* HTTP Access Functions *
* *
****************************************************************************/
/* Read data from an HTTP stream */
static int readFunction( STREAM *stream, void *buffer, int length )
{
void *bufPtr = buffer;
int flags = HTTP_FLAG_NONE, contentLength, readLength, status;
/* Read the HTTP packet header and adjust the read buffer size if
necessary (this only occurs on the client side, which needs to be
able to handle arbitrary-length responses from the server) */
if( stream->flags & STREAM_NFLAG_ISSERVER )
status = readRequestHeader( stream, &contentLength, buffer, length,
&flags );
else
status = readResponseHeader( stream, &contentLength, buffer, length,
( stream->callbackFunction != NULL ) ? \
TRUE : FALSE, &flags );
if( cryptStatusError( status ) )
return( status );
if( contentLength > length )
{
if( stream->callbackFunction != NULL )
{
/* There's a buffer-adjust callback present, try and increase the
buffer size */
assert( stream->callbackParams != NULL );
status = stream->callbackFunction( stream->callbackParams,
&bufPtr, contentLength );
if( cryptStatusError( status ) )
return( status );
assert( isWritePtr( bufPtr, contentLength ) );
}
else
return( CRYPT_ERROR_OVERFLOW );
}
/* Read the payload data from the client/server */
readLength = status = \
stream->bufferedTransportReadFunction( stream, bufPtr, contentLength,
TRANSPORT_FLAG_NONE );
if( cryptStatusError( status ) )
return( status );
if( readLength < contentLength )
/* We timed out before reading all 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 */
retExtStream( stream, CRYPT_ERROR_READ,
"HTTP read timed out before all data could be read" );
/* If it's an error message, return it to the caller */
if( flags & HTTP_FLAG_ERRORMSG )
{
( ( char * ) buffer )[ min( readLength, MAX_ERRMSG_SIZE - 32 ) ] = '\0';
retExtStream( stream, CRYPT_ERROR_READ,
"HTTP server reported: '%s'", buffer );
}
/* 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:
CRLF
"0" CRLF
trailer-lines*
CRLF
Normally we wouldn't have to worry about trailer data, but if it's an
HTTP 1.1 persistent connection we need to clear the way for the next
lot of data */
if( flags & HTTP_FLAG_CHUNKED )
{
char headerBuffer[ HTTP_LINEBUF_SIZE ];
int noopFlags = HTTP_FLAG_NOOP;
status = readLine( stream, headerBuffer, HTTP_LINEBUF_SIZE );
if( !cryptStatusError( status ) )
status = readLine( stream, headerBuffer, HTTP_LINEBUF_SIZE );
if( cryptStatusError( status ) )
return( status );
status = getChunkLength( headerBuffer, status );
if( status != 0 )
retExtStream( stream, CRYPT_ERROR_BADDATA,
"Unexpected additional data in HTTP chunked data" );
status = readHeaderLines( stream, headerBuffer, NULL, NULL,
&noopFlags, 0, HTTP_LINEBUF_SIZE, FALSE );
}
return( cryptStatusError( status ) ? status : readLength );
}
/* Write data to an HTTP stream */
static int writeFunction( STREAM *stream, const void *buffer,
const int length )
{
int status;
/* Send the out-of-band HTTP header data to the client or server */
if( stream->flags & STREAM_NFLAG_ISSERVER )
status = writeResponseHeader( stream, length );
else
{
assert( strlen( stream->contentType ) );
assert( stream->host != NULL );
status = writeRequestHeader( stream, length );
}
if( cryptStatusError( status ) )
return( status );
/* Send the payload data to the client/server */
return( stream->bufferedTransportWriteFunction( stream, buffer, length,
TRANSPORT_FLAG_FLUSH ) );
}
int setStreamLayerHTTP( STREAM *stream )
{
/* Set the access method pointers */
stream->writeFunction = writeFunction;
stream->readFunction = readFunction;
return( CRYPT_OK );
}
#endif /* USE_TCP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -