From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by mx.groups.io with SMTP id smtpd.web11.84900.1670008354257768666 for ; Fri, 02 Dec 2022 11:12:39 -0800 Authentication-Results: mx.groups.io; dkim=fail reason="unable to parse pub key" header.i=@intel.com header.s=intel header.b=eMHiCrxf; spf=pass (domain: intel.com, ip: 192.55.52.151, mailfrom: saloni.kasbekar@intel.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1670008359; x=1701544359; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=CP8qHwJSVuwa6sPQCgsNvZq9xZ4cB1+vd9PkvCqdMrw=; b=eMHiCrxf/lN5ZDAWjjaACgf8GgRbxK3LxxDFTRwTiIaWWFkBX99ee6uU kyhaFGM9wRpVX0+pK0UHasbCe+4uZEGaU5OqqdLhMkSAfVsn65B4GcGFu PJXapyRki8x52EBT/fYhp6s8vREoCfA+TjEV7/TNQ8ttboOTUwthMifNX NuNdeZfza8UAGbwXnfEfcforBG7+pmK+zjRT1BNfVzdvrsFA6tCRM9ad+ blrjU82SbgM2AQdjtmRP2A+M5UtQY2UdoEEVg0XidIBw6nRD+IVgfsMvo NDu6SP19PtptFkeoFqKsyq/Z4RQ4Ywsc4lQwSsL7E7Bpz4cboSvxU9Mw4 w==; X-IronPort-AV: E=McAfee;i="6500,9779,10549"; a="296382611" X-IronPort-AV: E=Sophos;i="5.96,213,1665471600"; d="scan'208";a="296382611" Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Dec 2022 11:12:39 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10549"; a="733916700" X-IronPort-AV: E=Sophos;i="5.96,213,1665471600"; d="scan'208";a="733916700" Received: from fmbiosdev02.amr.corp.intel.com ([10.80.127.10]) by fmsmga003-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Dec 2022 11:12:38 -0800 From: "Saloni Kasbekar" To: devel@edk2.groups.io Cc: Saloni Kasbekar , Maciej Rabeda , Wu Jiaxin , Siyuan Fu Subject: [edk2-staging/HttpProxy PATCH v3 5/7] NetworkPkg: Add support for HTTP CONNECT Method Date: Fri, 2 Dec 2022 11:12:24 -0800 Message-Id: <99951a1bb8751c1e9b001a047575be45413ef616.1670008048.git.saloni.kasbekar@intel.com> X-Mailer: git-send-email 2.36.1.windows.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3951 Add support for the HTTP CONNECT method to request the Proxy Server to open a tunnel to the EndPoint Server Cc: Maciej Rabeda Cc: Wu Jiaxin Cc: Siyuan Fu Signed-off-by: Saloni Kasbekar --- NetworkPkg/HttpBootDxe/HttpBootClient.c | 176 +++++++++++++++++++++ NetworkPkg/HttpBootDxe/HttpBootClient.h | 15 ++ NetworkPkg/HttpBootDxe/HttpBootImpl.c | 16 +- NetworkPkg/HttpBootDxe/HttpBootImpl.h | 1 + NetworkPkg/HttpDxe/HttpDriver.h | 2 + NetworkPkg/HttpDxe/HttpDxe.inf | 1 + NetworkPkg/HttpDxe/HttpImpl.c | 151 ++++++++++++++---- NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c | 5 + 8 files changed, 335 insertions(+), 32 deletions(-) diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.c b/NetworkPkg/HttpBootDxe/HttpBootClient.c index b13155b576..b4d02eaff2 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootClient.c +++ b/NetworkPkg/HttpBootDxe/HttpBootClient.c @@ -905,6 +905,182 @@ HttpBootGetBootFileCallback ( return EFI_SUCCESS; } +/** + This function establishes a connection through a proxy server + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Connection successful. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootConnectProxy ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_HTTP_STATUS_CODE StatusCode; + CHAR8 *HostName; + EFI_HTTP_REQUEST_DATA *RequestData; + HTTP_IO_RESPONSE_DATA *ResponseData; + HTTP_IO *HttpIo; + HTTP_IO_HEADER *HttpIoHeader; + CHAR16 *Url; + CHAR16 *ProxyUrl; + UINTN UrlSize; + + Url = NULL; + ProxyUrl = NULL; + RequestData = NULL; + ResponseData = NULL; + HttpIoHeader = NULL; + + UrlSize = AsciiStrSize (Private->BootFileUri); + Url = AllocatePool (UrlSize * sizeof (CHAR16)); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize); + + UrlSize = AsciiStrSize (Private->ProxyUri); + ProxyUrl = AllocatePool (UrlSize * (sizeof (CHAR16))); + if (ProxyUrl == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + AsciiStrToUnicodeStrS (Private->ProxyUri, ProxyUrl, UrlSize); + + // + // Send HTTP request message. + // + + // + // Build HTTP header for the request, 2 headers are needed to send a CONNECT method: + // Host + // User + // + HttpIoHeader = HttpIoCreateHeader (2); + if (HttpIoHeader == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Add HTTP header field 1: Host (EndPoint URI) + // + HostName = NULL; + Status = HttpUrlGetHostName ( + Private->BootFileUri, + Private->BootFileUriParser, + &HostName + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + Status = HttpIoSetHeader ( + HttpIoHeader, + HTTP_HEADER_HOST, + HostName + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Add HTTP header field 2: User-Agent + // + Status = HttpIoSetHeader ( + HttpIoHeader, + HTTP_HEADER_USER_AGENT, + HTTP_USER_AGENT_EFI_HTTP_BOOT + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Build the rest of HTTP request info. + // + RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA)); + if (RequestData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + RequestData->Method = HttpMethodConnect; + RequestData->ProxyUrl = ProxyUrl; + RequestData->Url = Url; + + // + // Send out the request to HTTP server. + // + HttpIo = &Private->HttpIo; + Status = HttpIoSendRequest ( + HttpIo, + RequestData, + HttpIoHeader->HeaderCount, + HttpIoHeader->Headers, + 0, + NULL + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Receive HTTP response message. + // + + // + // Use zero BodyLength to only receive the response headers. + // + ResponseData = AllocateZeroPool (sizeof (HTTP_IO_RESPONSE_DATA)); + if (ResponseData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + Status = HttpIoRecvResponse ( + &Private->HttpIo, + TRUE, + ResponseData + ); + + if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) { + if (EFI_ERROR (ResponseData->Status)) { + StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode; + HttpBootPrintErrorMessage (StatusCode); + Status = ResponseData->Status; + } + } + +EXIT: + if (ResponseData != NULL) { + FreePool (ResponseData); + } + + if (RequestData != NULL) { + FreePool (RequestData); + } + + HttpIoFreeHeader (HttpIoHeader); + + if (ProxyUrl != NULL) { + FreePool (ProxyUrl); + } + + if (Url != NULL) { + FreePool (Url); + } + + return Status; +} + /** This function download the boot file by using UEFI HTTP protocol. diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.h b/NetworkPkg/HttpBootDxe/HttpBootClient.h index 2fba713679..fcd624f536 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootClient.h +++ b/NetworkPkg/HttpBootDxe/HttpBootClient.h @@ -86,6 +86,21 @@ HttpBootCreateHttpIo ( IN HTTP_BOOT_PRIVATE_DATA *Private ); +/** + This function establishes a connection through a proxy server + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Connection successful. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootConnectProxy ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + /** This function download the boot file by using UEFI HTTP protocol. diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/NetworkPkg/HttpBootDxe/HttpBootImpl.c index 4748de0603..d4a7c8385a 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootImpl.c +++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.c @@ -313,7 +313,11 @@ HttpBootGetBootFileCaller ( EFI_STATUS Status; if (Private->BootFileSize == 0) { - State = GetBootFileHead; + if (Private->ProxyUri != NULL) { + State = ConnectToProxy; + } else { + State = GetBootFileHead; + } } else { State = LoadBootFile; } @@ -366,6 +370,16 @@ HttpBootGetBootFileCaller ( break; + case ConnectToProxy: + Status = HttpBootConnectProxy (Private); + if (Status == EFI_SUCCESS) { + State = GetBootFileHead; + } else { + State = GetBootFileError; + } + + break; + case LoadBootFile: if (*BufferSize < Private->BootFileSize) { *BufferSize = Private->BootFileSize; diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.h b/NetworkPkg/HttpBootDxe/HttpBootImpl.h index 33da4fec51..e4ffc3ed48 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootImpl.h +++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.h @@ -14,6 +14,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent typedef enum { GetBootFileHead, GetBootFileGet, + ConnectToProxy, LoadBootFile, GetBootFileError } HTTP_GET_BOOT_FILE_STATE; diff --git a/NetworkPkg/HttpDxe/HttpDriver.h b/NetworkPkg/HttpDxe/HttpDriver.h index 01a6bb7f4b..e0917f431e 100644 --- a/NetworkPkg/HttpDxe/HttpDriver.h +++ b/NetworkPkg/HttpDxe/HttpDriver.h @@ -26,6 +26,7 @@ #include #include #include +#include // // UEFI Driver Model Protocols @@ -64,6 +65,7 @@ // Driver Version // #define HTTP_DRIVER_VERSION 0xa +#define URI_STR_MAX_SIZE 255 // // Protocol instances diff --git a/NetworkPkg/HttpDxe/HttpDxe.inf b/NetworkPkg/HttpDxe/HttpDxe.inf index c9502d0bb6..30b7de1951 100644 --- a/NetworkPkg/HttpDxe/HttpDxe.inf +++ b/NetworkPkg/HttpDxe/HttpDxe.inf @@ -47,6 +47,7 @@ NetLib HttpLib DpcLib + PrintLib [Protocols] gEfiHttpServiceBindingProtocolGuid ## BY_START diff --git a/NetworkPkg/HttpDxe/HttpImpl.c b/NetworkPkg/HttpDxe/HttpImpl.c index a761ce3d5d..2a305e0864 100644 --- a/NetworkPkg/HttpDxe/HttpImpl.c +++ b/NetworkPkg/HttpDxe/HttpImpl.c @@ -234,6 +234,7 @@ EfiHttpRequest ( EFI_HTTP_MESSAGE *HttpMsg; EFI_HTTP_REQUEST_DATA *Request; VOID *UrlParser; + VOID *EndPointUrlParser; EFI_STATUS Status; CHAR8 *HostName; UINTN HostNameSize; @@ -247,25 +248,31 @@ EfiHttpRequest ( UINTN UrlLen; CHAR8 *ProxyUrl; UINTN ProxyUrlLen; + CHAR8 *ParseUrl; CHAR16 *HostNameStr; HTTP_TOKEN_WRAP *Wrap; CHAR8 *FileUrl; UINTN RequestMsgSize; EFI_HANDLE ImageHandle; + UINT16 EndPointRemotePort; + CHAR8 *EndPointUrlMsg; // // Initializations // - Url = NULL; - ProxyUrl = NULL; - UrlParser = NULL; - RemotePort = 0; - HostName = NULL; - RequestMsg = NULL; - HostNameStr = NULL; - Wrap = NULL; - FileUrl = NULL; - TlsConfigure = FALSE; + Url = NULL; + ProxyUrl = NULL; + UrlParser = NULL; + EndPointUrlParser = NULL; + RemotePort = 0; + HostName = NULL; + RequestMsg = NULL; + HostNameStr = NULL; + Wrap = NULL; + FileUrl = NULL; + TlsConfigure = FALSE; + EndPointUrlMsg = NULL; + EndPointRemotePort = 0; if ((This == NULL) || (Token == NULL)) { return EFI_INVALID_PARAMETER; @@ -279,7 +286,7 @@ EfiHttpRequest ( Request = HttpMsg->Data.Request; // - // Only support GET, HEAD, DELETE, PATCH, PUT and POST method in current implementation. + // Only support GET, HEAD, DELETE, CONNECT, PATCH, PUT and POST method in current implementation. // if (Request != NULL) { switch (Request->Method) { @@ -289,6 +296,12 @@ EfiHttpRequest ( case HttpMethodPut: case HttpMethodPost: case HttpMethodPatch: + break; + case HttpMethodConnect: + if (Request->ProxyUrl == NULL) { + return EFI_INVALID_PARAMETER; + } + break; default: return EFI_UNSUPPORTED; @@ -391,10 +404,14 @@ EfiHttpRequest ( } // - // From the information in Url, the HTTP instance will + // From the information in the Urls, the HTTP instance will // be able to determine whether to use http or https. // - HttpInstance->UseHttps = IsHttpsUrl (Url); + if (Request->Method == HttpMethodConnect) { + HttpInstance->UseHttps = IsHttpsUrl (ProxyUrl); + } else { + HttpInstance->UseHttps = IsHttpsUrl (Url); + } // // HTTP is disabled, return directly if the URI is not HTTPS. @@ -431,13 +448,26 @@ EfiHttpRequest ( TlsConfigure = TRUE; } - UrlParser = NULL; - Status = HttpParseUrl (Url, (UINT32)AsciiStrLen (Url), FALSE, &UrlParser); + // + // Setup RemoteAddress and RemotePort of HttpInstance. + // + if (Request->Method == HttpMethodConnect) { + // Case 1: HTTP Connect request + ParseUrl = ProxyUrl; + } else if (HttpInstance->ProxyConnected == TRUE) { + // Case 2: Other HTTP request (proxy connected) + ParseUrl = HttpInstance->ProxyUrl; + } else { + // Case 3: Other HTTP request (proxy not connected) + ParseUrl = Url; + } + + Status = HttpParseUrl (ParseUrl, (UINT32)AsciiStrLen (ParseUrl), FALSE, &UrlParser); if (EFI_ERROR (Status)) { goto Error1; } - Status = HttpUrlGetHostName (Url, UrlParser, &HostName); + Status = HttpUrlGetHostName (ParseUrl, UrlParser, &HostName); if (EFI_ERROR (Status)) { goto Error1; } @@ -455,7 +485,7 @@ EfiHttpRequest ( } } - Status = HttpUrlGetPort (Url, UrlParser, &RemotePort); + Status = HttpUrlGetPort (ParseUrl, UrlParser, &RemotePort); if (EFI_ERROR (Status)) { if (HttpInstance->UseHttps) { RemotePort = HTTPS_DEFAULT_PORT; @@ -551,7 +581,7 @@ EfiHttpRequest ( if (!HttpInstance->LocalAddressIsIPv6) { Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr); } else { - Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr); + Status = HttpUrlGetIp6 (ParseUrl, UrlParser, &HttpInstance->RemoteIpv6Addr); } if (EFI_ERROR (Status)) { @@ -649,27 +679,74 @@ EfiHttpRequest ( // // Create request message. // - FileUrl = Url; - if ((Url != NULL) && (*FileUrl != '/')) { + if (Request->Method == HttpMethodConnect) { // - // Convert the absolute-URI to the absolute-path + // HTTP Connect shall contain EndPoint host name in URI // - while (*FileUrl != ':') { - FileUrl++; + Status = HttpParseUrl (Url, (UINT32)AsciiStrLen (Url), FALSE, &EndPointUrlParser); + if (EFI_ERROR (Status)) { + goto Error3; } - if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) { - FileUrl += 3; - while (*FileUrl != '/') { - FileUrl++; + Status = HttpUrlGetHostName ( + Url, + EndPointUrlParser, + &HttpInstance->EndPointHostName + ); + if (EFI_ERROR (Status)) { + goto Error3; + } + + Status = HttpUrlGetPort (Url, EndPointUrlParser, &EndPointRemotePort); + if (EFI_ERROR (Status)) { + if (IsHttpsUrl (Url)) { + EndPointRemotePort = HTTPS_DEFAULT_PORT; + } else { + EndPointRemotePort = HTTP_DEFAULT_PORT; } - } else { - Status = EFI_INVALID_PARAMETER; + } + + EndPointUrlMsg = AllocateZeroPool (URI_STR_MAX_SIZE); + if (EndPointUrlMsg == NULL) { + Status = EFI_OUT_OF_RESOURCES; goto Error3; } - } - Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize); + AsciiSPrint ( + EndPointUrlMsg, + URI_STR_MAX_SIZE, + "%a:%d", + HttpInstance->EndPointHostName, + EndPointRemotePort + ); + + Status = HttpGenRequestMessage (HttpMsg, EndPointUrlMsg, &RequestMsg, &RequestMsgSize); + + FreePool (EndPointUrlMsg); + HttpUrlFreeParser (EndPointUrlParser); + } else { + FileUrl = Url; + if ((Url != NULL) && (*FileUrl != '/')) { + // + // Convert the absolute-URI to the absolute-path + // + while (*FileUrl != ':') { + FileUrl++; + } + + if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) { + FileUrl += 3; + while (*FileUrl != '/') { + FileUrl++; + } + } else { + Status = EFI_INVALID_PARAMETER; + goto Error3; + } + } + + Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize); + } if (EFI_ERROR (Status) || (NULL == RequestMsg)) { goto Error3; @@ -705,6 +782,10 @@ EfiHttpRequest ( DispatchDpc (); + if (HttpInstance->Method == HttpMethodConnect) { + HttpInstance->ProxyConnected = TRUE; + } + if (HostName != NULL) { FreePool (HostName); } @@ -750,6 +831,14 @@ Error2: } Error1: + if (EndPointUrlMsg != NULL) { + FreePool (EndPointUrlMsg); + } + + if (EndPointUrlParser != NULL) { + HttpUrlFreeParser (EndPointUrlParser); + } + if (HostName != NULL) { FreePool (HostName); } diff --git a/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c index 6a5d78629b..45087a1935 100644 --- a/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c +++ b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c @@ -1927,6 +1927,11 @@ HttpGenRequestMessage ( CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength); RequestPtr += StrLength; break; + case HttpMethodConnect: + StrLength = sizeof (HTTP_METHOD_CONNECT) - 1; + CopyMem (RequestPtr, HTTP_METHOD_CONNECT, StrLength); + RequestPtr += StrLength; + break; default: ASSERT (FALSE); Status = EFI_INVALID_PARAMETER; -- 2.36.1.windows.1