X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FLibrary%2FDxeHttpLib%2FDxeHttpLib.c;h=8b74554cd96157b2cb788b1c32f9c50a6664ef9f;hp=49fa80ba8c30d6a6bae20c299e90141b8b7c97d1;hb=c0fd7f734e2d33e22215899b40a47b843129541d;hpb=b8b65a0320c7f8ec797171b335dfda2456528d57 diff --git a/MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.c b/MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.c index 49fa80ba8c..8b74554cd9 100644 --- a/MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.c +++ b/MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.c @@ -2,86 +2,22 @@ This library is used to share code between UEFI network stack modules. It provides the helper routines to parse the HTTP message byte stream. -Copyright (c) 2015, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at
-http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#include -#include -#include -#include -#include -#include -#include - -#define BIT(x) (1 << x) - -#define NET_IS_HEX_CHAR(Ch) \ - ((('0' <= (Ch)) && ((Ch) <= '9')) || \ - (('A' <= (Ch)) && ((Ch) <= 'F')) || \ - (('a' <= (Ch)) && ((Ch) <= 'f'))) - -// -// Field index of the HTTP URL parse result. -// -#define HTTP_URI_FIELD_SCHEME 0 -#define HTTP_URI_FIELD_AUTHORITY 1 -#define HTTP_URI_FIELD_PATH 2 -#define HTTP_URI_FIELD_QUERY 3 -#define HTTP_URI_FIELD_FRAGMENT 4 -#define HTTP_URI_FIELD_USERINFO 5 -#define HTTP_URI_FIELD_HOST 6 -#define HTTP_URI_FIELD_PORT 7 -#define HTTP_URI_FIELD_MAX 8 - -// -// Structure to store the parse result of a HTTP URL. -// -typedef struct { - UINT32 Offset; - UINT32 Length; -} HTTP_URL_FILED_DATA; +#include "DxeHttpLib.h" -typedef struct { - UINT16 FieldBitMap; - HTTP_URL_FILED_DATA FieldData[HTTP_URI_FIELD_MAX]; -} HTTP_URL_PARSER; -typedef enum { - UrlParserUrlStart, - UrlParserScheme, - UrlParserSchemeColon, // ":" - UrlParserSchemeColonSlash, // ":/" - UrlParserSchemeColonSlashSlash, // "://" - UrlParserAuthority, - UrlParserAtInAuthority, - UrlParserPath, - UrlParserQueryStart, // "?" - UrlParserQuery, - UrlParserFragmentStart, // "#" - UrlParserFragment, - UrlParserUserInfo, - UrlParserHostStart, // "@" - UrlParserHost, - UrlParserHostIpv6, // "["(Ipv6 address) "]" - UrlParserPortStart, // ":" - UrlParserPort, - UrlParserStateMax -} HTTP_URL_PARSE_STATE; /** Decode a percent-encoded URI component to the ASCII character. - - Decode the input component in Buffer according to RFC 3986. The caller is responsible to make + + Decode the input component in Buffer according to RFC 3986. The caller is responsible to make sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer)) - in bytes. + in bytes. @param[in] Buffer The pointer to a percent-encoded URI component. @param[in] BufferLength Length of Buffer in bytes. @@ -90,7 +26,7 @@ typedef enum { @retval EFI_SUCCESS Successfully decoded the URI. @retval EFI_INVALID_PARAMETER Buffer is not a valid percent-encoded string. - + **/ EFI_STATUS EFIAPI @@ -108,13 +44,14 @@ UriPercentDecode ( if (Buffer == NULL || BufferLength == 0 || ResultBuffer == NULL) { return EFI_INVALID_PARAMETER; } - + Index = 0; Offset = 0; HexStr[2] = '\0'; while (Index < BufferLength) { if (Buffer[Index] == '%') { - if (!NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) { + if (Index + 1 >= BufferLength || Index + 2 >= BufferLength || + !NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) { return EFI_INVALID_PARAMETER; } HexStr[0] = Buffer[Index+1]; @@ -129,18 +66,18 @@ UriPercentDecode ( } *ResultLength = (UINT32) Offset; - + return EFI_SUCCESS; } /** - This function return the updated state accroding to the input state and next character of + This function return the updated state according to the input state and next character of the authority. @param[in] Char Next character. @param[in] State Current value of the parser state machine. - @param[in] IsRightBracket TRUE if there is an sign ']' in the authority component and - indicates the next part is ':' before Port. + @param[in] IsRightBracket TRUE if there is an sign ']' in the authority component and + indicates the next part is ':' before Port. @return Updated state value. **/ @@ -173,27 +110,27 @@ NetHttpParseAuthorityChar ( break; case UrlParserHost: - case UrlParserHostStart: + case UrlParserHostStart: if (Char == '[') { return UrlParserHostIpv6; } - + if (Char == ':') { return UrlParserPortStart; } - + return UrlParserHost; - - case UrlParserHostIpv6: + + case UrlParserHostIpv6: if (Char == ']') { *IsRightBracket = TRUE; } - + if (Char == ':' && *IsRightBracket) { return UrlParserPortStart; } return UrlParserHostIpv6; - + case UrlParserPort: case UrlParserPortStart: return UrlParserPort; @@ -213,7 +150,7 @@ NetHttpParseAuthorityChar ( @param[in, out] UrlParser Pointer to the buffer of the parse result. @retval EFI_SUCCESS Successfully parse the authority. - @retval Other Error happened. + @retval EFI_INVALID_PARAMETER The Url is invalid to parse the authority component. **/ EFI_STATUS @@ -230,7 +167,7 @@ NetHttpParseAuthority ( UINT32 Field; UINT32 OldField; BOOLEAN IsrightBracket; - + ASSERT ((UrlParser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0); // @@ -260,7 +197,7 @@ NetHttpParseAuthority ( case UrlParserUserInfo: Field = HTTP_URI_FIELD_USERINFO; break; - + case UrlParserHost: Field = HTTP_URI_FIELD_HOST; break; @@ -268,7 +205,7 @@ NetHttpParseAuthority ( case UrlParserHostIpv6: Field = HTTP_URI_FIELD_HOST; break; - + case UrlParserPort: Field = HTTP_URI_FIELD_PORT; break; @@ -299,7 +236,7 @@ NetHttpParseAuthority ( } /** - This function return the updated state accroding to the input state and next character of a URL. + This function return the updated state according to the input state and next character of a URL. @param[in] Char Next character. @param[in] State Current value of the parser state machine. @@ -316,12 +253,12 @@ NetHttpParseUrlChar ( if (Char == ' ' || Char == '\r' || Char == '\n') { return UrlParserStateMax; } - + // // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]] - // + // // Request-URI = "*" | absolute-URI | path-absolute | authority - // + // // absolute-URI = scheme ":" hier-part [ "?" query ] // path-absolute = "/" [ segment-nz *( "/" segment ) ] // authority = [ userinfo "@" ] host [ ":" port ] @@ -390,7 +327,7 @@ NetHttpParseUrlChar ( case UrlParserFragmentStart: return UrlParserFragment; - + default: break; } @@ -430,7 +367,9 @@ HttpParseUrl ( BOOLEAN FoundAt; EFI_STATUS Status; HTTP_URL_PARSER *Parser; - + + Parser = NULL; + if (Url == NULL || Length == 0 || UrlParser == NULL) { return EFI_INVALID_PARAMETER; } @@ -439,7 +378,7 @@ HttpParseUrl ( if (Parser == NULL) { return EFI_OUT_OF_RESOURCES; } - + if (IsConnectMethod) { // // According to RFC 2616, the authority form is only used by the CONNECT method. @@ -454,14 +393,15 @@ HttpParseUrl ( FoundAt = FALSE; for (Char = Url; Char < Url + Length; Char++) { // - // Update state machine accoring to next char. + // Update state machine according to next char. // State = NetHttpParseUrlChar (*Char, State); switch (State) { case UrlParserStateMax: + FreePool (Parser); return EFI_INVALID_PARAMETER; - + case UrlParserSchemeColon: case UrlParserSchemeColonSlash: case UrlParserSchemeColonSlashSlash: @@ -471,7 +411,7 @@ HttpParseUrl ( // Skip all the delimiting char: "://" "?" "@" // continue; - + case UrlParserScheme: Field = HTTP_URI_FIELD_SCHEME; break; @@ -522,12 +462,13 @@ HttpParseUrl ( if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0) { Status = NetHttpParseAuthority (Url, FoundAt, Parser); if (EFI_ERROR (Status)) { + FreePool (Parser); return Status; } } *UrlParser = Parser; - return EFI_SUCCESS; + return EFI_SUCCESS; } /** @@ -544,7 +485,7 @@ HttpParseUrl ( @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid. @retval EFI_NOT_FOUND No hostName component in the URL. @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. - + **/ EFI_STATUS EFIAPI @@ -563,7 +504,7 @@ HttpUrlGetHostName ( return EFI_INVALID_PARAMETER; } - Parser = (HTTP_URL_PARSER*) UrlParser; + Parser = (HTTP_URL_PARSER *) UrlParser; if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) { return EFI_NOT_FOUND; @@ -573,7 +514,7 @@ HttpUrlGetHostName ( if (Name == NULL) { return EFI_OUT_OF_RESOURCES; } - + Status = UriPercentDecode ( Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset, Parser->FieldData[HTTP_URI_FIELD_HOST].Length, @@ -581,6 +522,7 @@ HttpUrlGetHostName ( &ResultLength ); if (EFI_ERROR (Status)) { + FreePool (Name); return Status; } @@ -603,7 +545,7 @@ HttpUrlGetHostName ( @retval EFI_INVALID_PARAMETER Uri is NULL or Ip4Address is NULL or UrlParser is invalid. @retval EFI_NOT_FOUND No IPv4 address component in the URL. @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. - + **/ EFI_STATUS EFIAPI @@ -617,22 +559,22 @@ HttpUrlGetIp4 ( EFI_STATUS Status; UINT32 ResultLength; HTTP_URL_PARSER *Parser; - + if (Url == NULL || UrlParser == NULL || Ip4Address == NULL) { return EFI_INVALID_PARAMETER; } - Parser = (HTTP_URL_PARSER*) UrlParser; + Parser = (HTTP_URL_PARSER *) UrlParser; if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) { - return EFI_INVALID_PARAMETER; + return EFI_NOT_FOUND; } Ip4String = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1); if (Ip4String == NULL) { return EFI_OUT_OF_RESOURCES; } - + Status = UriPercentDecode ( Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset, Parser->FieldData[HTTP_URI_FIELD_HOST].Length, @@ -640,6 +582,7 @@ HttpUrlGetIp4 ( &ResultLength ); if (EFI_ERROR (Status)) { + FreePool (Ip4String); return Status; } @@ -663,7 +606,7 @@ HttpUrlGetIp4 ( @retval EFI_INVALID_PARAMETER Uri is NULL or Ip6Address is NULL or UrlParser is invalid. @retval EFI_NOT_FOUND No IPv6 address component in the URL. @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. - + **/ EFI_STATUS EFIAPI @@ -679,15 +622,15 @@ HttpUrlGetIp6 ( EFI_STATUS Status; UINT32 ResultLength; HTTP_URL_PARSER *Parser; - + if (Url == NULL || UrlParser == NULL || Ip6Address == NULL) { return EFI_INVALID_PARAMETER; } - Parser = (HTTP_URL_PARSER*) UrlParser; + Parser = (HTTP_URL_PARSER *) UrlParser; if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) { - return EFI_INVALID_PARAMETER; + return EFI_NOT_FOUND; } // @@ -707,7 +650,7 @@ HttpUrlGetIp6 ( if (Ip6String == NULL) { return EFI_OUT_OF_RESOURCES; } - + Status = UriPercentDecode ( Ptr + 1, Length - 2, @@ -715,9 +658,10 @@ HttpUrlGetIp6 ( &ResultLength ); if (EFI_ERROR (Status)) { + FreePool (Ip6String); return Status; } - + Ip6String[ResultLength] = '\0'; Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address); FreePool (Ip6String); @@ -738,7 +682,7 @@ HttpUrlGetIp6 ( @retval EFI_INVALID_PARAMETER Uri is NULL or Port is NULL or UrlParser is invalid. @retval EFI_NOT_FOUND No port number in the URL. @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. - + **/ EFI_STATUS EFIAPI @@ -748,19 +692,24 @@ HttpUrlGetPort ( OUT UINT16 *Port ) { - CHAR8 *PortString; - EFI_STATUS Status; - UINT32 ResultLength; + CHAR8 *PortString; + EFI_STATUS Status; + UINTN Index; + UINTN Data; + UINT32 ResultLength; HTTP_URL_PARSER *Parser; if (Url == NULL || UrlParser == NULL || Port == NULL) { return EFI_INVALID_PARAMETER; } - Parser = (HTTP_URL_PARSER*) UrlParser; + *Port = 0; + Index = 0; + + Parser = (HTTP_URL_PARSER *) UrlParser; if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) { - return EFI_INVALID_PARAMETER; + return EFI_NOT_FOUND; } PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1); @@ -775,20 +724,98 @@ HttpUrlGetPort ( &ResultLength ); if (EFI_ERROR (Status)) { - return Status; + goto ON_EXIT; } PortString[ResultLength] = '\0'; - *Port = (UINT16) AsciiStrDecimalToUintn (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset); - return EFI_SUCCESS; + while (Index < ResultLength) { + if (!NET_IS_DIGIT (PortString[Index])) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + Index ++; + } + + Status = AsciiStrDecimalToUintnS (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset, (CHAR8 **) NULL, &Data); + + if (Data > HTTP_URI_PORT_MAX_NUM) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + *Port = (UINT16) Data; + +ON_EXIT: + FreePool (PortString); + return Status; +} + +/** + Get the Path from a HTTP URL. + + This function will return the Path according to the Url and previous parse result,and + it is the caller's responsibility to free the buffer returned in *Path. + + @param[in] Url The pointer to a HTTP URL string. + @param[in] UrlParser URL Parse result returned by NetHttpParseUrl(). + @param[out] Path Pointer to a buffer to store the Path. + + @retval EFI_SUCCESS Successfully get the required component. + @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid. + @retval EFI_NOT_FOUND No hostName component in the URL. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. + +**/ +EFI_STATUS +EFIAPI +HttpUrlGetPath ( + IN CHAR8 *Url, + IN VOID *UrlParser, + OUT CHAR8 **Path + ) +{ + CHAR8 *PathStr; + EFI_STATUS Status; + UINT32 ResultLength; + HTTP_URL_PARSER *Parser; + + if (Url == NULL || UrlParser == NULL || Path == NULL) { + return EFI_INVALID_PARAMETER; + } + + Parser = (HTTP_URL_PARSER *) UrlParser; + + if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PATH)) == 0) { + return EFI_NOT_FOUND; + } + + PathStr = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PATH].Length + 1); + if (PathStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = UriPercentDecode ( + Url + Parser->FieldData[HTTP_URI_FIELD_PATH].Offset, + Parser->FieldData[HTTP_URI_FIELD_PATH].Length, + PathStr, + &ResultLength + ); + if (EFI_ERROR (Status)) { + FreePool (PathStr); + return Status; + } + + PathStr[ResultLength] = '\0'; + *Path = PathStr; + return EFI_SUCCESS; } /** Release the resource of the URL parser. @param[in] UrlParser Pointer to the parser. - + **/ VOID EFIAPI @@ -802,22 +829,23 @@ HttpUrlFreeParser ( /** Find a specified header field according to the field name. - @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] HeaderCount Number of HTTP header structures in Headers list. @param[in] Headers Array containing list of HTTP headers. - @param[in] FieldName Null terminated string which describes a field name. + @param[in] FieldName Null terminated string which describes a field name. @return Pointer to the found header or NULL. **/ EFI_HTTP_HEADER * -HttpIoFindHeader ( +EFIAPI +HttpFindHeader ( IN UINTN HeaderCount, IN EFI_HTTP_HEADER *Headers, IN CHAR8 *FieldName ) { UINTN Index; - + if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) { return NULL; } @@ -855,7 +883,7 @@ typedef struct { BOOLEAN IsChunked; // "chunked" transfer-coding. BOOLEAN ContentLengthIsValid; UINTN ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE - + HTTP_BODY_PARSER_CALLBACK Callback; VOID *Context; UINTN ParsedBodyLength; @@ -864,27 +892,6 @@ typedef struct { UINTN CurrentChunkParsedSize; } HTTP_BODY_PARSER; -/** - - Convert an Ascii char to its uppercase. - - @param[in] Char Ascii character. - - @return Uppercase value of the input Char. - -**/ -CHAR8 -HttpIoCharToUpper ( - IN CHAR8 Char - ) -{ - if (Char >= 'a' && Char <= 'z') { - return Char - ('a' - 'A'); - } - - return Char; -} - /** Convert an hexadecimal char to a value of type UINTN. @@ -902,7 +909,7 @@ HttpIoHexCharToUintn ( return Char - '0'; } - return (10 + HttpIoCharToUpper (Char) - 'A'); + return (10 + AsciiCharToUpper (Char) - 'A'); } /** @@ -924,14 +931,13 @@ HttpIoParseContentLengthHeader ( ) { EFI_HTTP_HEADER *Header; - - Header = HttpIoFindHeader (HeaderCount, Headers, "Content-Length"); + + Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH); if (Header == NULL) { return EFI_NOT_FOUND; } - *ContentLength = AsciiStrDecimalToUintn (Header->FieldValue); - return EFI_SUCCESS; + return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **) NULL, ContentLength); } /** @@ -942,7 +948,7 @@ HttpIoParseContentLengthHeader ( @param[in] Headers Array containing list of HTTP headers. @return The message is "chunked" transfer-coding (TRUE) or not (FALSE). - + **/ BOOLEAN HttpIoIsChunked ( @@ -953,7 +959,7 @@ HttpIoIsChunked ( EFI_HTTP_HEADER *Header; - Header = HttpIoFindHeader (HeaderCount, Headers, "Transfer-Encoding"); + Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING); if (Header == NULL) { return FALSE; } @@ -1039,7 +1045,7 @@ HttpInitMsgParser ( { EFI_STATUS Status; HTTP_BODY_PARSER *Parser; - + if (HeaderCount != 0 && Headers == NULL) { return EFI_INVALID_PARAMETER; } @@ -1054,9 +1060,9 @@ HttpInitMsgParser ( } Parser->State = BodyParserBodyStart; - + // - // Determine the message length accroding to RFC 2616. + // Determine the message length according to RFC 2616. // 1. Check whether the message "MUST NOT" have a message-body. // Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode); @@ -1075,7 +1081,7 @@ HttpInitMsgParser ( // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges". // 5. By server closing the connection // - + // // Set state to skip body parser if the message shouldn't have a message body. // @@ -1101,7 +1107,8 @@ HttpInitMsgParser ( @retval EFI_SUCCESS Successfully parse the message-body. @retval EFI_INVALID_PARAMETER MsgParser is NULL or Body is NULL or BodyLength is 0. - @retval Others Operation aborted. + @retval EFI_ABORTED Operation aborted. + @retval Other Error happened while parsing message body. **/ EFI_STATUS @@ -1117,7 +1124,7 @@ HttpParseMessageBody ( UINTN LengthForCallback; EFI_STATUS Status; HTTP_BODY_PARSER *Parser; - + if (BodyLength == 0 || Body == NULL) { return EFI_INVALID_PARAMETER; } @@ -1126,17 +1133,17 @@ HttpParseMessageBody ( return EFI_INVALID_PARAMETER; } - Parser = (HTTP_BODY_PARSER*) MsgParser; + Parser = (HTTP_BODY_PARSER *) MsgParser; if (Parser->IgnoreBody) { Parser->State = BodyParserComplete; if (Parser->Callback != NULL) { Status = Parser->Callback ( - BodyParseEventOnComplete, - Body, - 0, - Parser->Context - ); + BodyParseEventOnComplete, + Body, + 0, + Parser->Context + ); if (EFI_ERROR (Status)) { return Status; } @@ -1162,31 +1169,17 @@ HttpParseMessageBody ( case BodyParserStateMax: return EFI_ABORTED; - case BodyParserComplete: - if (Parser->Callback != NULL) { - Status = Parser->Callback ( - BodyParseEventOnComplete, - Char, - 0, - Parser->Context - ); - if (EFI_ERROR (Status)) { - return Status; - } - } - return EFI_SUCCESS; - case BodyParserBodyIdentity: // // Identity transfer-coding, just notify user to save the body data. // if (Parser->Callback != NULL) { Status = Parser->Callback ( - BodyParseEventOnData, - Char, - MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength), - Parser->Context - ); + BodyParseEventOnData, + Char, + MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength), + Parser->Context + ); if (EFI_ERROR (Status)) { return Status; } @@ -1195,6 +1188,17 @@ HttpParseMessageBody ( Parser->ParsedBodyLength += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength); if (Parser->ParsedBodyLength == Parser->ContentLength) { Parser->State = BodyParserComplete; + if (Parser->Callback != NULL) { + Status = Parser->Callback ( + BodyParseEventOnComplete, + Char, + 0, + Parser->Context + ); + if (EFI_ERROR (Status)) { + return Status; + } + } } break; @@ -1234,7 +1238,7 @@ HttpParseMessageBody ( } Char++; break; - + case BodyParserChunkSizeEndCR: if (*Char != '\n') { Parser->State = BodyParserStateMax; @@ -1243,7 +1247,7 @@ HttpParseMessageBody ( Char++; if (Parser->CurrentChunkSize == 0) { // - // The last chunk has been parsed and now assumed the state + // The last chunk has been parsed and now assumed the state // of HttpBodyParse is ParserLastCRLF. So it need to decide // whether the rest message is trailer or last CRLF in the next round. // @@ -1254,10 +1258,10 @@ HttpParseMessageBody ( Parser->State = BodyParserChunkDataStart; Parser->CurrentChunkParsedSize = 0; break; - + case BodyParserLastCRLF: // - // Judge the byte is belong to the Last CRLF or trailer, and then + // Judge the byte is belong to the Last CRLF or trailer, and then // configure the state of HttpBodyParse to corresponding state. // if (*Char == '\r') { @@ -1268,22 +1272,34 @@ HttpParseMessageBody ( Parser->State = BodyParserTrailer; break; } - + case BodyParserLastCRLFEnd: if (*Char == '\n') { Parser->State = BodyParserComplete; + Char++; + if (Parser->Callback != NULL) { + Status = Parser->Callback ( + BodyParseEventOnComplete, + Char, + 0, + Parser->Context + ); + if (EFI_ERROR (Status)) { + return Status; + } + } break; } else { Parser->State = BodyParserStateMax; break; } - + case BodyParserTrailer: if (*Char == '\r') { Parser->State = BodyParserChunkSizeEndCR; } Char++; - break; + break; case BodyParserChunkDataStart: // @@ -1293,11 +1309,11 @@ HttpParseMessageBody ( LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis); if (Parser->Callback != NULL) { Status = Parser->Callback ( - BodyParseEventOnData, - Char, - LengthForCallback, - Parser->Context - ); + BodyParseEventOnData, + Char, + LengthForCallback, + Parser->Context + ); if (EFI_ERROR (Status)) { return Status; } @@ -1307,7 +1323,7 @@ HttpParseMessageBody ( Parser->CurrentChunkParsedSize += LengthForCallback; if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) { Parser->State = BodyParserChunkDataEnd; - } + } break; case BodyParserChunkDataEnd: @@ -1326,7 +1342,7 @@ HttpParseMessageBody ( } Char++; Parser->State = BodyParserChunkSizeStart; - break; + break; default: break; @@ -1358,7 +1374,11 @@ HttpIsMessageComplete ( { HTTP_BODY_PARSER *Parser; - Parser = (HTTP_BODY_PARSER*) MsgParser; + if (MsgParser == NULL) { + return FALSE; + } + + Parser = (HTTP_BODY_PARSER *) MsgParser; if (Parser->State == BodyParserComplete) { return TRUE; @@ -1377,7 +1397,7 @@ HttpIsMessageComplete ( @retval EFI_SUCCESS Successfully to get the entity length. @retval EFI_NOT_READY Entity length is not valid yet. @retval EFI_INVALID_PARAMETER MsgParser is NULL or ContentLength is NULL. - + **/ EFI_STATUS EFIAPI @@ -1392,7 +1412,7 @@ HttpGetEntityLength ( return EFI_INVALID_PARAMETER; } - Parser = (HTTP_BODY_PARSER*) MsgParser; + Parser = (HTTP_BODY_PARSER *) MsgParser; if (!Parser->ContentLengthIsValid) { return EFI_NOT_READY; @@ -1406,7 +1426,7 @@ HttpGetEntityLength ( Release the resource of the message parser. @param[in] MsgParser Pointer to the message parser. - + **/ VOID EFIAPI @@ -1416,3 +1436,649 @@ HttpFreeMsgParser ( { FreePool (MsgParser); } + + +/** + Get the next string, which is distinguished by specified separator. + + @param[in] String Pointer to the string. + @param[in] Separator Specified separator used to distinguish where is the beginning + of next string. + + @return Pointer to the next string. + @return NULL if not find or String is NULL. + +**/ +CHAR8 * +AsciiStrGetNextToken ( + IN CONST CHAR8 *String, + IN CHAR8 Separator + ) +{ + CONST CHAR8 *Token; + + Token = String; + while (TRUE) { + if (*Token == 0) { + return NULL; + } + if (*Token == Separator) { + return (CHAR8 *)(Token + 1); + } + Token++; + } +} + +/** + Set FieldName and FieldValue into specified HttpHeader. + + @param[in,out] HttpHeader Specified HttpHeader. + @param[in] FieldName FieldName of this HttpHeader, a NULL terminated ASCII string. + @param[in] FieldValue FieldValue of this HttpHeader, a NULL terminated ASCII string. + + + @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully. + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +EFIAPI +HttpSetFieldNameAndValue ( + IN OUT EFI_HTTP_HEADER *HttpHeader, + IN CONST CHAR8 *FieldName, + IN CONST CHAR8 *FieldValue + ) +{ + UINTN FieldNameSize; + UINTN FieldValueSize; + + if (HttpHeader == NULL || FieldName == NULL || FieldValue == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (HttpHeader->FieldName != NULL) { + FreePool (HttpHeader->FieldName); + } + if (HttpHeader->FieldValue != NULL) { + FreePool (HttpHeader->FieldValue); + } + + FieldNameSize = AsciiStrSize (FieldName); + HttpHeader->FieldName = AllocateZeroPool (FieldNameSize); + if (HttpHeader->FieldName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize); + HttpHeader->FieldName[FieldNameSize - 1] = 0; + + FieldValueSize = AsciiStrSize (FieldValue); + HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize); + if (HttpHeader->FieldValue == NULL) { + FreePool (HttpHeader->FieldName); + return EFI_OUT_OF_RESOURCES; + } + CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize); + HttpHeader->FieldValue[FieldValueSize - 1] = 0; + + return EFI_SUCCESS; +} + +/** + Get one key/value header pair from the raw string. + + @param[in] String Pointer to the raw string. + @param[out] FieldName Points directly to field name within 'HttpHeader'. + @param[out] FieldValue Points directly to field value within 'HttpHeader'. + + @return Pointer to the next raw string. + @return NULL if no key/value header pair from this raw string. + +**/ +CHAR8 * +EFIAPI +HttpGetFieldNameAndValue ( + IN CHAR8 *String, + OUT CHAR8 **FieldName, + OUT CHAR8 **FieldValue + ) +{ + CHAR8 *FieldNameStr; + CHAR8 *FieldValueStr; + CHAR8 *StrPtr; + CHAR8 *EndofHeader; + + if (String == NULL || FieldName == NULL || FieldValue == NULL) { + return NULL; + } + + *FieldName = NULL; + *FieldValue = NULL; + FieldNameStr = NULL; + FieldValueStr = NULL; + StrPtr = NULL; + EndofHeader = NULL; + + + // + // Check whether the raw HTTP header string is valid or not. + // + EndofHeader = AsciiStrStr (String, "\r\n\r\n"); + if (EndofHeader == NULL) { + return NULL; + } + + // + // Each header field consists of a name followed by a colon (":") and the field value. + // The field value MAY be preceded by any amount of LWS, though a single SP is preferred. + // + // message-header = field-name ":" [ field-value ] + // field-name = token + // field-value = *( field-content | LWS ) + // + // Note: "*(element)" allows any number element, including zero; "1*(element)" requires at least one element. + // [element] means element is optional. + // LWS = [CRLF] 1*(SP|HT), it can be ' ' or '\t' or '\r\n ' or '\r\n\t'. + // CRLF = '\r\n'. + // SP = ' '. + // HT = '\t' (Tab). + // + FieldNameStr = String; + FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':'); + if (FieldValueStr == NULL) { + return NULL; + } + + // + // Replace ':' with 0, then FieldName has been retrived from String. + // + *(FieldValueStr - 1) = 0; + + // + // Handle FieldValueStr, skip all the preceded LWS. + // + while (TRUE) { + if (*FieldValueStr == ' ' || *FieldValueStr == '\t') { + // + // Boundary condition check. + // + if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 1) { + // + // Wrong String format! + // + return NULL; + } + + FieldValueStr ++; + } else if (*FieldValueStr == '\r') { + // + // Boundary condition check. + // + if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 3) { + // + // No more preceded LWS, so break here. + // + break; + } + + if (*(FieldValueStr + 1) == '\n' ) { + if (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t') { + FieldValueStr = FieldValueStr + 3; + } else { + // + // No more preceded LWS, so break here. + // + break; + } + } else { + // + // Wrong String format! + // + return NULL; + } + } else { + // + // No more preceded LWS, so break here. + // + break; + } + } + + StrPtr = FieldValueStr; + do { + // + // Handle the LWS within the field value. + // + StrPtr = AsciiStrGetNextToken (StrPtr, '\r'); + if (StrPtr == NULL || *StrPtr != '\n') { + // + // Wrong String format! + // + return NULL; + } + + StrPtr++; + } while (*StrPtr == ' ' || *StrPtr == '\t'); + + // + // Replace '\r' with 0 + // + *(StrPtr - 2) = 0; + + // + // Get FieldName and FieldValue. + // + *FieldName = FieldNameStr; + *FieldValue = FieldValueStr; + + return StrPtr; +} + +/** + Free existing HeaderFields. + + @param[in] HeaderFields Pointer to array of key/value header pairs waitting for free. + @param[in] FieldCount The number of header pairs in HeaderFields. + +**/ +VOID +EFIAPI +HttpFreeHeaderFields ( + IN EFI_HTTP_HEADER *HeaderFields, + IN UINTN FieldCount + ) +{ + UINTN Index; + + if (HeaderFields != NULL) { + for (Index = 0; Index < FieldCount; Index++) { + if (HeaderFields[Index].FieldName != NULL) { + FreePool (HeaderFields[Index].FieldName); + } + if (HeaderFields[Index].FieldValue != NULL) { + FreePool (HeaderFields[Index].FieldValue); + } + } + + FreePool (HeaderFields); + } +} + +/** + Generate HTTP request message. + + This function will allocate memory for the whole HTTP message and generate a + well formatted HTTP Request message in it, include the Request-Line, header + fields and also the message body. It is the caller's responsibility to free + the buffer returned in *RequestMsg. + + @param[in] Message Pointer to the EFI_HTTP_MESSAGE structure which + contains the required information to generate + the HTTP request message. + @param[in] Url The URL of a remote host. + @param[out] RequestMsg Pointer to the created HTTP request message. + NULL if any error occured. + @param[out] RequestMsgSize Size of the RequestMsg (in bytes). + + @retval EFI_SUCCESS If HTTP request string was created successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_INVALID_PARAMETER The input arguments are invalid. + +**/ +EFI_STATUS +EFIAPI +HttpGenRequestMessage ( + IN CONST EFI_HTTP_MESSAGE *Message, + IN CONST CHAR8 *Url, + OUT CHAR8 **RequestMsg, + OUT UINTN *RequestMsgSize + ) +{ + EFI_STATUS Status; + UINTN StrLength; + CHAR8 *RequestPtr; + UINTN HttpHdrSize; + UINTN MsgSize; + BOOLEAN Success; + VOID *HttpHdr; + EFI_HTTP_HEADER **AppendList; + UINTN Index; + EFI_HTTP_UTILITIES_PROTOCOL *HttpUtilitiesProtocol; + + Status = EFI_SUCCESS; + HttpHdrSize = 0; + MsgSize = 0; + Success = FALSE; + HttpHdr = NULL; + AppendList = NULL; + HttpUtilitiesProtocol = NULL; + + // + // 1. If we have a Request, we cannot have a NULL Url + // 2. If we have a Request, HeaderCount can not be non-zero + // 3. If we do not have a Request, HeaderCount should be zero + // 4. If we do not have Request and Headers, we need at least a message-body + // + if ((Message == NULL || RequestMsg == NULL || RequestMsgSize == NULL) || + (Message->Data.Request != NULL && Url == NULL) || + (Message->Data.Request != NULL && Message->HeaderCount == 0) || + (Message->Data.Request == NULL && Message->HeaderCount != 0) || + (Message->Data.Request == NULL && Message->HeaderCount == 0 && Message->BodyLength == 0)) { + return EFI_INVALID_PARAMETER; + } + + if (Message->HeaderCount != 0) { + // + // Locate the HTTP_UTILITIES protocol. + // + Status = gBS->LocateProtocol ( + &gEfiHttpUtilitiesProtocolGuid, + NULL, + (VOID **) &HttpUtilitiesProtocol + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR,"Failed to locate Http Utilities protocol. Status = %r.\n", Status)); + return Status; + } + + // + // Build AppendList to send into HttpUtilitiesBuild + // + AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount)); + if (AppendList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for(Index = 0; Index < Message->HeaderCount; Index++){ + AppendList[Index] = &Message->Headers[Index]; + } + + // + // Build raw HTTP Headers + // + Status = HttpUtilitiesProtocol->Build ( + HttpUtilitiesProtocol, + 0, + NULL, + 0, + NULL, + Message->HeaderCount, + AppendList, + &HttpHdrSize, + &HttpHdr + ); + + FreePool (AppendList); + + if (EFI_ERROR (Status) || HttpHdr == NULL){ + return Status; + } + } + + // + // If we have headers to be sent, account for it. + // + if (Message->HeaderCount != 0) { + MsgSize = HttpHdrSize; + } + + // + // If we have a request line, account for the fields. + // + if (Message->Data.Request != NULL) { + MsgSize += HTTP_METHOD_MAXIMUM_LEN + AsciiStrLen (HTTP_VERSION_CRLF_STR) + AsciiStrLen (Url); + } + + + // + // If we have a message body to be sent, account for it. + // + MsgSize += Message->BodyLength; + + // + // memory for the string that needs to be sent to TCP + // + *RequestMsg = NULL; + *RequestMsg = AllocateZeroPool (MsgSize); + if (*RequestMsg == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + RequestPtr = *RequestMsg; + // + // Construct header request + // + if (Message->Data.Request != NULL) { + switch (Message->Data.Request->Method) { + case HttpMethodGet: + StrLength = sizeof (HTTP_METHOD_GET) - 1; + CopyMem (RequestPtr, HTTP_METHOD_GET, StrLength); + RequestPtr += StrLength; + break; + case HttpMethodPut: + StrLength = sizeof (HTTP_METHOD_PUT) - 1; + CopyMem (RequestPtr, HTTP_METHOD_PUT, StrLength); + RequestPtr += StrLength; + break; + case HttpMethodPatch: + StrLength = sizeof (HTTP_METHOD_PATCH) - 1; + CopyMem (RequestPtr, HTTP_METHOD_PATCH, StrLength); + RequestPtr += StrLength; + break; + case HttpMethodPost: + StrLength = sizeof (HTTP_METHOD_POST) - 1; + CopyMem (RequestPtr, HTTP_METHOD_POST, StrLength); + RequestPtr += StrLength; + break; + case HttpMethodHead: + StrLength = sizeof (HTTP_METHOD_HEAD) - 1; + CopyMem (RequestPtr, HTTP_METHOD_HEAD, StrLength); + RequestPtr += StrLength; + break; + case HttpMethodDelete: + StrLength = sizeof (HTTP_METHOD_DELETE) - 1; + CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength); + RequestPtr += StrLength; + break; + default: + ASSERT (FALSE); + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + StrLength = AsciiStrLen(EMPTY_SPACE); + CopyMem (RequestPtr, EMPTY_SPACE, StrLength); + RequestPtr += StrLength; + + StrLength = AsciiStrLen (Url); + CopyMem (RequestPtr, Url, StrLength); + RequestPtr += StrLength; + + StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1; + CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength); + RequestPtr += StrLength; + + if (HttpHdr != NULL) { + // + // Construct header + // + CopyMem (RequestPtr, HttpHdr, HttpHdrSize); + RequestPtr += HttpHdrSize; + } + } + + // + // Construct body + // + if (Message->Body != NULL) { + CopyMem (RequestPtr, Message->Body, Message->BodyLength); + RequestPtr += Message->BodyLength; + } + + // + // Done + // + (*RequestMsgSize) = (UINTN)(RequestPtr) - (UINTN)(*RequestMsg); + Success = TRUE; + +Exit: + + if (!Success) { + if (*RequestMsg != NULL) { + FreePool (*RequestMsg); + } + *RequestMsg = NULL; + return Status; + } + + if (HttpHdr != NULL) { + FreePool (HttpHdr); + } + + return EFI_SUCCESS; +} + +/** + Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined + in UEFI 2.5 specification. + + @param[in] StatusCode The status code value in HTTP message. + + @return Value defined in EFI_HTTP_STATUS_CODE . + +**/ +EFI_HTTP_STATUS_CODE +EFIAPI +HttpMappingToStatusCode ( + IN UINTN StatusCode + ) +{ + switch (StatusCode) { + case 100: + return HTTP_STATUS_100_CONTINUE; + case 101: + return HTTP_STATUS_101_SWITCHING_PROTOCOLS; + case 200: + return HTTP_STATUS_200_OK; + case 201: + return HTTP_STATUS_201_CREATED; + case 202: + return HTTP_STATUS_202_ACCEPTED; + case 203: + return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION; + case 204: + return HTTP_STATUS_204_NO_CONTENT; + case 205: + return HTTP_STATUS_205_RESET_CONTENT; + case 206: + return HTTP_STATUS_206_PARTIAL_CONTENT; + case 300: + return HTTP_STATUS_300_MULTIPLE_CHOICES; + case 301: + return HTTP_STATUS_301_MOVED_PERMANENTLY; + case 302: + return HTTP_STATUS_302_FOUND; + case 303: + return HTTP_STATUS_303_SEE_OTHER; + case 304: + return HTTP_STATUS_304_NOT_MODIFIED; + case 305: + return HTTP_STATUS_305_USE_PROXY; + case 307: + return HTTP_STATUS_307_TEMPORARY_REDIRECT; + case 308: + return HTTP_STATUS_308_PERMANENT_REDIRECT; + case 400: + return HTTP_STATUS_400_BAD_REQUEST; + case 401: + return HTTP_STATUS_401_UNAUTHORIZED; + case 402: + return HTTP_STATUS_402_PAYMENT_REQUIRED; + case 403: + return HTTP_STATUS_403_FORBIDDEN; + case 404: + return HTTP_STATUS_404_NOT_FOUND; + case 405: + return HTTP_STATUS_405_METHOD_NOT_ALLOWED; + case 406: + return HTTP_STATUS_406_NOT_ACCEPTABLE; + case 407: + return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED; + case 408: + return HTTP_STATUS_408_REQUEST_TIME_OUT; + case 409: + return HTTP_STATUS_409_CONFLICT; + case 410: + return HTTP_STATUS_410_GONE; + case 411: + return HTTP_STATUS_411_LENGTH_REQUIRED; + case 412: + return HTTP_STATUS_412_PRECONDITION_FAILED; + case 413: + return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE; + case 414: + return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE; + case 415: + return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE; + case 416: + return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED; + case 417: + return HTTP_STATUS_417_EXPECTATION_FAILED; + case 500: + return HTTP_STATUS_500_INTERNAL_SERVER_ERROR; + case 501: + return HTTP_STATUS_501_NOT_IMPLEMENTED; + case 502: + return HTTP_STATUS_502_BAD_GATEWAY; + case 503: + return HTTP_STATUS_503_SERVICE_UNAVAILABLE; + case 504: + return HTTP_STATUS_504_GATEWAY_TIME_OUT; + case 505: + return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED; + + default: + return HTTP_STATUS_UNSUPPORTED_STATUS; + } +} + +/** + Check whether header field called FieldName is in DeleteList. + + @param[in] DeleteList Pointer to array of key/value header pairs. + @param[in] DeleteCount The number of header pairs. + @param[in] FieldName Pointer to header field's name. + + @return TRUE if FieldName is not in DeleteList, that means this header field is valid. + @return FALSE if FieldName is in DeleteList, that means this header field is invalid. + +**/ +BOOLEAN +EFIAPI +HttpIsValidHttpHeader ( + IN CHAR8 *DeleteList[], + IN UINTN DeleteCount, + IN CHAR8 *FieldName + ) +{ + UINTN Index; + + if (FieldName == NULL) { + return FALSE; + } + + for (Index = 0; Index < DeleteCount; Index++) { + if (DeleteList[Index] == NULL) { + continue; + } + + if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) { + return FALSE; + } + } + + return TRUE; +} +