10 #if defined( INC_ALL )
24 typedef enum { HTTP_HEADER_NONE, HTTP_HEADER_HOST, HTTP_HEADER_CONTENT_LENGTH,
25 HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_TRANSFER_ENCODING,
26 HTTP_HEADER_CONTENT_ENCODING,
27 HTTP_HEADER_CONTENT_TRANSFER_ENCODING, HTTP_HEADER_TRAILER,
28 HTTP_HEADER_CONNECTION, HTTP_HEADER_WARNING,
29 HTTP_HEADER_LOCATION, HTTP_HEADER_EXPECT, HTTP_HEADER_LAST
35 #define HTTP_STATUSSTRING_LENGTH 3
39 const
char FAR_BSS *headerString;
40 const int headerStringLen;
41 const HTTP_HEADER_TYPE headerType;
42 } HTTP_HEADER_PARSE_INFO;
44 static const HTTP_HEADER_PARSE_INFO
FAR_BSS httpHeaderParseInfo[] = {
45 {
"Host:", 5, HTTP_HEADER_HOST },
46 {
"Content-Length:", 15, HTTP_HEADER_CONTENT_LENGTH },
47 {
"Content-Type:", 13, HTTP_HEADER_CONTENT_TYPE },
48 {
"Transfer-Encoding:", 18, HTTP_HEADER_TRANSFER_ENCODING },
49 {
"Content-Encoding:", 17, HTTP_HEADER_CONTENT_ENCODING },
50 {
"Content-Transfer-Encoding:", 26, HTTP_HEADER_CONTENT_TRANSFER_ENCODING },
51 {
"Trailer:", 8, HTTP_HEADER_TRAILER },
52 {
"Connection:", 11, HTTP_HEADER_CONNECTION },
53 {
"NnCoection:", 11, HTTP_HEADER_CONNECTION },
54 {
"Cneonction:", 11, HTTP_HEADER_CONNECTION },
69 {
"Warning:", 8, HTTP_HEADER_WARNING },
70 {
"Location:", 9, HTTP_HEADER_LOCATION },
71 {
"Expect:", 7, HTTP_HEADER_EXPECT },
72 { NULL, 0, HTTP_HEADER_NONE }, { NULL, 0, HTTP_HEADER_NONE }
84 const
char FAR_BSS *httpStatusString;
86 const
char FAR_BSS *httpErrorString;
87 const int httpErrorStringLength;
91 static const HTTP_STATUS_INFO
FAR_BSS httpStatusInfo[] = {
94 { 110,
"110",
"Warning: Response is stale", 26,
CRYPT_OK },
95 { 111,
"111",
"Warning: Revalidation failed", 28,
CRYPT_OK },
96 { 112,
"112",
"Warning: Disconnected operation", 31,
CRYPT_OK },
97 { 113,
"113",
"Warning: Heuristic expiration", 29,
CRYPT_OK },
98 { 199,
"199",
"Warning: Miscellaneous warning", 30,
CRYPT_OK },
102 { 203,
"203",
"Non-Authoritative Information", 29,
CRYPT_OK },
106 { 214,
"214",
"Warning: Transformation applied", 31,
CRYPT_OK },
107 { 250,
"250",
"RTSP: Low on Storage Space", 26,
CRYPT_OK },
108 { 299,
"299",
"Warning: Miscellaneous persistent warning", 41,
CRYPT_OK },
110 { 301,
"301",
"Moved Permanently", 17,
OK_SPECIAL },
111 { 302,
"302",
"Moved Temporarily/Found", 23,
OK_SPECIAL },
116 { 307,
"307",
"Temporary Redirect", 18,
OK_SPECIAL },
184 status = netStream->bufferedTransportReadFunction( stream, &
ch, 1,
193 static
int getNibble(
IN_CHAR const char srcCh )
200 return( ( ch <=
'9' ) ? ch -
'0' : ch - (
'a' - 10 ) );
220 chHi = getNibble( buffer[ 0 ] );
221 chLo = getNibble( buffer[ 1 ] );
224 ch = ( chHi << 4 ) | chLo;
233 if( ch <= 0 || ch > 0x7F || !
isPrint( ch ) )
242 static
int decodeRFC1866(
IN_BUFFER( bufSize )
char *buffer,
245 int srcIndex = 0, destIndex = 0;
251 while( srcIndex < bufSize )
253 int ch =
byteToInt( buffer[ srcIndex++ ] );
261 ch = getEncodedChar( buffer + srcIndex, bufSize - srcIndex );
272 return( ( destIndex < srcIndex ) ? destIndex :
OK_SPECIAL );
290 for( i = 0; i <
length; i++ )
292 if( data[ i ] ==
';' )
295 while( i > 0 && data[ i - 1 ] ==
' ' )
303 if( length < 1 || length > 4 )
309 for( i = 0; i <
length; i++ )
311 const int ch = getNibble( data[ i ] );
315 chunkLength = ( chunkLength << 4 ) | ch;
320 return( chunkLength );
347 if( !isTextLineError )
389 sanitiseString( strArg, strArgLen,
401 IN_BUFFER( headerBufMaxLen )
char *headerBuffer,
403 IN_INT const int httpStatus )
406 const char *statusString =
"400";
411 assert(
isWritePtr( headerBuffer, headerBufMaxLen ) );
413 REQUIRES( headerBufMaxLen >= 256 && \
418 for( i = 0; httpStatusInfo[ i ].httpStatus > 0 && \
419 httpStatusInfo[ i ].httpStatus != httpStatus && \
423 if( httpStatusInfo[ i ].httpStatus > 0 )
425 statusString = httpStatusInfo[ i ].httpStatusString;
426 errorString = httpStatusInfo[ i ].httpErrorString;
427 errorStringLength = httpStatusInfo[ i ].httpErrorStringLength;
431 sMemOpen( &headerStream, headerBuffer, headerBufMaxLen );
432 swrite( &headerStream, isHTTP10( stream ) ?
"HTTP/1.0 " : \
434 swrite( &headerStream, statusString, HTTP_STATUSSTRING_LENGTH );
435 sputc( &headerStream,
' ' );
436 swrite( &headerStream, errorString, errorStringLength );
437 swrite( &headerStream,
"\r\n", 2 );
438 if( httpStatus == 501 )
446 swrite( &headerStream,
"Content-Length: 139\r\n\r\n", 23 );
447 swrite( &headerStream,
448 "<html><head><title>Invalid PKI Server Request</title></head>"
449 "<body>This is a PKI messaging service, not a standard web "
450 "server.</body></html>", 139 );
452 status = swrite( &headerStream,
"\r\n", 2 );
454 length = stell( &headerStream );
455 sMemDisconnect( &headerStream );
457 return( sendHTTPData( stream, headerBuffer, length,
487 const char segmentEndChar, altSegmentEndChar;
488 const int segmentMinLength, segmentMaxLength;
497 const URI_PARSE_INFO *uriParseInfo,
500 const int maxLength =
min( dataMaxLength, uriParseInfo->segmentMaxLength );
503 assert(
isReadPtr( data, dataMaxLength ) );
504 assert(
isWritePtr( dataLength,
sizeof(
int ) ) );
505 assert(
isReadPtr( uriParseInfo,
sizeof( URI_PARSE_INFO ) ) );
506 assert( ( uriParseInfo->altSegmentEndChar ==
'\0' && \
507 altDelimiterFound == NULL ) || \
508 ( uriParseInfo->altSegmentEndChar >
'\0' && \
512 REQUIRES( uriParseInfo->segmentMinLength >= 0 && \
513 uriParseInfo->segmentMinLength < \
514 uriParseInfo->segmentMaxLength && \
515 uriParseInfo->segmentMaxLength <= 1024 );
516 REQUIRES( ( uriParseInfo->altSegmentEndChar ==
'\0' && \
517 altDelimiterFound == NULL ) || \
518 ( uriParseInfo->altSegmentEndChar >
'\0' && \
519 altDelimiterFound != NULL ) );
523 if( altDelimiterFound != NULL )
524 *altDelimiterFound =
FALSE;
529 if( data[ i ] == uriParseInfo->segmentEndChar )
531 if( uriParseInfo->altSegmentEndChar >
'\0' && \
532 data[ i ] == uriParseInfo->altSegmentEndChar )
534 *altDelimiterFound =
TRUE;
543 if( uriParseInfo->segmentEndChar !=
'\0' && i >= dataMaxLength )
548 if( i < uriParseInfo->segmentMinLength || \
549 i >= uriParseInfo->segmentMaxLength )
565 int parseUriInfo(
INOUT_BUFFER( dataInLength, *dataOutLength )
char *data,
570 static const URI_PARSE_INFO locationParseInfo = \
572 static const URI_PARSE_INFO attributeParseInfo = \
574 static const URI_PARSE_INFO valueParseInfo = \
576 static const URI_PARSE_INFO extraParseInfo = \
579 const char *bufPtr =
data;
580 int length = dataInLength, segmentLength, parsedLength, i,
status;
583 assert(
isWritePtr( dataOutLength,
sizeof(
int ) ) );
597 status = decodeRFC1866( data, length );
607 if( i >= FAILSAFE_ITERATIONS_SMALL )
623 status = getUriSegmentLength( bufPtr, length, &segmentLength,
624 &locationParseInfo, NULL );
627 memcpy( uriInfo->location, bufPtr, segmentLength );
628 uriInfo->locationLen = segmentLength;
629 bufPtr += segmentLength + 1;
630 length -= segmentLength + 1;
631 parsedLength = segmentLength + 1;
632 status = getUriSegmentLength( bufPtr, length, &segmentLength,
633 &attributeParseInfo, NULL );
636 memcpy( uriInfo->attribute, bufPtr, segmentLength );
637 uriInfo->attributeLen = segmentLength;
638 bufPtr += segmentLength + 1;
639 length -= segmentLength + 1;
640 parsedLength += segmentLength + 1;
641 status = getUriSegmentLength( bufPtr, length, &segmentLength,
642 &valueParseInfo, &altDelimiterFound );
645 memcpy( uriInfo->value, bufPtr, segmentLength );
646 uriInfo->valueLen = segmentLength;
647 bufPtr += segmentLength + 1;
648 length -= segmentLength + 1;
649 parsedLength += segmentLength + 1;
650 if( altDelimiterFound )
652 status = getUriSegmentLength( bufPtr, length, &segmentLength,
653 &extraParseInfo, NULL );
656 memcpy( uriInfo->extraData, bufPtr, segmentLength );
657 uriInfo->extraDataLen = segmentLength;
658 parsedLength += segmentLength + 1;
661 return( parsedLength );
668 int checkHTTPID(
IN_BUFFER( dataLength )
const char *data,
679 if( dataLength < 8 ||
strCompare( data,
"HTTP/1.", 7 ) )
681 if( data[ 7 ] ==
'0' )
685 if( data[ 7 ] !=
'1' )
702 static
int readHTTPStatus(
IN_BUFFER( dataLength )
const char *data,
707 const HTTP_STATUS_INFO *httpStatusPtr;
713 assert( httpStatus == NULL || \
720 if( httpStatus != NULL )
725 if( dataLength < 3 || strSkipNonWhitespace( data, dataLength ) != 3 )
729 "Invalid/missing HTTP %sstatus code",
730 isResponseStatus ?
"response " :
"" ) );
734 status = strGetNumeric( data, 3, &value, 1, 999 );
739 "Invalid HTTP %sstatus code",
740 isResponseStatus ?
"response " :
"" ) );
742 if( httpStatus != NULL )
749 thirdChar = data[ 2 ];
750 for( i = 0; httpStatusInfo[ i ].httpStatus != 0 && \
757 if( httpStatusInfo[ i ].httpStatusString[ 2 ] == thirdChar && \
758 !
strCompare( data, httpStatusInfo[ i ].httpStatusString, 3 ) )
762 httpStatusPtr = &httpStatusInfo[ i ];
767 if( !isResponseStatus )
772 remainderLength = dataLength - 3;
773 if( remainderLength < 2 || \
774 ( offset = strSkipWhitespace( data + 3, remainderLength ) ) < 0 || \
775 dataLength - offset < 1 )
779 "Missing HTTP response status text" ) );
789 if( httpStatusPtr->status !=
CRYPT_OK )
791 assert( httpStatusPtr->httpStatusString != NULL );
793 retExt( httpStatusPtr->status,
794 ( httpStatusPtr->status, errorInfo,
795 "HTTP response status: %s",
796 httpStatusPtr->httpErrorString ) );
805 static
int processHeaderLine(
IN_BUFFER( dataLength )
const char *data,
808 HTTP_HEADER_TYPE *headerType,
810 IN_RANGE( 1, 999 )
const int errorLineNo )
812 const HTTP_HEADER_PARSE_INFO *headerParseInfoPtr = NULL;
813 const int firstChar =
toUpper( *data );
814 int processedLength, dataLeft, i;
817 assert(
isWritePtr( headerType,
sizeof( HTTP_HEADER_TYPE ) ) );
820 REQUIRES( errorLineNo > 0 && errorLineNo < 1000 );
824 *headerType = HTTP_HEADER_NONE;
828 httpHeaderParseInfo[ i ].headerString != NULL && \
830 HTTP_HEADER_PARSE_INFO );
833 if( httpHeaderParseInfo[ i ].headerString[ 0 ] == firstChar && \
834 dataLength >= httpHeaderParseInfo[ i ].headerStringLen && \
835 !
strCompare( data, httpHeaderParseInfo[ i ].headerString, \
836 httpHeaderParseInfo[ i ].headerStringLen ) )
838 headerParseInfoPtr = &httpHeaderParseInfo[ i ];
843 HTTP_HEADER_PARSE_INFO ) );
844 if( headerParseInfoPtr == NULL )
849 processedLength = headerParseInfoPtr->headerStringLen;
850 dataLeft = dataLength - processedLength;
855 const int extraLength = \
856 strSkipWhitespace( data + processedLength, dataLeft );
865 if( extraLength > 0 )
869 dataLeft -= extraLength;
870 processedLength += extraLength;
878 "Missing HTTP header value for '%s' token, line %d",
879 headerParseInfoPtr->headerString, errorLineNo ) );
883 *headerType = headerParseInfoPtr->headerType;
884 return( processedLength );
897 int length, processedLength, dataLeft, status;
900 assert(
isWritePtr( dataBuffer, dataMaxLength ) );
901 assert(
isWritePtr( httpStatus,
sizeof(
int ) ) );
909 status = readTextLine( readCharFunction, stream, dataBuffer,
910 dataMaxLength, &length, &textDataError );
912 return( retTextLineError( stream, status, textDataError,
913 "Invalid HTTP header line 1: ", 0 ) );
918 "Invalid HTTP header line length %d ", length ) );
920 processedLength = checkHTTPID( dataBuffer, length, stream );
926 processedLength : CRYPT_ERROR_BADDATA,
929 dataLeft = length - processedLength;
934 const int extraLength = \
935 strSkipWhitespace( dataBuffer + processedLength, dataLeft );
944 if( extraLength > 0 )
948 dataLeft -= extraLength;
949 processedLength += extraLength;
955 retExt( CRYPT_ERROR_BADDATA,
957 "Missing HTTP status code, line 1" ) );
961 return( readHTTPStatus( dataBuffer + processedLength, dataLeft,
971 INOUT HTTP_HEADER_INFO *headerInfo )
975 int contentLength = 0, lineCount, status;
978 assert(
isWritePtr( lineBuffer, lineBufMaxLen ) );
979 assert(
isWritePtr( headerInfo,
sizeof( HTTP_HEADER_INFO ) ) );
1001 HTTP_HEADER_TYPE headerType;
1006 status = readTextLine( readCharFunction, stream, lineBuffer,
1007 lineBufMaxLen, &lineLength, &textDataError );
1010 return( retTextLineError( stream, status, textDataError,
1011 "Invalid HTTP header line %d: ",
1014 if( lineLength <= 0 )
1019 status = processHeaderLine( lineBuffer, lineLength, &headerType,
1023 lineBufPtr = lineBuffer +
status;
1026 switch( headerType )
1028 case HTTP_HEADER_HOST:
1034 retExt( CRYPT_ERROR_BADDATA,
1036 "Duplicate HTTP 'Host:' header, line %d",
1042 case HTTP_HEADER_CONTENT_LENGTH:
1053 retExt( CRYPT_ERROR_BADDATA,
1055 "Duplicate HTTP 'Content-Length:' header, "
1056 "line %d", lineCount + 2 ) );
1058 status = strGetNumeric( lineBufPtr, lineLength,
1062 retExt( CRYPT_ERROR_BADDATA,
1064 "Invalid HTTP content length, line %d",
1070 case HTTP_HEADER_CONTENT_TYPE:
1072 static const URI_PARSE_INFO typeParseInfo = \
1074 static const URI_PARSE_INFO subtypeParseInfo = \
1078 int contentTypeLen, subTypeLen;
1114 status = getUriSegmentLength( lineBufPtr, lineLength,
1116 &typeParseInfo, NULL );
1121 return( retHeaderError( stream,
1122 "Invalid HTTP content type '%s', line %d",
1123 lineBufPtr, lineLength, lineCount ) );
1125 contentType = lineBufPtr;
1126 lineBufPtr += contentTypeLen + 1;
1127 lineLength -= contentTypeLen + 1;
1128 status = getUriSegmentLength( lineBufPtr, lineLength,
1130 &subtypeParseInfo, &dummy );
1135 return( retHeaderError( stream,
1136 "Invalid HTTP content subtype '%s', line %d",
1137 lineBufPtr, lineLength, lineCount ) );
1139 if( contentTypeLen == 4 && \
1141 headerInfo->flags |= HTTP_FLAG_TEXTMSG;
1145 case HTTP_HEADER_TRANSFER_ENCODING:
1146 if( lineLength < 7 || \
1149 return( retHeaderError( stream,
1150 "Invalid HTTP transfer encoding method "
1151 "'%s', expected 'Chunked', line %d",
1152 lineBufPtr, lineLength, lineCount ) );
1159 retExt( CRYPT_ERROR_BADDATA,
1161 "Duplicate HTTP 'Content-Length:' header, "
1162 "line %d", lineCount + 2 ) );
1164 headerInfo->flags |= HTTP_FLAG_CHUNKED;
1168 case HTTP_HEADER_CONTENT_ENCODING:
1172 if( lineLength < 8 || \
1175 headerInfo->httpStatus = 415;
1176 return( retHeaderError( stream,
1177 "Invalid HTTP content encoding method "
1178 "'%s', expected 'Identity', line %d",
1179 lineBufPtr, lineLength, lineCount ) );
1183 case HTTP_HEADER_CONTENT_TRANSFER_ENCODING:
1189 if( !( lineLength >= 6 && \
1190 !
strCompare( lineBufPtr,
"Binary", 6 ) ) && \
1191 !( lineLength >= 8 && \
1194 headerInfo->httpStatus = 415;
1195 return( retHeaderError( stream,
1196 "Invalid HTTP content transfer encoding "
1197 "method '%s', expected 'Identity' or "
1198 "'Binary', line %d", lineBufPtr, lineLength,
1203 case HTTP_HEADER_TRAILER:
1213 headerInfo->flags |= HTTP_FLAG_TRAILER;
1216 case HTTP_HEADER_CONNECTION:
1220 if( lineLength >= 5 && \
1225 case HTTP_HEADER_WARNING:
1232 status = readHTTPStatus( lineBufPtr, lineLength, NULL,
1236 return( retHeaderError( stream,
1237 "Invalid HTTP warning information '%s', "
1238 "line %d", lineBufPtr, lineLength,
1243 case HTTP_HEADER_LOCATION:
1245 #if defined( __WIN32__ ) && !defined( NDEBUG ) && 1
1253 if( lineLength < 10 || \
1256 return( retHeaderError( stream,
1257 "Invalid HTTP redirect location '%s', line %d",
1258 lineBufPtr, lineLength, lineCount ) );
1262 #if defined( __WIN32__ ) && !defined( NDEBUG ) && 1
1267 status = sNetParseURL( &urlInfo, lineBufPtr, lineLength,
1271 return( retHeaderError( stream,
1272 "Invalid HTTP redirect location '%s', line %d",
1273 lineBufPtr, lineLength, lineCount ) );
1279 case HTTP_HEADER_EXPECT:
1284 if( lineLength >= 12 && \
1285 !
strCompare( lineBufPtr,
"100-Continue", 12 ) )
1286 sendHTTPError( stream, lineBufPtr, lineBufMaxLen, 100 );
1289 case HTTP_HEADER_NONE:
1297 if( lineCount >= FAILSAFE_ITERATIONS_MED )
1301 "Too many HTTP header lines" ) );
1311 if( headerInfo->flags & HTTP_FLAG_CHUNKED )
1316 status = readTextLine( readCharFunction, stream, lineBuffer,
1317 lineBufMaxLen, &lineLength, &textDataError );
1320 return( retTextLineError( stream, status, textDataError,
1321 "Invalid HTTP chunked encoding "
1322 "header line %d: ", lineCount + 2 ) );
1324 if( lineLength <= 0 )
1326 retExt( CRYPT_ERROR_BADDATA,
1328 "Missing HTTP chunk length, line %d", lineCount + 2 ) );
1330 status = contentLength = getChunkLength( lineBuffer, lineLength );
1333 retExt( CRYPT_ERROR_BADDATA,
1335 "Invalid length for HTTP chunked encoding, line %d",
1343 if( headerInfo->flags & HTTP_FLAG_NOOP )
1349 !isHTTP10( stream ) && !seenHost )
1351 headerInfo->httpStatus = 400;
1352 retExt( CRYPT_ERROR_BADDATA,
1354 "Missing HTTP 1.1 'Host:' header" ) );
1358 if( headerInfo->flags & HTTP_FLAG_GET )
1362 retExt( CRYPT_ERROR_BADDATA,
1364 "Unexpected %d bytes HTTP body content received in "
1365 "idempotent read", contentLength ) );
1377 headerInfo->httpStatus = 411;
1378 retExt( CRYPT_ERROR_BADDATA,
1380 "Missing HTTP length" ) );
1384 if( contentLength < headerInfo->minContentLength || \
1385 contentLength > headerInfo->maxContentLength )
1387 retExt( ( contentLength < headerInfo->minContentLength ) ? \
1389 ( ( contentLength < headerInfo->minContentLength ) ? \
1392 "Invalid HTTP content length %d bytes, expected "
1393 "%d...%d bytes", contentLength,
1394 headerInfo->minContentLength,
1395 headerInfo->maxContentLength ) );
1397 headerInfo->contentLength = contentLength;
1417 HTTP_HEADER_INFO headerInfo;
1422 assert(
isReadPtr( lineBuffer, lineBufMaxLen ) );
1427 status = readTextLine( readCharFunction, stream, lineBuffer,
1428 lineBufMaxLen, &dummy, &textDataError );
1430 status = readTextLine( readCharFunction, stream, lineBuffer,
1431 lineBufMaxLen, &readLength, &textDataError );
1433 return( retTextLineError( stream, status, textDataError,
1434 "Invalid HTTP chunked trailer line: ",
1438 status = getChunkLength( lineBuffer, readLength );
1441 retExt( CRYPT_ERROR_BADDATA,
1443 "Unexpected additional data following HTTP chunked "
1448 initHeaderInfo( &headerInfo, 0, 0, HTTP_FLAG_NOOP );
1449 return( readHeaderLines( stream, lineBuffer, lineBufMaxLen,