EFI_STATUS ConvertIpStringToEfiIp ( IN CHAR8 *PathName, OUT EFI_IP_ADDRESS *ServerIp ) { CHAR8 *Str; Str = PathName; ServerIp->v4.Addr[0] = (UINT8)AsciiStrDecimalToUintn (Str); Str = AsciiStrStr (Str, "."); if (Str == NULL) { return EFI_DEVICE_ERROR; } ServerIp->v4.Addr[1] = (UINT8)AsciiStrDecimalToUintn (++Str); Str = AsciiStrStr (Str, "."); if (Str == NULL) { return EFI_DEVICE_ERROR; } ServerIp->v4.Addr[2] = (UINT8)AsciiStrDecimalToUintn (++Str); Str = AsciiStrStr (Str, "."); if (Str == NULL) { return EFI_DEVICE_ERROR; } ServerIp->v4.Addr[3] = (UINT8)AsciiStrDecimalToUintn (++Str); return EFI_SUCCESS; }
/** Get section entry decimal UINTN value. @param[in] Context INI Config file context. @param[in] SectionName Section name. @param[in] EntryName Section entry name. @param[out] Data Point to the got decimal UINTN value. @retval EFI_SUCCESS Section entry decimal UINTN value is got. @retval EFI_NOT_FOUND Section is not found. **/ EFI_STATUS EFIAPI GetDecimalUintnFromDataFile ( IN VOID *Context, IN CHAR8 *SectionName, IN CHAR8 *EntryName, OUT UINTN *Data ) { CHAR8 *Value; EFI_STATUS Status; if (Context == NULL || SectionName == NULL || EntryName == NULL || Data == NULL) { return EFI_INVALID_PARAMETER; } Status = GetStringFromDataFile( Context, SectionName, EntryName, &Value ); if (EFI_ERROR(Status)) { return EFI_NOT_FOUND; } ASSERT (Value != NULL); if (!IsValidDecimalString(Value, AsciiStrLen(Value))) { return EFI_NOT_FOUND; } *Data = AsciiStrDecimalToUintn(Value); return EFI_SUCCESS; }
EFI_STATUS EFIAPI EblSleepCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { UINTN Delay; Delay = (Argc == 1)? 10 : AsciiStrDecimalToUintn (Argv[1]); gBS->Stall (Delay * 1000000); return EFI_SUCCESS; }
/** Get the value of the content length if there is a "Content-Length" header. @param[in] HeaderCount Number of HTTP header structures in Headers. @param[in] Headers Array containing list of HTTP headers. @param[out] ContentLength Pointer to save the value of the content length. @retval EFI_SUCCESS Successfully get the content length. @retval EFI_NOT_FOUND No "Content-Length" header in the Headers. **/ EFI_STATUS HttpIoParseContentLengthHeader ( IN UINTN HeaderCount, IN EFI_HTTP_HEADER *Headers, OUT UINTN *ContentLength ) { EFI_HTTP_HEADER *Header; Header = HttpIoFindHeader (HeaderCount, Headers, "Content-Length"); if (Header == NULL) { return EFI_NOT_FOUND; } *ContentLength = AsciiStrDecimalToUintn (Header->FieldValue); return EFI_SUCCESS; }
/** Get the port number from a HTTP URL. This function will return the port number according to the Url and previous parse result. @param[in] Url The pointer to a HTTP URL string. @param[in] UrlParser URL Parse result returned by NetHttpParseUrl(). @param[out] Port Pointer to a buffer to store the port number. @retval EFI_SUCCESS Successfully get the required component. @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 HttpUrlGetPort ( IN CHAR8 *Url, IN VOID *UrlParser, OUT UINT16 *Port ) { CHAR8 *PortString; EFI_STATUS Status; UINT32 ResultLength; HTTP_URL_PARSER *Parser; if (Url == NULL || UrlParser == NULL || Port == NULL) { return EFI_INVALID_PARAMETER; } Parser = (HTTP_URL_PARSER*) UrlParser; if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) { return EFI_INVALID_PARAMETER; } PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1); if (PortString == NULL) { return EFI_OUT_OF_RESOURCES; } Status = UriPercentDecode ( Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset, Parser->FieldData[HTTP_URI_FIELD_PORT].Length, PortString, &ResultLength ); if (EFI_ERROR (Status)) { return Status; } PortString[ResultLength] = '\0'; *Port = (UINT16) AsciiStrDecimalToUintn (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset); return EFI_SUCCESS; }
/** Internal work function to extract a device number from a string skipping text. Easy way to extract numbers from strings like blk7:. @param Str String to extract device number form @return -1 Device string is not valid @return Device # **/ UINTN EblConvertDevStringToNumber ( IN CHAR8 *Str ) { UINTN Max; UINTN Index; // Find the first digit Max = AsciiStrLen (Str); for (Index = 0; !((*Str >= '0') && (*Str <= '9')) && (Index < Max); Index++) { Str++; } if (Index == Max) { return (UINTN)-1; } return AsciiStrDecimalToUintn (Str); }
/** Pause until a key is pressed and abort the remaining commands on the command line. If no key is pressed continue processing the command line. This command allows the user to stop an operation from happening and return control to the command prompt. Argv[0] - "pause" Argv[1] - timeout value is decimal seconds @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS Timeout expired with no input @return EFI_TIMEOUT Stop processing other commands on the same command line **/ EFI_STATUS EFIAPI EblPauseCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; UINTN Delay; EFI_INPUT_KEY Key; Delay = (Argc == 1)? 10 : AsciiStrDecimalToUintn (Argv[1]); AsciiPrint ("Hit any key to break. You have %3d seconds", Delay); Status = EblGetCharKey (&Key, Delay, EblPauseCallback); AsciiPrint ("\n"); // If we timeout then the pause succeeded thus return success // If we get a key return timeout to stop other command on this cmd line return (Status == EFI_SUCCESS) ? EFI_TIMEOUT : EFI_SUCCESS;; }
/** The atoi function converts the initial portion of the string pointed to by nptr to int representation. Except for the behavior on error, it is equivalent to: - (int)strtol(nptr, (char **)NULL, 10) @return The atoi function returns the converted value. **/ int atoi(const char *nptr) { int Retval; BOOLEAN Negative = FALSE; while(isspace((const unsigned char)*nptr)) ++nptr; // Skip leading spaces if(*nptr == '+') { Negative = FALSE; ++nptr; } else if(*nptr == '-') { Negative = TRUE; ++nptr; } Retval = (int)AsciiStrDecimalToUintn(nptr); if(Negative) { Retval = -Retval; } return Retval; }
/** See if command contains .# where # is a number. Return # as the Width or 1 as the default Width for commands. Example hexdump.4 returns a width of 4. @param Argv Argv[0] is the command name @return Width of command **/ UINTN WidthFromCommandName ( IN CHAR8 *Argv, IN UINTN Default ) { CHAR8 *Str; UINTN Width; //Hexdump.2 HexDump.4 mean use a different width Str = AsciiStrStr (Argv, "."); if (Str != NULL) { Width = AsciiStrDecimalToUintn (Str + 1); if (Width == 0) { Width = Default; } } else { // Default answer return Default; } return Width; }
/** This function checks the received iSCSI Login Response during the security negotiation stage. @param[in] Conn The iSCSI connection. @retval EFI_SUCCESS The Login Response passed the CHAP validation. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. @retval EFI_PROTOCOL_ERROR Some kind of protocol error happend. @retval Others Other errors as indicated. **/ EFI_STATUS IScsiCHAPOnRspReceived ( IN ISCSI_CONNECTION *Conn ) { EFI_STATUS Status; ISCSI_SESSION *Session; ISCSI_CHAP_AUTH_DATA *AuthData; CHAR8 *Value; UINT8 *Data; UINT32 Len; LIST_ENTRY *KeyValueList; UINTN Algorithm; CHAR8 *Identifier; CHAR8 *Challenge; CHAR8 *Name; CHAR8 *Response; UINT8 TargetRsp[ISCSI_CHAP_RSP_LEN]; UINT32 RspLen; ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION); ASSERT (Conn->RspQue.BufNum != 0); Session = Conn->Session; AuthData = &Session->AuthData; Len = Conn->RspQue.BufSize; Data = AllocatePool (Len); if (Data == NULL) { return EFI_OUT_OF_RESOURCES; } // // Copy the data in case the data spans over multiple PDUs. // NetbufQueCopy (&Conn->RspQue, 0, Len, Data); // // Build the key-value list from the data segment of the Login Response. // KeyValueList = IScsiBuildKeyValueList ((CHAR8 *) Data, Len); if (KeyValueList == NULL) { FreePool (Data); return EFI_OUT_OF_RESOURCES; } Status = EFI_PROTOCOL_ERROR; switch (Conn->CHAPStep) { case ISCSI_CHAP_INITIAL: // // The first Login Response. // Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG); if (Value == NULL) { goto ON_EXIT; } Session->TargetPortalGroupTag = (UINT16) AsciiStrDecimalToUintn (Value); Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD); if (Value == NULL) { goto ON_EXIT; } // // Initiator mandates CHAP authentication but target replies without "CHAP" or // initiator suggets "None" but target replies with some kind of auth method. // if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) == 0) { if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_NONE) { goto ON_EXIT; } } else { if (AuthData->AuthConfig.CHAPType != ISCSI_CHAP_NONE) { goto ON_EXIT; } } // // Transit to CHAP step one. // Conn->CHAPStep = ISCSI_CHAP_STEP_ONE; Status = EFI_SUCCESS; break; case ISCSI_CHAP_STEP_TWO: // // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C> // Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_ALGORITHM); if (Value == NULL) { goto ON_EXIT; } Algorithm = AsciiStrDecimalToUintn (Value); if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) { // // Unsupported algorithm is chosen by target. // goto ON_EXIT; } Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER); if (Identifier == NULL) { goto ON_EXIT; } Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE); if (Challenge == NULL) { goto ON_EXIT; } // // Process the CHAP identifier and CHAP Challenge from Target // Calculate Response value // AuthData->InIdentifier = (UINT32) AsciiStrDecimalToUintn (Identifier); AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN; IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge); Status = IScsiCHAPCalculateResponse ( AuthData->InIdentifier, AuthData->AuthConfig.CHAPSecret, (UINT32) AsciiStrLen (AuthData->AuthConfig.CHAPSecret), AuthData->InChallenge, AuthData->InChallengeLength, AuthData->CHAPResponse ); // // Transit to next step. // Conn->CHAPStep = ISCSI_CHAP_STEP_THREE; break; case ISCSI_CHAP_STEP_THREE: // // one way CHAP authentication and the target would like to // authenticate us. // Status = EFI_SUCCESS; break; case ISCSI_CHAP_STEP_FOUR: ASSERT (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_MUTUAL); // // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target. // Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME); if (Name == NULL) { goto ON_EXIT; } Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE); if (Response == NULL) { goto ON_EXIT; } RspLen = ISCSI_CHAP_RSP_LEN; IScsiHexToBin (TargetRsp, &RspLen, Response); // // Check the CHAP Response replied by Target. // Status = IScsiCHAPAuthTarget (AuthData, TargetRsp); break; default: break; } ON_EXIT: IScsiFreeKeyValueList (KeyValueList); FreePool (Data); return Status; }
/** Worker function that gets the size in numbers of bytes of a file from a TFTP server before to download the file. @param[in] Mtftp4 MTFTP4 protocol interface @param[in] FilePath Path of the file, ASCII encoded @param[out] FileSize Address where to store the file size in number of bytes. @retval EFI_SUCCESS The size of the file was returned. @retval EFI_UNSUPPORTED The server does not support the "tsize" option. @retval Others Error when retrieving the information from the server (see EFI_MTFTP4_PROTOCOL.GetInfo() status codes) or error when parsing the response of the server. **/ STATIC EFI_STATUS GetFileSize ( IN EFI_MTFTP4_PROTOCOL *Mtftp4, IN CONST CHAR8 *FilePath, OUT UINTN *FileSize ) { EFI_STATUS Status; EFI_MTFTP4_OPTION ReqOpt[1]; EFI_MTFTP4_PACKET *Packet; UINT32 PktLen; EFI_MTFTP4_OPTION *TableOfOptions; EFI_MTFTP4_OPTION *Option; UINT32 OptCnt; UINT8 OptBuf[128]; ReqOpt[0].OptionStr = (UINT8*)"tsize"; OptBuf[0] = '0'; OptBuf[1] = 0; ReqOpt[0].ValueStr = OptBuf; Status = Mtftp4->GetInfo ( Mtftp4, NULL, (UINT8*)FilePath, NULL, 1, ReqOpt, &PktLen, &Packet ); if (EFI_ERROR (Status)) { goto Error; } Status = Mtftp4->ParseOptions ( Mtftp4, PktLen, Packet, (UINT32 *) &OptCnt, &TableOfOptions ); if (EFI_ERROR (Status)) { goto Error; } Option = TableOfOptions; while (OptCnt != 0) { if (AsciiStrnCmp ((CHAR8 *)Option->OptionStr, "tsize", 5) == 0) { *FileSize = AsciiStrDecimalToUintn ((CHAR8 *)Option->ValueStr); break; } OptCnt--; Option++; } FreePool (TableOfOptions); if (OptCnt == 0) { Status = EFI_UNSUPPORTED; } Error : return Status; }
/** The work function of EfiHttpResponse(). @param[in] Wrap Pointer to HTTP token's wrap data. @retval EFI_SUCCESS Allocation succeeded. @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources. @retval EFI_NOT_READY Can't find a corresponding TxToken. **/ EFI_STATUS HttpResponseWorker ( IN HTTP_TOKEN_WRAP *Wrap ) { EFI_STATUS Status; EFI_HTTP_MESSAGE *HttpMsg; EFI_TCP4_IO_TOKEN *RxToken; EFI_TCP4_PROTOCOL *Tcp4; CHAR8 *EndofHeader; CHAR8 *HttpHeaders; UINTN SizeofHeaders; CHAR8 *Buffer; UINTN BufferSize; UINTN StatusCode; CHAR8 *Tmp; CHAR8 *HeaderTmp; CHAR8 *StatusCodeStr; UINTN BodyLen; HTTP_PROTOCOL *HttpInstance; EFI_HTTP_TOKEN *Token; NET_MAP_ITEM *Item; HTTP_TOKEN_WRAP *ValueInItem; UINTN HdrLen; if (Wrap == NULL || Wrap->HttpInstance == NULL) { return EFI_INVALID_PARAMETER; } HttpInstance = Wrap->HttpInstance; Token = Wrap->HttpToken; HttpMsg = Token->Message; Tcp4 = HttpInstance->Tcp4; ASSERT (Tcp4 != NULL); HttpMsg->Headers = NULL; HttpHeaders = NULL; SizeofHeaders = 0; Buffer = NULL; BufferSize = 0; EndofHeader = NULL; if (HttpMsg->Data.Response != NULL) { // // Need receive the HTTP headers, prepare buffer. // Status = HttpCreateTcp4RxEventForHeader (HttpInstance); if (EFI_ERROR (Status)) { goto Error; } // // Check whether we have cached header from previous call. // if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) { // // The data is stored at [NextMsg, CacheBody + CacheLen]. // HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg; HttpHeaders = AllocateZeroPool (HdrLen); if (HttpHeaders == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen); FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; HttpInstance->NextMsg = NULL; HttpInstance->CacheOffset = 0; SizeofHeaders = HdrLen; BufferSize = HttpInstance->CacheLen; // // Check whether we cached the whole HTTP headers. // EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); } RxToken = &HttpInstance->RxToken; RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } // // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. // while (EndofHeader == NULL) { HttpInstance->IsRxDone = FALSE; RxToken->Packet.RxData->DataLength = DEF_BUF_LEN; RxToken->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; Status = Tcp4->Receive (Tcp4, RxToken); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); goto Error; } while (!HttpInstance->IsRxDone) { Tcp4->Poll (Tcp4); } Status = RxToken->CompletionToken.Status; if (EFI_ERROR (Status)) { goto Error; } // // Append the response string. // BufferSize = SizeofHeaders + RxToken->Packet.RxData->FragmentTable[0].FragmentLength; Buffer = AllocateZeroPool (BufferSize); if (Buffer == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } if (HttpHeaders != NULL) { CopyMem (Buffer, HttpHeaders, SizeofHeaders); FreePool (HttpHeaders); } CopyMem ( Buffer + SizeofHeaders, RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer, RxToken->Packet.RxData->FragmentTable[0].FragmentLength ); HttpHeaders = Buffer; SizeofHeaders = BufferSize; // // Check whether we received end of HTTP headers. // EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); }; // // Skip the CRLF after the HTTP headers. // EndofHeader = EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR); // // Cache the part of body. // BodyLen = BufferSize - (EndofHeader - HttpHeaders); if (BodyLen > 0) { if (HttpInstance->CacheBody != NULL) { FreePool (HttpInstance->CacheBody); } HttpInstance->CacheBody = AllocateZeroPool (BodyLen); if (HttpInstance->CacheBody == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen); HttpInstance->CacheLen = BodyLen; } FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; // // Search for Status Code. // StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1; if (StatusCodeStr == NULL) { goto Error; } StatusCode = AsciiStrDecimalToUintn (StatusCodeStr); // // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n". // Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR); if (Tmp == NULL) { goto Error; } Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR); SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders); HeaderTmp = AllocateZeroPool (SizeofHeaders); if (HeaderTmp == NULL) { goto Error; } CopyMem (HeaderTmp, Tmp, SizeofHeaders); FreePool (HttpHeaders); HttpHeaders = HeaderTmp; // // Parse the HTTP header into array of key/value pairs. // Status = HttpUtilitiesParse (HttpHeaders, SizeofHeaders, &HttpMsg->Headers, &HttpMsg->HeaderCount); if (EFI_ERROR (Status)) { goto Error; } FreePool (HttpHeaders); HttpHeaders = NULL; HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode); // // Init message-body parser by header information. // Status = EFI_NOT_READY; ValueInItem = NULL; NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem); if (ValueInItem == NULL) { goto Error; } // // The first TxToken not transmitted yet, insert back and return error. // if (!ValueInItem->TcpWrap.IsTxDone) { goto Error2; } Status = HttpInitMsgParser ( ValueInItem->TcpWrap.Method, HttpMsg->Data.Response->StatusCode, HttpMsg->HeaderCount, HttpMsg->Headers, HttpBodyParserCallback, (VOID *) ValueInItem, &HttpInstance->MsgParser ); if (EFI_ERROR (Status)) { goto Error2; } // // Check whether we received a complete HTTP message. // if (HttpInstance->CacheBody != NULL) { Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody); if (EFI_ERROR (Status)) { goto Error2; } if (HttpIsMessageComplete (HttpInstance->MsgParser)) { // // Free the MsgParse since we already have a full HTTP message. // HttpFreeMsgParser (HttpInstance->MsgParser); HttpInstance->MsgParser = NULL; } } if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { Status = EFI_SUCCESS; goto Exit; } } // // Receive the response body. // BodyLen = 0; // // First check whether we cached some data. // if (HttpInstance->CacheBody != NULL) { // // Calculate the length of the cached data. // if (HttpInstance->NextMsg != NULL) { // // We have a cached HTTP message which includes a part of HTTP header of next message. // BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); } else { BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset; } if (BodyLen > 0) { // // We have some cached data. Just copy the data and return. // if (HttpMsg->BodyLength < BodyLen) { CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength); HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength; } else { // // Copy all cached data out. // CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen); HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset; HttpMsg->BodyLength = BodyLen; if (HttpInstance->NextMsg == NULL) { // // There is no HTTP header of next message. Just free the cache buffer. // FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; HttpInstance->NextMsg = NULL; HttpInstance->CacheOffset = 0; } } // // Return since we aready received required data. // Status = EFI_SUCCESS; goto Exit; } if (BodyLen == 0 && HttpInstance->MsgParser == NULL) { // // We received a complete HTTP message, and we don't have more data to return to caller. // HttpMsg->BodyLength = 0; Status = EFI_SUCCESS; goto Exit; } } ASSERT (HttpInstance->MsgParser != NULL); // // We still need receive more data when there is no cache data and MsgParser is not NULL; // RxToken = &Wrap->TcpWrap.RxToken; RxToken->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; RxToken->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; RxToken->CompletionToken.Status = EFI_NOT_READY; Status = Tcp4->Receive (Tcp4, RxToken); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); goto Error; } return Status; Exit: Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); if (Item != NULL) { NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); } Token->Status = Status; gBS->SignalEvent (Token->Event); FreePool (Wrap); return Status; Error2: NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); Error: if (Wrap != NULL) { if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); } RxToken = &Wrap->TcpWrap.RxToken; if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; } FreePool (Wrap); } if (HttpInstance->RxToken.CompletionToken.Event != NULL) { gBS->CloseEvent (HttpInstance->RxToken.CompletionToken.Event); HttpInstance->RxToken.CompletionToken.Event = NULL; } RxToken = &HttpInstance->RxToken; if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; } if (HttpHeaders != NULL) { FreePool (HttpHeaders); } if (HttpMsg->Headers != NULL) { FreePool (HttpMsg->Headers); } if (HttpInstance->CacheBody != NULL) { FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; } Token->Status = Status; gBS->SignalEvent (Token->Event); return Status; }
/** The work function of EfiHttpResponse(). @param[in] Wrap Pointer to HTTP token's wrap data. @retval EFI_SUCCESS Allocation succeeded. @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources. @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or the EFI_HTTP_UTILITIES_PROTOCOL is not available. **/ EFI_STATUS HttpResponseWorker ( IN HTTP_TOKEN_WRAP *Wrap ) { EFI_STATUS Status; EFI_HTTP_MESSAGE *HttpMsg; CHAR8 *EndofHeader; CHAR8 *HttpHeaders; UINTN SizeofHeaders; UINTN BufferSize; UINTN StatusCode; CHAR8 *Tmp; CHAR8 *HeaderTmp; CHAR8 *StatusCodeStr; UINTN BodyLen; HTTP_PROTOCOL *HttpInstance; EFI_HTTP_TOKEN *Token; NET_MAP_ITEM *Item; HTTP_TOKEN_WRAP *ValueInItem; UINTN HdrLen; if (Wrap == NULL || Wrap->HttpInstance == NULL) { return EFI_INVALID_PARAMETER; } HttpInstance = Wrap->HttpInstance; Token = Wrap->HttpToken; HttpMsg = Token->Message; HttpInstance->EndofHeader = NULL; HttpInstance->HttpHeaders = NULL; HttpMsg->Headers = NULL; HttpHeaders = NULL; SizeofHeaders = 0; BufferSize = 0; EndofHeader = NULL; if (HttpMsg->Data.Response != NULL) { // // Need receive the HTTP headers, prepare buffer. // Status = HttpCreateTcpRxEventForHeader (HttpInstance); if (EFI_ERROR (Status)) { goto Error; } // // Check whether we have cached header from previous call. // if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) { // // The data is stored at [NextMsg, CacheBody + CacheLen]. // HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg; HttpHeaders = AllocateZeroPool (HdrLen); if (HttpHeaders == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen); FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; HttpInstance->NextMsg = NULL; HttpInstance->CacheOffset = 0; SizeofHeaders = HdrLen; BufferSize = HttpInstance->CacheLen; // // Check whether we cached the whole HTTP headers. // EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); } HttpInstance->EndofHeader = &EndofHeader; HttpInstance->HttpHeaders = &HttpHeaders; Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize); if (EFI_ERROR (Status)) { goto Error; } ASSERT (HttpHeaders != NULL); // // Cache the part of body. // BodyLen = BufferSize - (EndofHeader - HttpHeaders); if (BodyLen > 0) { if (HttpInstance->CacheBody != NULL) { FreePool (HttpInstance->CacheBody); } HttpInstance->CacheBody = AllocateZeroPool (BodyLen); if (HttpInstance->CacheBody == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen); HttpInstance->CacheLen = BodyLen; } // // Search for Status Code. // StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1; if (StatusCodeStr == NULL) { goto Error; } StatusCode = AsciiStrDecimalToUintn (StatusCodeStr); // // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n". // Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR); if (Tmp == NULL) { goto Error; } Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR); SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders); HeaderTmp = AllocateZeroPool (SizeofHeaders); if (HeaderTmp == NULL) { goto Error; } CopyMem (HeaderTmp, Tmp, SizeofHeaders); FreePool (HttpHeaders); HttpHeaders = HeaderTmp; // // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available. // if (mHttpUtilities == NULL) { Status = EFI_NOT_READY; goto Error; } // // Parse the HTTP header into array of key/value pairs. // Status = mHttpUtilities->Parse ( mHttpUtilities, HttpHeaders, SizeofHeaders, &HttpMsg->Headers, &HttpMsg->HeaderCount ); if (EFI_ERROR (Status)) { goto Error; } FreePool (HttpHeaders); HttpHeaders = NULL; HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode); HttpInstance->StatusCode = StatusCode; // // Init message-body parser by header information. // Status = EFI_NOT_READY; ValueInItem = NULL; NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem); if (ValueInItem == NULL) { goto Error; } // // The first Tx Token not transmitted yet, insert back and return error. // if (!ValueInItem->TcpWrap.IsTxDone) { goto Error2; } Status = HttpInitMsgParser ( ValueInItem->TcpWrap.Method, HttpMsg->Data.Response->StatusCode, HttpMsg->HeaderCount, HttpMsg->Headers, HttpBodyParserCallback, (VOID *) ValueInItem, &HttpInstance->MsgParser ); if (EFI_ERROR (Status)) { goto Error2; } // // Check whether we received a complete HTTP message. // if (HttpInstance->CacheBody != NULL) { Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody); if (EFI_ERROR (Status)) { goto Error2; } if (HttpIsMessageComplete (HttpInstance->MsgParser)) { // // Free the MsgParse since we already have a full HTTP message. // HttpFreeMsgParser (HttpInstance->MsgParser); HttpInstance->MsgParser = NULL; } } if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { Status = EFI_SUCCESS; goto Exit; } } // // Receive the response body. // BodyLen = 0; // // First check whether we cached some data. // if (HttpInstance->CacheBody != NULL) { // // Calculate the length of the cached data. // if (HttpInstance->NextMsg != NULL) { // // We have a cached HTTP message which includes a part of HTTP header of next message. // BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); } else { BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset; } if (BodyLen > 0) { // // We have some cached data. Just copy the data and return. // if (HttpMsg->BodyLength < BodyLen) { CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength); HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength; } else { // // Copy all cached data out. // CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen); HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset; HttpMsg->BodyLength = BodyLen; if (HttpInstance->NextMsg == NULL) { // // There is no HTTP header of next message. Just free the cache buffer. // FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; HttpInstance->NextMsg = NULL; HttpInstance->CacheOffset = 0; } } // // Return since we aready received required data. // Status = EFI_SUCCESS; goto Exit; } if (BodyLen == 0 && HttpInstance->MsgParser == NULL) { // // We received a complete HTTP message, and we don't have more data to return to caller. // HttpMsg->BodyLength = 0; Status = EFI_SUCCESS; goto Exit; } } ASSERT (HttpInstance->MsgParser != NULL); // // We still need receive more data when there is no cache data and MsgParser is not NULL; // Status = HttpTcpReceiveBody (Wrap, HttpMsg); if (EFI_ERROR (Status)) { goto Error; } return Status; Exit: Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); if (Item != NULL) { NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); } if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) { Token->Status = EFI_HTTP_ERROR; } else { Token->Status = Status; } gBS->SignalEvent (Token->Event); HttpCloseTcpRxEvent (Wrap); FreePool (Wrap); return Status; Error2: NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); Error: HttpTcpTokenCleanup (Wrap); if (HttpHeaders != NULL) { FreePool (HttpHeaders); } if (HttpMsg->Headers != NULL) { FreePool (HttpMsg->Headers); } if (HttpInstance->CacheBody != NULL) { FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; } if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) { Token->Status = EFI_HTTP_ERROR; } else { Token->Status = Status; } gBS->SignalEvent (Token->Event); return Status; }
/** Extract the Root Path option and get the required target information. @param[in] RootPath The RootPath. @param[in] Length Length of the RootPath option payload. @param[in, out] ConfigData The iSCSI attempt configuration data read from a nonvolatile device. @retval EFI_SUCCESS All required information is extracted from the RootPath option. @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. @retval EFI_INVALID_PARAMETER The RootPath is malformatted. **/ EFI_STATUS IScsiDhcpExtractRootPath ( IN CHAR8 *RootPath, IN UINT8 Length, IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData ) { EFI_STATUS Status; UINT8 IScsiRootPathIdLen; CHAR8 *TmpStr; ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX]; ISCSI_ROOT_PATH_FIELD *Field; UINT32 FieldIndex; UINT8 Index; ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData; EFI_IP_ADDRESS Ip; UINT8 IpMode; ConfigNvData = &ConfigData->SessionConfigData; // // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname> // IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID); if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) { return EFI_NOT_FOUND; } // // Skip the iSCSI RootPath ID "iscsi:". // RootPath += IScsiRootPathIdLen; Length = (UINT8) (Length - IScsiRootPathIdLen); TmpStr = (CHAR8 *) AllocatePool (Length + 1); if (TmpStr == NULL) { return EFI_OUT_OF_RESOURCES; } CopyMem (TmpStr, RootPath, Length); TmpStr[Length] = '\0'; Index = 0; FieldIndex = RP_FIELD_IDX_SERVERNAME; ZeroMem (&Fields[0], sizeof (Fields)); // // Extract the fields in the Root Path option string. // for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) { if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) { Fields[FieldIndex].Str = &TmpStr[Index]; } while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) { Index++; } if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) { if (FieldIndex != RP_FIELD_IDX_TARGETNAME) { TmpStr[Index] = '\0'; Index++; } if (Fields[FieldIndex].Str != NULL) { Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str); } } } if (FieldIndex != RP_FIELD_IDX_MAX) { Status = EFI_INVALID_PARAMETER; goto ON_EXIT; } if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) || (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) || (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1) ) { Status = EFI_INVALID_PARAMETER; goto ON_EXIT; } // // Get the IP address of the target. // Field = &Fields[RP_FIELD_IDX_SERVERNAME]; if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) { IpMode = ConfigNvData->IpMode; } else { IpMode = ConfigData->AutoConfigureMode; } Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip); CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS)); if (EFI_ERROR (Status)) { goto ON_EXIT; } // // Check the protocol type. // Field = &Fields[RP_FIELD_IDX_PROTOCOL]; if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) { Status = EFI_INVALID_PARAMETER; goto ON_EXIT; } // // Get the port of the iSCSI target. // Field = &Fields[RP_FIELD_IDX_PORT]; if (Field->Str != NULL) { ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str); } else { ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT; } // // Get the LUN. // Field = &Fields[RP_FIELD_IDX_LUN]; if (Field->Str != NULL) { Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun); if (EFI_ERROR (Status)) { goto ON_EXIT; } } else { ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun)); } // // Get the target iSCSI Name. // Field = &Fields[RP_FIELD_IDX_TARGETNAME]; if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) { Status = EFI_INVALID_PARAMETER; goto ON_EXIT; } // // Validate the iSCSI name. // Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str)); if (EFI_ERROR (Status)) { goto ON_EXIT; } AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str); ON_EXIT: FreePool (TmpStr); return Status; }
/** The work function of EfiHttpResponse(). @param[in] Wrap Pointer to HTTP token's wrap data. @retval EFI_SUCCESS Allocation succeeded. @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources. @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or the EFI_HTTP_UTILITIES_PROTOCOL is not available. **/ EFI_STATUS HttpResponseWorker ( IN HTTP_TOKEN_WRAP *Wrap ) { EFI_STATUS Status; EFI_HTTP_MESSAGE *HttpMsg; CHAR8 *EndofHeader; CHAR8 *HttpHeaders; UINTN SizeofHeaders; UINTN BufferSize; UINTN StatusCode; CHAR8 *Tmp; CHAR8 *HeaderTmp; CHAR8 *StatusCodeStr; UINTN BodyLen; HTTP_PROTOCOL *HttpInstance; EFI_HTTP_TOKEN *Token; NET_MAP_ITEM *Item; HTTP_TOKEN_WRAP *ValueInItem; UINTN HdrLen; if (Wrap == NULL || Wrap->HttpInstance == NULL) { return EFI_INVALID_PARAMETER; } HttpInstance = Wrap->HttpInstance; Token = Wrap->HttpToken; HttpMsg = Token->Message; HttpInstance->EndofHeader = NULL; HttpInstance->HttpHeaders = NULL; HttpMsg->Headers = NULL; HttpHeaders = NULL; SizeofHeaders = 0; BufferSize = 0; EndofHeader = NULL; if (HttpMsg->Data.Response != NULL) { // // Need receive the HTTP headers, prepare buffer. // Status = HttpCreateTcpRxEventForHeader (HttpInstance); if (EFI_ERROR (Status)) { goto Error; } // // Check whether we have cached header from previous call. // if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) { // // The data is stored at [NextMsg, CacheBody + CacheLen]. // HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg; HttpHeaders = AllocateZeroPool (HdrLen); if (HttpHeaders == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen); FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; HttpInstance->NextMsg = NULL; HttpInstance->CacheOffset = 0; SizeofHeaders = HdrLen; BufferSize = HttpInstance->CacheLen; // // Check whether we cached the whole HTTP headers. // EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); } HttpInstance->EndofHeader = &EndofHeader; HttpInstance->HttpHeaders = &HttpHeaders; if (HttpInstance->TimeoutEvent == NULL) { // // Create TimeoutEvent for response // Status = gBS->CreateEvent ( EVT_TIMER, TPL_CALLBACK, NULL, NULL, &HttpInstance->TimeoutEvent ); if (EFI_ERROR (Status)) { goto Error; } } // // Start the timer, and wait Timeout seconds to receive the header packet. // Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND); if (EFI_ERROR (Status)) { goto Error; } Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent); gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); if (EFI_ERROR (Status)) { goto Error; } ASSERT (HttpHeaders != NULL); // // Cache the part of body. // BodyLen = BufferSize - (EndofHeader - HttpHeaders); if (BodyLen > 0) { if (HttpInstance->CacheBody != NULL) { FreePool (HttpInstance->CacheBody); } HttpInstance->CacheBody = AllocateZeroPool (BodyLen); if (HttpInstance->CacheBody == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen); HttpInstance->CacheLen = BodyLen; } // // Search for Status Code. // StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1; if (StatusCodeStr == NULL) { goto Error; } StatusCode = AsciiStrDecimalToUintn (StatusCodeStr); // // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n". // Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR); if (Tmp == NULL) { goto Error; } // // We could have response with just a HTTP message and no headers. For Example, // "100 Continue". In such cases, we would not want to unnecessarily call a Parse // method. A "\r\n" following Tmp string again would indicate an end. Compare and // set SizeofHeaders to 0. // Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR); if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) { SizeofHeaders = 0; } else { SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders); } HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode); HttpInstance->StatusCode = StatusCode; Status = EFI_NOT_READY; ValueInItem = NULL; // // In cases of PUT/POST, after an initial request-response pair, we would do a // continuous request without a response call. So, we would not do an insert of // TxToken. After we have sent the complete file, we will call a response to get // a final response from server. In such a case, we would not have any TxTokens. // Hence, check that case before doing a NetMapRemoveHead. // if (!NetMapIsEmpty (&HttpInstance->TxTokens)) { NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem); if (ValueInItem == NULL) { goto Error; } // // The first Tx Token not transmitted yet, insert back and return error. // if (!ValueInItem->TcpWrap.IsTxDone) { goto Error2; } } if (SizeofHeaders != 0) { HeaderTmp = AllocateZeroPool (SizeofHeaders); if (HeaderTmp == NULL) { goto Error; } CopyMem (HeaderTmp, Tmp, SizeofHeaders); FreePool (HttpHeaders); HttpHeaders = HeaderTmp; // // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available. // if (mHttpUtilities == NULL) { Status = EFI_NOT_READY; goto Error; } // // Parse the HTTP header into array of key/value pairs. // Status = mHttpUtilities->Parse ( mHttpUtilities, HttpHeaders, SizeofHeaders, &HttpMsg->Headers, &HttpMsg->HeaderCount ); if (EFI_ERROR (Status)) { goto Error; } FreePool (HttpHeaders); HttpHeaders = NULL; // // Init message-body parser by header information. // Status = HttpInitMsgParser ( HttpInstance->Method, HttpMsg->Data.Response->StatusCode, HttpMsg->HeaderCount, HttpMsg->Headers, HttpBodyParserCallback, (VOID *) ValueInItem, &HttpInstance->MsgParser ); if (EFI_ERROR (Status)) { goto Error2; } // // Check whether we received a complete HTTP message. // if (HttpInstance->CacheBody != NULL) { Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody); if (EFI_ERROR (Status)) { goto Error2; } if (HttpIsMessageComplete (HttpInstance->MsgParser)) { // // Free the MsgParse since we already have a full HTTP message. // HttpFreeMsgParser (HttpInstance->MsgParser); HttpInstance->MsgParser = NULL; } } } if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { Status = EFI_SUCCESS; goto Exit; } } // // Receive the response body. // BodyLen = 0; // // First check whether we cached some data. // if (HttpInstance->CacheBody != NULL) { // // Calculate the length of the cached data. // if (HttpInstance->NextMsg != NULL) { // // We have a cached HTTP message which includes a part of HTTP header of next message. // BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); } else { BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset; } if (BodyLen > 0) { // // We have some cached data. Just copy the data and return. // if (HttpMsg->BodyLength < BodyLen) { CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength); HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength; } else { // // Copy all cached data out. // CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen); HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset; HttpMsg->BodyLength = BodyLen; if (HttpInstance->NextMsg == NULL) { // // There is no HTTP header of next message. Just free the cache buffer. // FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; HttpInstance->NextMsg = NULL; HttpInstance->CacheOffset = 0; } } // // Return since we aready received required data. // Status = EFI_SUCCESS; goto Exit; } if (BodyLen == 0 && HttpInstance->MsgParser == NULL) { // // We received a complete HTTP message, and we don't have more data to return to caller. // HttpMsg->BodyLength = 0; Status = EFI_SUCCESS; goto Exit; } } ASSERT (HttpInstance->MsgParser != NULL); if (HttpInstance->TimeoutEvent == NULL) { // // Create TimeoutEvent for response // Status = gBS->CreateEvent ( EVT_TIMER, TPL_CALLBACK, NULL, NULL, &HttpInstance->TimeoutEvent ); if (EFI_ERROR (Status)) { goto Error; } } // // Start the timer, and wait Timeout seconds to receive the body packet. // Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND); if (EFI_ERROR (Status)) { goto Error; } // // We still need receive more data when there is no cache data and MsgParser is not NULL; // Status = HttpTcpReceiveBody (Wrap, HttpMsg, HttpInstance->TimeoutEvent); gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); if (EFI_ERROR (Status)) { goto Error; } FreePool (Wrap); return Status; Exit: Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); if (Item != NULL) { NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); } if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) { Token->Status = EFI_HTTP_ERROR; } else { Token->Status = Status; } gBS->SignalEvent (Token->Event); HttpCloseTcpRxEvent (Wrap); FreePool (Wrap); return Status; Error2: NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); Error: HttpTcpTokenCleanup (Wrap); if (HttpHeaders != NULL) { FreePool (HttpHeaders); } if (HttpMsg->Headers != NULL) { FreePool (HttpMsg->Headers); } if (HttpInstance->CacheBody != NULL) { FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; } if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) { Token->Status = EFI_HTTP_ERROR; } else { Token->Status = Status; } gBS->SignalEvent (Token->Event); return Status; }
/** Parse the NULL terminated ASCII string of multicast option. @param[in] Str The pointer to the Ascii string of multicast option. @param[in] ExtInfo The pointer to the option information to be filled. @retval EFI_SUCCESS Parse the multicast option successfully. @retval EFI_INVALID_PARAMETER The string is malformatted. @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of resources. **/ EFI_STATUS Mtftp6ParseMcastOption ( IN UINT8 *Str, IN MTFTP6_EXT_OPTION_INFO *ExtInfo ) { EFI_STATUS Status; UINT32 Num; CHAR8 *Ip6Str; CHAR8 *TempStr; // // The multicast option is formated like "addr,port,mc" // The server can also omit the ip and port, use ",,1" // if (*Str == ',') { ZeroMem (&ExtInfo->McastIp, sizeof (EFI_IPv6_ADDRESS)); } else { Ip6Str = (CHAR8 *) AllocateCopyPool (AsciiStrSize ((CHAR8 *) Str), Str); if (Ip6Str == NULL) { return EFI_OUT_OF_RESOURCES; } // // The IPv6 address locates before comma in the input Str. // TempStr = Ip6Str; while ((*TempStr != '\0') && (*TempStr != ',')) { TempStr++; } *TempStr = '\0'; Status = NetLibAsciiStrToIp6 (Ip6Str, &ExtInfo->McastIp); FreePool (Ip6Str); if (EFI_ERROR (Status)) { return Status; } while ((*Str != '\0') && (*Str != ',')) { Str++; } } if (*Str != ',') { return EFI_INVALID_PARAMETER; } Str++; // // Convert the port setting. the server can send us a port number or // empty string. such as the port in ",,1" // if (*Str == ',') { ExtInfo->McastPort = 0; } else { Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str); if (Num > 65535) { return EFI_INVALID_PARAMETER; } ExtInfo->McastPort = (UINT16) Num; while (NET_IS_DIGIT (*Str)) { Str++; } } if (*Str != ',') { return EFI_INVALID_PARAMETER; } Str++; // // Check the master/slave setting, 1 for master, 0 for slave. // Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str); if (Num != 0 && Num != 1) { return EFI_INVALID_PARAMETER; } ExtInfo->IsMaster = (BOOLEAN) (Num == 1); while (NET_IS_DIGIT (*Str)) { Str++; } if (*Str != '\0') { return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; }
/** Parse the MTFTP6 extesion options. @param[in] Options The pointer to the extension options list. @param[in] Count The num of the extension options. @param[in] IsRequest If FALSE, the extension options is included by a request packet. @param[in] ExtInfo The pointer to the option information to be filled. @retval EFI_SUCCESS Parse the multicast option successfully. @retval EFI_INVALID_PARAMETER There is one option is malformatted at least. @retval EFI_UNSUPPORTED There is one option is not supported at least. **/ EFI_STATUS Mtftp6ParseExtensionOption ( IN EFI_MTFTP6_OPTION *Options, IN UINT32 Count, IN BOOLEAN IsRequest, IN MTFTP6_EXT_OPTION_INFO *ExtInfo ) { EFI_STATUS Status; EFI_MTFTP6_OPTION *Opt; UINT32 Index; UINT32 Value; ExtInfo->BitMap = 0; for (Index = 0; Index < Count; Index++) { Opt = Options + Index; if (Opt->OptionStr == NULL || Opt->ValueStr == NULL) { return EFI_INVALID_PARAMETER; } if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "blksize") == 0) { // // block size option, valid value is between [8, 65464] // Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); if ((Value < 8) || (Value > 65464)) { return EFI_INVALID_PARAMETER; } ExtInfo->BlkSize = (UINT16) Value; ExtInfo->BitMap |= MTFTP6_OPT_BLKSIZE_BIT; } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "timeout") == 0) { // // timeout option, valid value is between [1, 255] // Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); if (Value < 1 || Value > 255) { return EFI_INVALID_PARAMETER; } ExtInfo->Timeout = (UINT8) Value; ExtInfo->BitMap |= MTFTP6_OPT_TIMEOUT_BIT; } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "tsize") == 0) { // // tsize option, the biggest transfer supported is 4GB with block size option // ExtInfo->Tsize = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); ExtInfo->BitMap |= MTFTP6_OPT_TSIZE_BIT; } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "multicast") == 0) { // // Multicast option, if it is a request, the value must be a zero string, // otherwise, it must be like "addr,port,mc" string, mc indicates master. // if (!IsRequest) { Status = Mtftp6ParseMcastOption (Opt->ValueStr, ExtInfo); if (EFI_ERROR (Status)) { return Status; } } else if (*(Opt->ValueStr) != '\0') { return EFI_INVALID_PARAMETER; } ExtInfo->BitMap |= MTFTP6_OPT_MCAST_BIT; } else if (IsRequest) { // // If it's a request, unsupported; else if it's a reply, ignore. // return EFI_UNSUPPORTED; } } return EFI_SUCCESS; }