/** @file This file is cloned from DMTF libredfish library tag v1.0.0 and maintained by EDKII. //---------------------------------------------------------------------------- // Copyright Notice: // Copyright 2017 Distributed Management Task Force, Inc. All rights reserved. // License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libredfish/LICENSE.md //---------------------------------------------------------------------------- Copyright (c) 2019, Intel Corporation. All rights reserved.
(C) Copyright 2021 Hewlett Packard Enterprise Development LP
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include static int initRest(redfishService* service, void * restProtocol); static redfishService* createServiceEnumeratorNoAuth(const char* host, const char* rootUri, bool enumerate, unsigned int flags, void * restProtocol); static redfishService* createServiceEnumeratorBasicAuth(const char* host, const char* rootUri, const char* username, const char* password, unsigned int flags, void * restProtocol); static redfishService* createServiceEnumeratorSessionAuth(const char* host, const char* rootUri, const char* username, const char* password, unsigned int flags, void * restProtocol); static char* makeUrlForService(redfishService* service, const char* uri); static json_t* getVersions(redfishService* service, const char* rootUri); static void addStringToJsonObject(json_t* object, const char* key, const char* value); CHAR16* C8ToC16 (CHAR8 *AsciiStr) { CHAR16 *Str; UINTN BufLen; BufLen = (AsciiStrLen (AsciiStr) + 1) * 2; Str = AllocatePool (BufLen); ASSERT (Str != NULL); AsciiStrToUnicodeStrS (AsciiStr, Str, AsciiStrLen (AsciiStr) + 1); return Str; } VOID RestConfigFreeHttpRequestData ( IN EFI_HTTP_REQUEST_DATA *RequestData ) { if (RequestData == NULL) { return ; } if (RequestData->Url != NULL) { FreePool (RequestData->Url); } FreePool (RequestData); } VOID RestConfigFreeHttpMessage ( IN EFI_HTTP_MESSAGE *Message, IN BOOLEAN IsRequest ) { if (Message == NULL) { return ; } if (IsRequest) { RestConfigFreeHttpRequestData (Message->Data.Request); Message->Data.Request = NULL; } else { if (Message->Data.Response != NULL) { FreePool (Message->Data.Response); Message->Data.Response = NULL; } } if (Message->Headers != NULL) { FreePool (Message->Headers); Message->Headers = NULL; } if (Message->Body != NULL) { FreePool (Message->Body); Message->Body = NULL; } } /** Converts the Unicode string to ASCII string to a new allocated buffer. @param[in] String Unicode string to be converted. @return Buffer points to ASCII string, or NULL if error happens. **/ CHAR8 * UnicodeStrDupToAsciiStr ( CONST CHAR16 *String ) { CHAR8 *AsciiStr; UINTN BufLen; EFI_STATUS Status; BufLen = StrLen (String) + 1; AsciiStr = AllocatePool (BufLen); if (AsciiStr == NULL) { return NULL; } Status = UnicodeStrToAsciiStrS (String, AsciiStr, BufLen); if (EFI_ERROR (Status)) { return NULL; } return AsciiStr; } /** This function encodes the content. @param[in] ContentEncodedValue HTTP conent encoded value. @param[in] OriginalContent Original content. @param[out] EncodedContent Pointer to receive encoded content. @param[out] EncodedContentLength Length of encoded content. @retval EFI_SUCCESS Content encoded successfully. @retval EFI_UNSUPPORTED No source encoding funciton, @retval EFI_INVALID_PARAMETER One of the given parameter is invalid. **/ EFI_STATUS EncodeRequestContent ( IN CHAR8 *ContentEncodedValue, IN CHAR8 *OriginalContent, OUT VOID **EncodedContent, OUT UINTN *EncodedContentLength ) { EFI_STATUS Status; VOID *EncodedPointer; UINTN EncodedLength; if (OriginalContent == NULL || EncodedContent == NULL || EncodedContentLength == NULL) { return EFI_INVALID_PARAMETER; } Status = RedfishContentEncode ( ContentEncodedValue, OriginalContent, AsciiStrLen (OriginalContent), &EncodedPointer, &EncodedLength ); if (Status == EFI_SUCCESS) { *EncodedContent = EncodedPointer; *EncodedContentLength = EncodedLength; return EFI_SUCCESS; } return Status; } /** This function decodes the content. The Memory block pointed by ContentPointer would be freed and replaced with the cooked Redfish payload. @param[in] ContentEncodedValue HTTP conent encoded value. @param[in, out] ContentPointer Pointer to encoded content. Pointer of decoded content when out. @param[in, out] ContentLength Pointer to the length of encoded content. Length of decoded content when out. @retval EFI_SUCCESS Functinos found. @retval EFI_UNSUPPORTED No functions found. @retval EFI_INVALID_PARAMETER One of the given parameter is invalid. **/ EFI_STATUS DecodeResponseContent ( IN CHAR8 *ContentEncodedValue, IN OUT VOID **ContentPointer, IN OUT UINTN *ContentLength ) { EFI_STATUS Status; VOID *DecodedPointer; UINTN DecodedLength; if (ContentEncodedValue == NULL) { return EFI_INVALID_PARAMETER; } Status = RedfishContentDecode ( ContentEncodedValue, *ContentPointer, *ContentLength, &DecodedPointer, &DecodedLength ); if (Status == EFI_SUCCESS) { FreePool (*ContentPointer); *ContentPointer = DecodedPointer; *ContentLength = DecodedLength; } return Status; } /** Create a HTTP URL string for specific Redfish resource. This function build a URL string from the Redfish Host interface record and caller specified relative path of the resource. Callers are responsible for freeing the returned string storage pointed by HttpUrl. @param[in] RedfishData Redfish network host interface record. @param[in] RelativePath Relative path of a resource. @param[out] HttpUrl The pointer to store the returned URL string. @retval EFI_SUCCESS Build the URL string successfully. @retval EFI_INVALID_PARAMETER RedfishData or HttpUrl is NULL. @retval EFI_OUT_OF_RESOURCES There are not enough memory resources. **/ EFI_STATUS RedfishBuildUrl ( IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo, IN CHAR16 *RelativePath, OPTIONAL OUT CHAR16 **HttpUrl ) { CHAR16 *Url; CHAR16 *UrlHead; UINTN UrlLength; UINTN PathLen; if ((RedfishConfigServiceInfo == NULL) || (HttpUrl == NULL)) { return EFI_INVALID_PARAMETER; } // // RFC2616: http_URL = "http(s):" "//" host [ ":" port ] [ abs_path [ "?" query ]] // if (RelativePath == NULL) { PathLen = 0; } else { PathLen = StrLen (RelativePath); } UrlLength = StrLen (HTTPS_FLAG) + StrLen (REDFISH_FIRST_URL) + 1 + StrLen(RedfishConfigServiceInfo->RedfishServiceLocation) + PathLen; Url = AllocateZeroPool (UrlLength * sizeof (CHAR16)); if (Url == NULL) { return EFI_OUT_OF_RESOURCES; } UrlHead = Url; // // Copy "http://" or "https://" according RedfishServiceIpPort. // if (!RedfishConfigServiceInfo->RedfishServiceUseHttps) { StrCpyS (Url, StrLen (HTTPS_FLAG) + 1, HTTP_FLAG); Url = Url + StrLen (HTTP_FLAG); } else { StrCpyS (Url, StrLen (HTTPS_FLAG) + 1, HTTPS_FLAG); Url = Url + StrLen (HTTPS_FLAG); } StrCpyS (Url, StrLen (RedfishConfigServiceInfo->RedfishServiceLocation) + 1, RedfishConfigServiceInfo->RedfishServiceLocation); Url = Url + StrLen (RedfishConfigServiceInfo->RedfishServiceLocation); // // Copy abs_path // if (RelativePath != NULL && PathLen != 0 ) { StrnCpyS (Url, UrlLength, RelativePath, PathLen); } *HttpUrl = UrlHead; return EFI_SUCCESS; } redfishService* createServiceEnumerator(REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo, const char* rootUri, enumeratorAuthentication* auth, unsigned int flags) { EFI_STATUS Status; CHAR16 *HttpUrl; CHAR8 *AsciiHost; EFI_REST_EX_PROTOCOL *RestEx; redfishService *ret; HttpUrl = NULL; AsciiHost = NULL; RestEx = NULL; ret = NULL; if (RedfishConfigServiceInfo->RedfishServiceRestExHandle == NULL) { goto ON_EXIT; } Status = RedfishBuildUrl(RedfishConfigServiceInfo, NULL, &HttpUrl); if (EFI_ERROR (Status)) { goto ON_EXIT; } ASSERT (HttpUrl != NULL); AsciiHost = UnicodeStrDupToAsciiStr (HttpUrl); if (AsciiHost == NULL) { goto ON_EXIT; } Status = gBS->HandleProtocol ( RedfishConfigServiceInfo->RedfishServiceRestExHandle, &gEfiRestExProtocolGuid, (VOID **)&RestEx ); if (EFI_ERROR (Status)) { goto ON_EXIT; } if(auth == NULL) { ret = createServiceEnumeratorNoAuth(AsciiHost, rootUri, true, flags, RestEx); } else if(auth->authType == REDFISH_AUTH_BASIC) { ret = createServiceEnumeratorBasicAuth(AsciiHost, rootUri, auth->authCodes.userPass.username, auth->authCodes.userPass.password, flags, RestEx); } else if(auth->authType == REDFISH_AUTH_SESSION) { ret = createServiceEnumeratorSessionAuth(AsciiHost, rootUri, auth->authCodes.userPass.username, auth->authCodes.userPass.password, flags, RestEx); } else { goto ON_EXIT; } ret->RestEx = RestEx; ON_EXIT: if (HttpUrl != NULL) { FreePool (HttpUrl); } if (AsciiHost != NULL) { FreePool (AsciiHost); } return ret; } json_t* getUriFromService(redfishService* service, const char* uri, EFI_HTTP_STATUS_CODE** StatusCode) { char* url; json_t* ret; HTTP_IO_HEADER *HttpIoHeader = NULL; EFI_STATUS Status; EFI_HTTP_REQUEST_DATA *RequestData = NULL; EFI_HTTP_MESSAGE *RequestMsg = NULL; EFI_HTTP_MESSAGE ResponseMsg; EFI_HTTP_HEADER *ContentEncodedHeader; if(service == NULL || uri == NULL || StatusCode == NULL) { return NULL; } *StatusCode = NULL; url = makeUrlForService(service, uri); if(!url) { return NULL; } DEBUG((DEBUG_INFO, "libredfish: getUriFromService(): %a\n", url)); // // Step 1: Create HTTP request message with 4 headers: // HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service->basicAuthStr) ? 6 : 5); if (HttpIoHeader == NULL) { ret = NULL; goto ON_EXIT; } if(service->sessionToken) { Status = HttpIoSetHeader (HttpIoHeader, "X-Auth-Token", service->sessionToken); ASSERT_EFI_ERROR (Status); } else if (service->basicAuthStr) { Status = HttpIoSetHeader (HttpIoHeader, "Authorization", service->basicAuthStr); ASSERT_EFI_ERROR (Status); } Status = HttpIoSetHeader (HttpIoHeader, "Host", service->HostHeaderValue); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "OData-Version", "4.0"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "Accept", "application/json"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "User-Agent", "libredfish"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "Connection", "Keep-Alive"); ASSERT_EFI_ERROR (Status); // // Step 2: build the rest of HTTP request info. // RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); if (RequestData == NULL) { ret = NULL; goto ON_EXIT; } RequestData->Method = HttpMethodGet; RequestData->Url = C8ToC16 (url); // // Step 3: fill in EFI_HTTP_MESSAGE // RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); if (RequestMsg == NULL) { ret = NULL; goto ON_EXIT; } RequestMsg->Data.Request = RequestData; RequestMsg->HeaderCount = HttpIoHeader->HeaderCount; RequestMsg->Headers = HttpIoHeader->Headers; ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); // // Step 4: call RESTEx to get response from REST service. // Status = service->RestEx->SendReceive (service->RestEx, RequestMsg, &ResponseMsg); if (EFI_ERROR (Status)) { ret = NULL; goto ON_EXIT; } // // Step 5: Return the HTTP StatusCode and Body message. // if (ResponseMsg.Data.Response != NULL) { *StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE)); if (*StatusCode == NULL) { ret = NULL; goto ON_EXIT; } // // The caller shall take the responsibility to free the buffer. // **StatusCode = ResponseMsg.Data.Response->StatusCode; } if (ResponseMsg.BodyLength != 0 && ResponseMsg.Body != NULL) { // // Check if data is encoded. // ContentEncodedHeader = HttpFindHeader (ResponseMsg.HeaderCount, ResponseMsg.Headers, HTTP_HEADER_CONTENT_ENCODING); if (ContentEncodedHeader != NULL) { // // The content is encoded. // Status = DecodeResponseContent (ContentEncodedHeader->FieldValue, &ResponseMsg.Body, &ResponseMsg.BodyLength); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Failed to decompress the response content %r\n.", __FUNCTION__, Status)); ret = NULL; goto ON_EXIT; } } ret = json_loadb (ResponseMsg.Body, ResponseMsg.BodyLength, 0, NULL); } else { // // There is no message body returned from server. // ret = NULL; } ON_EXIT: if (url != NULL) { free (url); } if (HttpIoHeader != NULL) { HttpIoFreeHeader (HttpIoHeader); } if (RequestData != NULL) { RestConfigFreeHttpRequestData (RequestData); } if (RequestMsg != NULL) { FreePool (RequestMsg); } RestConfigFreeHttpMessage (&ResponseMsg, FALSE); return ret; } json_t* patchUriFromService(redfishService* service, const char* uri, const char* content, EFI_HTTP_STATUS_CODE** StatusCode) { char* url; json_t* ret; HTTP_IO_HEADER *HttpIoHeader = NULL; EFI_STATUS Status; EFI_HTTP_REQUEST_DATA *RequestData = NULL; EFI_HTTP_MESSAGE *RequestMsg = NULL; EFI_HTTP_MESSAGE ResponseMsg; CHAR8 ContentLengthStr[80]; CHAR8 *EncodedContent; UINTN EncodedContentLen; if(service == NULL || uri == NULL || content == NULL || StatusCode == NULL) { return NULL; } *StatusCode = NULL; url = makeUrlForService(service, uri); if(!url) { return NULL; } DEBUG((DEBUG_INFO, "libredfish: patchUriFromService(): %a\n", url)); // // Step 1: Create HTTP request message with 4 headers: // HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service->basicAuthStr) ? 9 : 8); if (HttpIoHeader == NULL) { ret = NULL; goto ON_EXIT; } if(service->sessionToken) { Status = HttpIoSetHeader (HttpIoHeader, "X-Auth-Token", service->sessionToken); ASSERT_EFI_ERROR (Status); } else if (service->basicAuthStr) { Status = HttpIoSetHeader (HttpIoHeader, "Authorization", service->basicAuthStr); ASSERT_EFI_ERROR (Status); } Status = HttpIoSetHeader (HttpIoHeader, "Host", service->HostHeaderValue); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "Content-Type", "application/json"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "Accept", "application/json"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "User-Agent", "libredfish"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "Connection", "Keep-Alive"); ASSERT_EFI_ERROR (Status); AsciiSPrint( ContentLengthStr, sizeof (ContentLengthStr), "%lu", (UINT64) strlen(content) ); Status = HttpIoSetHeader (HttpIoHeader, "Content-Length", ContentLengthStr); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "OData-Version", "4.0"); ASSERT_EFI_ERROR (Status); // // Step 2: build the rest of HTTP request info. // RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); if (RequestData == NULL) { ret = NULL; goto ON_EXIT; } RequestData->Method = HttpMethodPatch; RequestData->Url = C8ToC16 (url); // // Step 3: fill in EFI_HTTP_MESSAGE // RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); if (RequestMsg == NULL) { ret = NULL; goto ON_EXIT; } EncodedContent = (CHAR8 *)content; EncodedContentLen = strlen(content); // // We currently only support gzip Content-Encoding. // Status = EncodeRequestContent ((CHAR8 *)HTTP_CONTENT_ENCODING_GZIP, (CHAR8 *)content, (VOID **)&EncodedContent, &EncodedContentLen); if (Status == EFI_INVALID_PARAMETER) { DEBUG((DEBUG_ERROR, "%a: Error to encode content.\n", __FUNCTION__)); ret = NULL; goto ON_EXIT; } else if (Status == EFI_UNSUPPORTED) { DEBUG((DEBUG_INFO, "No content coding for %a! Use raw data instead.\n", HTTP_CONTENT_ENCODING_GZIP)); Status = HttpIoSetHeader (HttpIoHeader, "Content-Encoding", HTTP_CONTENT_ENCODING_IDENTITY); ASSERT_EFI_ERROR (Status); } else { Status = HttpIoSetHeader (HttpIoHeader, "Content-Encoding", HTTP_CONTENT_ENCODING_GZIP); ASSERT_EFI_ERROR (Status); } RequestMsg->Data.Request = RequestData; RequestMsg->HeaderCount = HttpIoHeader->HeaderCount; RequestMsg->Headers = HttpIoHeader->Headers; RequestMsg->BodyLength = EncodedContentLen; RequestMsg->Body = (VOID*) EncodedContent; ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); // // Step 4: call RESTEx to get response from REST service. // Status = service->RestEx->SendReceive (service->RestEx, RequestMsg, &ResponseMsg); if (EFI_ERROR (Status)) { ret = NULL; goto ON_EXIT; } // // Step 5: Return the HTTP StatusCode and Body message. // if (ResponseMsg.Data.Response != NULL) { *StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE)); if (*StatusCode == NULL) { ret = NULL; goto ON_EXIT; } // // The caller shall take the responsibility to free the buffer. // **StatusCode = ResponseMsg.Data.Response->StatusCode; } if (EncodedContent != content) { FreePool (EncodedContent); } if (ResponseMsg.BodyLength != 0 && ResponseMsg.Body != NULL) { ret = json_loadb (ResponseMsg.Body, ResponseMsg.BodyLength, 0, NULL); } else { // // There is no message body returned from server. // ret = NULL; } ON_EXIT: if (url != NULL) { free (url); } if (HttpIoHeader != NULL) { HttpIoFreeHeader (HttpIoHeader); } if (RequestData != NULL) { RestConfigFreeHttpRequestData (RequestData); } if (RequestMsg != NULL) { FreePool (RequestMsg); } RestConfigFreeHttpMessage (&ResponseMsg, FALSE); return ret; } json_t* postUriFromService(redfishService* service, const char* uri, const char* content, size_t contentLength, const char* contentType, EFI_HTTP_STATUS_CODE** StatusCode) { char* url = NULL; json_t* ret; HTTP_IO_HEADER *HttpIoHeader = NULL; EFI_STATUS Status; EFI_HTTP_REQUEST_DATA *RequestData = NULL; EFI_HTTP_MESSAGE *RequestMsg = NULL; EFI_HTTP_MESSAGE ResponseMsg; CHAR8 ContentLengthStr[80]; EFI_HTTP_HEADER *HttpHeader = NULL; ret = NULL; if(service == NULL || uri == NULL || content == NULL || StatusCode == NULL) { return NULL; } *StatusCode = NULL; url = makeUrlForService(service, uri); if(!url) { return NULL; } DEBUG((DEBUG_INFO, "libredfish: postUriFromService(): %a\n", url)); if(contentLength == 0) { contentLength = strlen(content); } // // Step 1: Create HTTP request message with 4 headers: // HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service->basicAuthStr) ? 8 : 7); if (HttpIoHeader == NULL) { goto ON_EXIT; } if(service->sessionToken) { Status = HttpIoSetHeader (HttpIoHeader, "X-Auth-Token", service->sessionToken); ASSERT_EFI_ERROR (Status); } else if (service->basicAuthStr) { Status = HttpIoSetHeader (HttpIoHeader, "Authorization", service->basicAuthStr); ASSERT_EFI_ERROR (Status); } if(contentType == NULL) { Status = HttpIoSetHeader (HttpIoHeader, "Content-Type", "application/json"); ASSERT_EFI_ERROR (Status); } else { Status = HttpIoSetHeader (HttpIoHeader, "Content-Type", (CHAR8 *) contentType); ASSERT_EFI_ERROR (Status); } Status = HttpIoSetHeader (HttpIoHeader, "Host", service->HostHeaderValue); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "Accept", "application/json"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "User-Agent", "libredfish"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "Connection", "Keep-Alive"); ASSERT_EFI_ERROR (Status); AsciiSPrint( ContentLengthStr, sizeof (ContentLengthStr), "%lu", (UINT64) contentLength ); Status = HttpIoSetHeader (HttpIoHeader, "Content-Length", ContentLengthStr); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "OData-Version", "4.0"); ASSERT_EFI_ERROR (Status); // // Step 2: build the rest of HTTP request info. // RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); if (RequestData == NULL) { goto ON_EXIT; } RequestData->Method = HttpMethodPost; RequestData->Url = C8ToC16 (url); // // Step 3: fill in EFI_HTTP_MESSAGE // RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); if (RequestMsg == NULL) { goto ON_EXIT; } RequestMsg->Data.Request = RequestData; RequestMsg->HeaderCount = HttpIoHeader->HeaderCount; RequestMsg->Headers = HttpIoHeader->Headers; RequestMsg->BodyLength = contentLength; RequestMsg->Body = (VOID*) content; ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); // // Step 4: call RESTEx to get response from REST service. // Status = service->RestEx->SendReceive (service->RestEx, RequestMsg, &ResponseMsg); if (EFI_ERROR (Status)) { goto ON_EXIT; } // // Step 5: Return the HTTP StatusCode and Body message. // if (ResponseMsg.Data.Response != NULL) { *StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE)); if (*StatusCode == NULL) { goto ON_EXIT; } // // The caller shall take the responsibility to free the buffer. // **StatusCode = ResponseMsg.Data.Response->StatusCode; } if (ResponseMsg.BodyLength != 0 && ResponseMsg.Body != NULL) { ret = json_loadb (ResponseMsg.Body, ResponseMsg.BodyLength, 0, NULL); } // // Step 6: Parsing the HttpHeader to retrive the X-Auth-Token if the HTTP StatusCode is correct. // if (ResponseMsg.Data.Response->StatusCode == HTTP_STATUS_200_OK || ResponseMsg.Data.Response->StatusCode == HTTP_STATUS_204_NO_CONTENT) { HttpHeader = HttpFindHeader (ResponseMsg.HeaderCount, ResponseMsg.Headers, "X-Auth-Token"); if (HttpHeader != NULL) { if(service->sessionToken) { free(service->sessionToken); } service->sessionToken = AllocateCopyPool (AsciiStrSize (HttpHeader->FieldValue), HttpHeader->FieldValue); } /* // // Below opeation seems to be unnecessary. // Besides, the FieldValue for the Location is the full HTTP URI (Http://0.0.0.0:5000/XXX), so we can't use it as the // parameter of getUriFromService () directly. // HttpHeader = HttpFindHeader (ResponseMsg.HeaderCount, ResponseMsg.Headers, "Location"); if (HttpHeader != NULL) { ret = getUriFromService(service, HttpHeader->FieldValue); goto ON_EXIT; } */ } ON_EXIT: if (url != NULL) { free (url); } if (HttpIoHeader != NULL) { HttpIoFreeHeader (HttpIoHeader); } if (RequestData != NULL) { RestConfigFreeHttpRequestData (RequestData); } if (RequestMsg != NULL) { FreePool (RequestMsg); } RestConfigFreeHttpMessage (&ResponseMsg, FALSE); return ret; } json_t* deleteUriFromService(redfishService* service, const char* uri, EFI_HTTP_STATUS_CODE** StatusCode) { char* url; json_t* ret; HTTP_IO_HEADER *HttpIoHeader = NULL; EFI_STATUS Status; EFI_HTTP_REQUEST_DATA *RequestData = NULL; EFI_HTTP_MESSAGE *RequestMsg = NULL; EFI_HTTP_MESSAGE ResponseMsg; ret = NULL; if(service == NULL || uri == NULL || StatusCode == NULL) { return NULL; } *StatusCode = NULL; url = makeUrlForService(service, uri); if(!url) { return NULL; } DEBUG((DEBUG_INFO, "libredfish: deleteUriFromService(): %a\n", url)); // // Step 1: Create HTTP request message with 4 headers: // HttpIoHeader = HttpIoCreateHeader ((service->sessionToken || service->basicAuthStr) ? 5 : 4); if (HttpIoHeader == NULL) { ret = NULL; goto ON_EXIT; } if(service->sessionToken) { Status = HttpIoSetHeader (HttpIoHeader, "X-Auth-Token", service->sessionToken); ASSERT_EFI_ERROR (Status); } else if (service->basicAuthStr) { Status = HttpIoSetHeader (HttpIoHeader, "Authorization", service->basicAuthStr); ASSERT_EFI_ERROR (Status); } Status = HttpIoSetHeader (HttpIoHeader, "Host", service->HostHeaderValue); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "User-Agent", "libredfish"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "OData-Version", "4.0"); ASSERT_EFI_ERROR (Status); Status = HttpIoSetHeader (HttpIoHeader, "Connection", "Keep-Alive"); ASSERT_EFI_ERROR (Status); // // Step 2: build the rest of HTTP request info. // RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA)); if (RequestData == NULL) { ret = NULL; goto ON_EXIT; } RequestData->Method = HttpMethodDelete; RequestData->Url = C8ToC16 (url); // // Step 3: fill in EFI_HTTP_MESSAGE // RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE)); if (RequestMsg == NULL) { ret = NULL; goto ON_EXIT; } RequestMsg->Data.Request = RequestData; RequestMsg->HeaderCount = HttpIoHeader->HeaderCount; RequestMsg->Headers = HttpIoHeader->Headers; ZeroMem (&ResponseMsg, sizeof (ResponseMsg)); // // Step 4: call RESTEx to get response from REST service. // Status = service->RestEx->SendReceive (service->RestEx, RequestMsg, &ResponseMsg); if (EFI_ERROR (Status)) { ret = NULL; goto ON_EXIT; } // // Step 5: Return the HTTP StatusCode and Body message. // if (ResponseMsg.Data.Response != NULL) { *StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE)); if (*StatusCode == NULL) { ret = NULL; goto ON_EXIT; } // // The caller shall take the responsibility to free the buffer. // **StatusCode = ResponseMsg.Data.Response->StatusCode; } if (ResponseMsg.BodyLength != 0 && ResponseMsg.Body != NULL) { ret = json_loadb (ResponseMsg.Body, ResponseMsg.BodyLength, 0, NULL); } ON_EXIT: if (url != NULL) { free (url); } if (HttpIoHeader != NULL) { HttpIoFreeHeader (HttpIoHeader); } if (RequestData != NULL) { RestConfigFreeHttpRequestData (RequestData); } if (RequestMsg != NULL) { FreePool (RequestMsg); } RestConfigFreeHttpMessage (&ResponseMsg, FALSE); return ret; } redfishPayload* getRedfishServiceRoot(redfishService* service, const char* version, EFI_HTTP_STATUS_CODE** StatusCode) { json_t* value; json_t* versionNode; const char* verUrl; if(version == NULL) { versionNode = json_object_get(service->versions, "v1"); } else { versionNode = json_object_get(service->versions, version); } if(versionNode == NULL) { return NULL; } verUrl = json_string_value(versionNode); if(verUrl == NULL) { return NULL; } value = getUriFromService(service, verUrl, StatusCode); if(value == NULL) { if((service->flags & REDFISH_FLAG_SERVICE_NO_VERSION_DOC) == 0) { json_decref(versionNode); } return NULL; } return createRedfishPayload(value, service); } redfishPayload* getPayloadByPath(redfishService* service, const char* path, EFI_HTTP_STATUS_CODE** StatusCode) { redPathNode* redpath; redfishPayload* root; redfishPayload* ret; if(!service || !path || StatusCode == NULL) { return NULL; } *StatusCode = NULL; redpath = parseRedPath(path); if(!redpath) { return NULL; } if(!redpath->isRoot) { cleanupRedPath(redpath); return NULL; } root = getRedfishServiceRoot(service, redpath->version, StatusCode); if (*StatusCode == NULL || **StatusCode < HTTP_STATUS_200_OK || **StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT) { cleanupRedPath(redpath); return root; } if(redpath->next == NULL) { cleanupRedPath(redpath); return root; } FreePool (*StatusCode); *StatusCode = NULL; ret = getPayloadForPath(root, redpath->next, StatusCode); if (*StatusCode == NULL && ret != NULL) { // // In such a case, the Redfish resource is parsed from the input payload (root) directly. // So, we still return HTTP_STATUS_200_OK. // *StatusCode = AllocateZeroPool (sizeof (EFI_HTTP_STATUS_CODE)); if (*StatusCode == NULL) { ret = NULL; } else { **StatusCode = HTTP_STATUS_200_OK; } } cleanupPayload(root); cleanupRedPath(redpath); return ret; } void cleanupServiceEnumerator(redfishService* service) { if(!service) { return; } free(service->host); json_decref(service->versions); if(service->sessionToken != NULL) { ZeroMem (service->sessionToken, (UINTN)strlen(service->sessionToken)); FreePool(service->sessionToken); } if (service->basicAuthStr != NULL) { ZeroMem (service->basicAuthStr, (UINTN)strlen(service->basicAuthStr)); FreePool (service->basicAuthStr); } free(service); } static int initRest(redfishService* service, void * restProtocol) { service->RestEx = restProtocol; return 0; } static redfishService* createServiceEnumeratorNoAuth(const char* host, const char* rootUri, bool enumerate, unsigned int flags, void * restProtocol) { redfishService* ret; char *HostStart; ret = (redfishService*)calloc(1, sizeof(redfishService)); ZeroMem (ret, sizeof(redfishService)); if(initRest(ret, restProtocol) != 0) { free(ret); return NULL; } ret->host = AllocateCopyPool(AsciiStrSize(host), host); ret->flags = flags; if(enumerate) { ret->versions = getVersions(ret, rootUri); } HostStart = strstr (ret->host, "//"); if (HostStart != NULL && (*(HostStart + 2) != '\0')) { ret->HostHeaderValue = HostStart + 2; } return ret; } EFI_STATUS createBasicAuthStr ( IN redfishService* service, IN CONST CHAR8 *UserId, IN CONST CHAR8 *Password ) { EFI_STATUS Status; CHAR8 *RawAuthValue; UINTN RawAuthBufSize; CHAR8 *EnAuthValue; UINTN EnAuthValueSize; CHAR8 *BasicWithEnAuthValue; UINTN BasicBufSize; EnAuthValue = NULL; EnAuthValueSize = 0; RawAuthBufSize = AsciiStrLen (UserId) + AsciiStrLen (Password) + 2; RawAuthValue = AllocatePool (RawAuthBufSize); if (RawAuthValue == NULL) { return EFI_OUT_OF_RESOURCES; } // // Build raw AuthValue (UserId:Password). // AsciiSPrint ( RawAuthValue, RawAuthBufSize, "%a:%a", UserId, Password ); // // Encoding RawAuthValue into Base64 format. // Status = Base64Encode ( (CONST UINT8 *) RawAuthValue, AsciiStrLen (RawAuthValue), EnAuthValue, &EnAuthValueSize ); if (Status == EFI_BUFFER_TOO_SMALL) { EnAuthValue = (CHAR8 *) AllocateZeroPool (EnAuthValueSize); if (EnAuthValue == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = Base64Encode ( (CONST UINT8 *) RawAuthValue, AsciiStrLen (RawAuthValue), EnAuthValue, &EnAuthValueSize ); } if (EFI_ERROR (Status)) { goto Exit; } BasicBufSize = AsciiStrLen ("Basic ") + AsciiStrLen(EnAuthValue) + 2; BasicWithEnAuthValue = AllocatePool (BasicBufSize); if (BasicWithEnAuthValue == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } // // Build encoded EnAuthValue with Basic (Basic EnAuthValue). // AsciiSPrint ( BasicWithEnAuthValue, BasicBufSize, "%a %a", "Basic", EnAuthValue ); service->basicAuthStr = BasicWithEnAuthValue; Exit: if (RawAuthValue != NULL) { ZeroMem (RawAuthValue, RawAuthBufSize); FreePool (RawAuthValue); } if (EnAuthValue != NULL) { ZeroMem (EnAuthValue, EnAuthValueSize); FreePool (EnAuthValue); } return Status; } static redfishService* createServiceEnumeratorBasicAuth(const char* host, const char* rootUri, const char* username, const char* password, unsigned int flags, void * restProtocol) { redfishService* ret; EFI_STATUS Status; ret = createServiceEnumeratorNoAuth(host, rootUri, false, flags, restProtocol); // add basic auth str Status = createBasicAuthStr (ret, username, password); if (EFI_ERROR(Status)) { cleanupServiceEnumerator (ret); return NULL; } ret->versions = getVersions(ret, rootUri); return ret; } static redfishService* createServiceEnumeratorSessionAuth(const char* host, const char* rootUri, const char* username, const char* password, unsigned int flags, void * restProtocol) { redfishService* ret; redfishPayload* payload; redfishPayload* links; json_t* sessionPayload; json_t* session; json_t* odataId; const char* uri; json_t* post; char* content; EFI_HTTP_STATUS_CODE *StatusCode; content = NULL; StatusCode = NULL; ret = createServiceEnumeratorNoAuth(host, rootUri, true, flags, restProtocol); if(ret == NULL) { return NULL; } payload = getRedfishServiceRoot(ret, NULL, &StatusCode); if(StatusCode == NULL || *StatusCode < HTTP_STATUS_200_OK || *StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT) { if (StatusCode != NULL) { FreePool (StatusCode); } if (payload != NULL) { cleanupPayload(payload); } cleanupServiceEnumerator(ret); return NULL; } if (StatusCode != NULL) { FreePool (StatusCode); StatusCode = NULL; } links = getPayloadByNodeName(payload, "Links", &StatusCode); cleanupPayload(payload); if(links == NULL) { cleanupServiceEnumerator(ret); return NULL; } session = json_object_get(links->json, "Sessions"); if(session == NULL) { cleanupPayload(links); cleanupServiceEnumerator(ret); return NULL; } odataId = json_object_get(session, "@odata.id"); if(odataId == NULL) { cleanupPayload(links); cleanupServiceEnumerator(ret); return NULL; } uri = json_string_value(odataId); post = json_object(); addStringToJsonObject(post, "UserName", username); addStringToJsonObject(post, "Password", password); content = json_dumps(post, 0); json_decref(post); sessionPayload = postUriFromService(ret, uri, content, 0, NULL, &StatusCode); if (content != NULL) { ZeroMem (content, (UINTN)strlen(content)); free(content); } if(sessionPayload == NULL || StatusCode == NULL || *StatusCode < HTTP_STATUS_200_OK || *StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT) { //Failed to create session! cleanupPayload(links); cleanupServiceEnumerator(ret); if (StatusCode != NULL) { FreePool (StatusCode); } if (sessionPayload != NULL) { json_decref(sessionPayload); } return NULL; } json_decref(sessionPayload); cleanupPayload(links); FreePool (StatusCode); return ret; } static char* makeUrlForService(redfishService* service, const char* uri) { char* url; if(service->host == NULL) { return NULL; } url = (char*)malloc(strlen(service->host)+strlen(uri)+1); strcpy(url, service->host); strcat(url, uri); return url; } static json_t* getVersions(redfishService* service, const char* rootUri) { json_t* ret = NULL; EFI_HTTP_STATUS_CODE* StatusCode = NULL; if(service->flags & REDFISH_FLAG_SERVICE_NO_VERSION_DOC) { service->versions = json_object(); if(service->versions == NULL) { return NULL; } addStringToJsonObject(service->versions, "v1", "/redfish/v1"); return service->versions; } if(rootUri != NULL) { ret = getUriFromService(service, rootUri, &StatusCode); } else { ret = getUriFromService(service, "/redfish", &StatusCode); } if (ret == NULL || StatusCode == NULL || *StatusCode < HTTP_STATUS_200_OK || *StatusCode > HTTP_STATUS_206_PARTIAL_CONTENT) { if (ret != NULL) { json_decref(ret); } ret = NULL; } if (StatusCode != NULL) { FreePool (StatusCode); } return ret; } static void addStringToJsonObject(json_t* object, const char* key, const char* value) { json_t* jValue = json_string(value); json_object_set(object, key, jValue); json_decref(jValue); }