Saloni: This change requests the change in UEFI spec. Please follow the code first process (https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Code-First-Pro cess)
And, for the code changes, please separate them for the different packages. Thanks Liming > -----邮件原件----- > 发件人: devel@edk2.groups.io <devel@edk2.groups.io> 代表 Saloni > Kasbekar > 发送时间: 2022年9月15日 4:15 > 收件人: devel@edk2.groups.io > 抄送: Saloni Kasbekar <saloni.kasbe...@intel.com>; Maciej Rabeda > <maciej.rab...@linux.intel.com>; Wu Jiaxin <jiaxin...@intel.com>; Siyuan > Fu <siyuan...@intel.com>; Jian J Wang <jian.j.w...@intel.com>; Liming Gao > <gaolim...@byosoft.com.cn> > 主题: [edk2-devel] [PATCH v6 1/1] NetworkPkg/HttpBootDxe: Add Support > for HTTPS Proxy Server for HTTP Boot > > Add CONNECT HTTP command to create a tunnel from Proxy to EndPoint > Server. > Add support to connect through proxy server using multi-URI DevicePath > sent to the Boot Manager. > > Cc: Maciej Rabeda <maciej.rab...@linux.intel.com> > Cc: Wu Jiaxin <jiaxin...@intel.com> > Cc: Siyuan Fu <siyuan...@intel.com> > Cc: Jian J Wang <jian.j.w...@intel.com> > Cc: Liming Gao <gaolim...@byosoft.com.cn> > Signed-off-by: Saloni Kasbekar <saloni.kasbe...@intel.com> > --- > .../Library/UefiBootManagerLib/BmBoot.c | 28 +++ > .../UefiBootManagerLib/BmBootDescription.c | 4 +- > MdePkg/Include/Protocol/Http.h | 5 + > NetworkPkg/HttpBootDxe/HttpBootClient.c | 222 > +++++++++++++++++- > NetworkPkg/HttpBootDxe/HttpBootClient.h | 15 ++ > NetworkPkg/HttpBootDxe/HttpBootDxe.h | 6 + > NetworkPkg/HttpBootDxe/HttpBootImpl.c | 211 > ++++++++++++----- > NetworkPkg/HttpBootDxe/HttpBootImpl.h | 8 + > NetworkPkg/HttpBootDxe/HttpBootSupport.c | 18 +- > NetworkPkg/HttpBootDxe/HttpBootSupport.h | 8 +- > NetworkPkg/HttpDxe/HttpDriver.h | 2 + > NetworkPkg/HttpDxe/HttpDxe.inf | 1 + > NetworkPkg/HttpDxe/HttpImpl.c | 139 +++++++++-- > NetworkPkg/HttpDxe/HttpProto.c | 41 ++-- > NetworkPkg/HttpDxe/HttpProto.h | 14 +- > NetworkPkg/HttpDxe/HttpsSupport.c | 14 +- > NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c | 5 + > 17 files changed, 631 insertions(+), 110 deletions(-) > > diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c > b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c > index 962892d38f14..fdef1ba292e2 100644 > --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c > +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c > @@ -1513,6 +1513,9 @@ BmExpandLoadFiles ( > UINTN HandleCount; > UINTN Index; > EFI_DEVICE_PATH_PROTOCOL *Node; > + URI_DEVICE_PATH *NullUriPath; > + > + NullUriPath = NULL; > > // > // Get file buffer from load file instance. > @@ -1545,11 +1548,36 @@ BmExpandLoadFiles ( > > for (Index = 0; Index < HandleCount; Index++) { > if (BmMatchHttpBootDevicePath (DevicePathFromHandle > (Handles[Index]), FilePath)) { > + // > + // Matches HTTP Boot Device Path described as > + > // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...) > + > // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...) > + // > + Handle = Handles[Index]; > + goto Done; > + } > + } > + > + NullUriPath = (URI_DEVICE_PATH *)CreateDeviceNode ( > + > MESSAGING_DEVICE_PATH, > + MSG_URI_DP, > + (UINT16)(sizeof > (URI_DEVICE_PATH)) > + ); > + for (Index = 0; Index < HandleCount; Index++) { > + if (BmMatchHttpBootDevicePath (AppendDevicePathNode > (DevicePathFromHandle (Handles[Index]), (EFI_DEVICE_PATH_PROTOCOL > *)NullUriPath), FilePath)) { > + // > + // Matches HTTP Boot Device Path described as > + > // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...)/Uri(. ..) > + > // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...)/Uri(. ..) > + // > Handle = Handles[Index]; > break; > } > } > > + FreePool (NullUriPath); > + > +Done: > if (Handles != NULL) { > FreePool (Handles); > } > diff --git > a/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c > b/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c > index fac33b9ee915..19b7cd14575f 100644 > --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c > +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c > @@ -412,8 +412,8 @@ BmGetNetworkDescription ( > // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...) > // > // The HTTP device path is like: > - // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...) > - // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...) > + > // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...)[/Uri( ...)] > + > // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...)[/Uri( ...)] > // > while (!IsDevicePathEnd (DevicePath) && > ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) || > diff --git a/MdePkg/Include/Protocol/Http.h > b/MdePkg/Include/Protocol/Http.h > index 28e622159392..d47092c58e5a 100644 > --- a/MdePkg/Include/Protocol/Http.h > +++ b/MdePkg/Include/Protocol/Http.h > @@ -191,6 +191,11 @@ typedef struct { > /// is assumed. See RFC 3986 for more details on URI syntax. > /// > CHAR16 *Url; > + /// > + /// The URI of an endpoint host if the Url field contains the address of a > proxy server. > + /// This field will be NULL there is no proxy server in the device path. > + /// > + CHAR16 *EndPointUrl; > } EFI_HTTP_REQUEST_DATA; > > /// > diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.c > b/NetworkPkg/HttpBootDxe/HttpBootClient.c > index 40f64fcb6bf8..f8ff5d0e8956 100644 > --- a/NetworkPkg/HttpBootDxe/HttpBootClient.c > +++ b/NetworkPkg/HttpBootDxe/HttpBootClient.c > @@ -678,6 +678,10 @@ HttpBootFreeCache ( > FreePool (Cache->RequestData->Url); > } > > + if (Cache->RequestData->EndPointUrl != NULL) { > + FreePool (Cache->RequestData->EndPointUrl); > + } > + > FreePool (Cache->RequestData); > } > > @@ -901,6 +905,200 @@ 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 *EndPointUrl; > + UINTN UrlSize; > + VOID *UrlParser; > + > + 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->EndPointUri); > + EndPointUrl = AllocatePool (UrlSize * (sizeof (CHAR16))); > + if (EndPointUrl == NULL) { > + Status = EFI_OUT_OF_RESOURCES; > + goto ERROR_1; > + } > + > + AsciiStrToUnicodeStrS (Private->EndPointUri, EndPointUrl, 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 ERROR_2; > + } > + > + // > + // Add HTTP header field 1: Host (EndPoint URI) > + // > + Status = HttpParseUrl (Private->EndPointUri, (UINT32)AsciiStrLen > (Private->EndPointUri), FALSE, &UrlParser); > + if (EFI_ERROR (Status)) { > + goto ERROR_2; > + } > + > + Status = HttpUrlGetHostName ( > + Private->EndPointUri, > + UrlParser, > + &HostName > + ); > + if (EFI_ERROR (Status)) { > + goto ERROR_2; > + } > + > + Status = HttpIoSetHeader ( > + HttpIoHeader, > + HTTP_HEADER_HOST, > + HostName > + ); > + if (EFI_ERROR (Status)) { > + goto ERROR_2; > + } > + > + // > + // 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 ERROR_2; > + } > + > + // > + // Build the rest of HTTP request info. > + // > + RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA)); > + if (RequestData == NULL) { > + Status = EFI_OUT_OF_RESOURCES; > + goto ERROR_2; > + } > + > + RequestData->Method = HttpMethodConnect; > + RequestData->Url = Url; > + RequestData->EndPointUrl = EndPointUrl; > + > + // > + // 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 ERROR_3; > + } > + > + // > + // 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 ERROR_3; > + } > + > + 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; > + } > + > + goto ERROR_4; > + } > + > + if (EndPointUrl != NULL) { > + FreePool (EndPointUrl); > + } > + > + if (ResponseData != NULL) { > + FreePool (ResponseData); > + } > + > + if (RequestData != NULL) { > + FreePool (RequestData); > + } > + > + return Status; > + > +ERROR_4: > + if (ResponseData != NULL) { > + FreePool (ResponseData); > + } > + > +ERROR_3: > + if (RequestData != NULL) { > + FreePool (RequestData); > + } > + > +ERROR_2: > + HttpIoFreeHeader (HttpIoHeader); > + > +ERROR_1: > + if (EndPointUrl != NULL) { > + FreePool (EndPointUrl); > + } > + > + if (Url != NULL) { > + FreePool (Url); > + } > + > + return Status; > +} > + > /** > This function download the boot file by using UEFI HTTP protocol. > > @@ -950,6 +1148,7 @@ HttpBootGetBootFile ( > UINT8 *Block; > UINTN UrlSize; > CHAR16 *Url; > + CHAR16 *EndPointUrl; > BOOLEAN IdentityMode; > UINTN ReceivedSize; > CHAR8 BaseAuthValue[80]; > @@ -977,6 +1176,20 @@ HttpBootGetBootFile ( > } > > AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize); > + > + if (Private->EndPointUri != NULL) { > + UrlSize = AsciiStrSize (Private->EndPointUri); > + EndPointUrl = AllocatePool (UrlSize * (sizeof (CHAR16))); > + if (EndPointUrl == NULL) { > + Status = EFI_OUT_OF_RESOURCES; > + goto ERROR_1; > + } > + > + AsciiStrToUnicodeStrS (Private->EndPointUri, EndPointUrl, UrlSize); > + } else { > + EndPointUrl = NULL; > + } > + > if (!HeaderOnly && (Buffer != NULL)) { > Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, > ImageType); > if (Status != EFI_NOT_FOUND) { > @@ -1106,8 +1319,9 @@ HttpBootGetBootFile ( > goto ERROR_3; > } > > - RequestData->Method = HeaderOnly ? HttpMethodHead : > HttpMethodGet; > - RequestData->Url = Url; > + RequestData->Method = HeaderOnly ? HttpMethodHead : > HttpMethodGet; > + RequestData->Url = Url; > + RequestData->EndPointUrl = EndPointUrl; > > // > // 2.3 Record the request info in a temp cache item. > @@ -1441,6 +1655,10 @@ ERROR_2: > } > > ERROR_1: > + if (EndPointUrl != NULL) { > + FreePool (EndPointUrl); > + } > + > if (Url != NULL) { > FreePool (Url); > } > diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.h > b/NetworkPkg/HttpBootDxe/HttpBootClient.h > index 2fba71367950..fcd624f536a3 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/HttpBootDxe.h > b/NetworkPkg/HttpBootDxe/HttpBootDxe.h > index 5ff8ad4698b2..e2eb1ffc4544 100644 > --- a/NetworkPkg/HttpBootDxe/HttpBootDxe.h > +++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.h > @@ -223,6 +223,12 @@ struct _HTTP_BOOT_PRIVATE_DATA { > CHAR8 *FilePathUri; > VOID > *FilePathUriParser; > > + // > + // URI string for the endpoint host if BootFileUri contains a proxy > + // server in the path > + // > + CHAR8 *EndPointUri; > + > // > // Cached HTTP data > // > diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.c > b/NetworkPkg/HttpBootDxe/HttpBootImpl.c > index b4c61925b94f..d5332f2bac71 100644 > --- a/NetworkPkg/HttpBootDxe/HttpBootImpl.c > +++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.c > @@ -116,8 +116,10 @@ HttpBootStart ( > UINTN Index; > EFI_STATUS Status; > CHAR8 *Uri; > + CHAR8 *EndPointUri; > > - Uri = NULL; > + Uri = NULL; > + EndPointUri = NULL; > > if ((Private == NULL) || (FilePath == NULL)) { > return EFI_INVALID_PARAMETER; > @@ -127,7 +129,7 @@ HttpBootStart ( > // Check the URI in the input FilePath, in order to see whether it is > // required to boot from a new specified boot file. > // > - Status = HttpBootParseFilePath (FilePath, &Uri); > + Status = HttpBootParseFilePath (FilePath, &Uri, &EndPointUri); > if (EFI_ERROR (Status)) { > return EFI_INVALID_PARAMETER; > } > @@ -154,6 +156,10 @@ HttpBootStart ( > FreePool (Uri); > } > > + if (EndPointUri != NULL) { > + FreePool (EndPointUri); > + } > + > return Status; > } > } else { > @@ -164,6 +170,10 @@ HttpBootStart ( > FreePool (Uri); > } > > + if (EndPointUri != NULL) { > + FreePool (EndPointUri); > + } > + > return EFI_ALREADY_STARTED; > } > } > @@ -180,6 +190,10 @@ HttpBootStart ( > FreePool (Uri); > } > > + if (EndPointUri != NULL) { > + FreePool (EndPointUri); > + } > + > return EFI_UNSUPPORTED; > } > > @@ -187,6 +201,7 @@ HttpBootStart ( > // Record the specified URI and prepare the URI parser if needed. > // > Private->FilePathUri = Uri; > + Private->EndPointUri = EndPointUri; > if (Private->FilePathUri != NULL) { > Status = HttpParseUrl ( > Private->FilePathUri, > @@ -274,6 +289,136 @@ HttpBootDhcp ( > return Status; > } > > +/** > + Issue calls to HttpBootGetBootFile() based on current Boot File State > + > + @param[in] Private The pointer to the driver's > private data. > + @param[in, out] BufferSize On input the size of Buffer in > bytes. On output with a return > + code of EFI_SUCCESS, the > amount of data transferred to > + Buffer. On output with a > return code of EFI_BUFFER_TOO_SMALL, > + the size of Buffer required to > retrieve the requested file. > + @param[in] Buffer The memory buffer to transfer > the file to. If Buffer is NULL, > + then the size of the > requested file is returned in > + BufferSize. > + @param[out] ImageType The image type of the > downloaded file. > + > + @retval EFI_SUCCESS The file was loaded. > + @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is > not NULL but Buffer is NULL. > + @retval EFI_OUT_OF_RESOURCES Could not allocate needed > resources > + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to > read the current directory entry. > + BufferSize has been updated > with the size needed to complete > + the request. > + @retval EFI_ACCESS_DENIED Server authentication failed. > + @retval Others Unexpected error happened. > + > +**/ > +EFI_STATUS > +HttpBootGetBootFileCaller ( > + IN HTTP_BOOT_PRIVATE_DATA *Private, > + IN OUT UINTN *BufferSize, > + IN VOID *Buffer OPTIONAL, > + OUT HTTP_BOOT_IMAGE_TYPE *ImageType > + ) > +{ > + HTTP_GET_BOOT_FILE_STATE State; > + EFI_STATUS Status; > + > + if (Private->BootFileSize == 0) { > + if (Private->EndPointUri != NULL) { > + State = ConnectToProxy; > + } else { > + State = GetBootFileHead; > + } > + } else { > + State = LoadBootFile; > + } > + > + for ( ; ;) { > + switch (State) { > + case GetBootFileHead: > + // > + // Try to use HTTP HEAD method. > + // > + Status = HttpBootGetBootFile ( > + Private, > + TRUE, > + &Private->BootFileSize, > + NULL, > + &Private->ImageType > + ); > + if ((EFI_ERROR (Status)) && (Status != EFI_BUFFER_TOO_SMALL)) { > + if ((Private->AuthData != NULL) && (Status == > EFI_ACCESS_DENIED)) { > + // > + // Try to use HTTP HEAD method again since the > Authentication information is provided. > + // > + State = GetBootFileHead; > + } else { > + State = GetBootFileGet; > + } > + } else { > + State = LoadBootFile; > + } > + > + break; > + > + case GetBootFileGet: > + // > + // Failed to get file size by HEAD method, may be trunked encoding, > try HTTP GET method. > + // > + ASSERT (Private->BootFileSize == 0); > + Status = HttpBootGetBootFile ( > + Private, > + FALSE, > + &Private->BootFileSize, > + NULL, > + &Private->ImageType > + ); > + if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { > + State = GetBootFileError; > + } else { > + State = LoadBootFile; > + } > + > + 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; > + *ImageType = Private->ImageType; > + Status = EFI_BUFFER_TOO_SMALL; > + return Status; > + } > + > + // > + // Load the boot file into Buffer > + // > + Status = HttpBootGetBootFile ( > + Private, > + FALSE, > + BufferSize, > + Buffer, > + ImageType > + ); > + return Status; > + > + case GetBootFileError: > + default: > + AsciiPrint ("\n Error: Could not retrieve NBP file size from HTTP > server.\n"); > + return Status; > + } > + } > +} > + > /** > Attempt to download the boot file through HTTP message exchange. > > @@ -345,68 +490,10 @@ HttpBootLoadFile ( > } > } > > - if (Private->BootFileSize == 0) { > - // > - // Discover the information about the bootfile if we haven't. > - // > - > - // > - // Try to use HTTP HEAD method. > - // > - Status = HttpBootGetBootFile ( > - Private, > - TRUE, > - &Private->BootFileSize, > - NULL, > - &Private->ImageType > - ); > - if ((Private->AuthData != NULL) && (Status == EFI_ACCESS_DENIED)) { > - // > - // Try to use HTTP HEAD method again since the Authentication > information is provided. > - // > - Status = HttpBootGetBootFile ( > - Private, > - TRUE, > - &Private->BootFileSize, > - NULL, > - &Private->ImageType > - ); > - } else if ((EFI_ERROR (Status)) && (Status != EFI_BUFFER_TOO_SMALL)) > { > - // > - // Failed to get file size by HEAD method, may be trunked encoding, > try HTTP GET method. > - // > - ASSERT (Private->BootFileSize == 0); > - Status = HttpBootGetBootFile ( > - Private, > - FALSE, > - &Private->BootFileSize, > - NULL, > - &Private->ImageType > - ); > - if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { > - AsciiPrint ("\n Error: Could not retrieve NBP file size from HTTP > server.\n"); > - goto ON_EXIT; > - } > - } > - } > - > - if (*BufferSize < Private->BootFileSize) { > - *BufferSize = Private->BootFileSize; > - *ImageType = Private->ImageType; > - Status = EFI_BUFFER_TOO_SMALL; > - goto ON_EXIT; > - } > - > // > - // Load the boot file into Buffer > + // Load the Boot File > // > - Status = HttpBootGetBootFile ( > - Private, > - FALSE, > - BufferSize, > - Buffer, > - ImageType > - ); > + Status = HttpBootGetBootFileCaller (Private, BufferSize, Buffer, > ImageType); > > ON_EXIT: > HttpBootUninstallCallback (Private); > diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.h > b/NetworkPkg/HttpBootDxe/HttpBootImpl.h > index 55adc9cb500f..e4ffc3ed48e5 100644 > --- a/NetworkPkg/HttpBootDxe/HttpBootImpl.h > +++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.h > @@ -11,6 +11,14 @@ SPDX-License-Identifier: BSD-2-Clause-Patent > > #define HTTP_BOOT_CHECK_MEDIA_WAITING_TIME > EFI_TIMER_PERIOD_SECONDS(20) > > +typedef enum { > + GetBootFileHead, > + GetBootFileGet, > + ConnectToProxy, > + LoadBootFile, > + GetBootFileError > +} HTTP_GET_BOOT_FILE_STATE; > + > /** > Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to > retrieve the boot resource information. > > diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.c > b/NetworkPkg/HttpBootDxe/HttpBootSupport.c > index 236ef259318b..fddcf8b16746 100644 > --- a/NetworkPkg/HttpBootDxe/HttpBootSupport.c > +++ b/NetworkPkg/HttpBootDxe/HttpBootSupport.c > @@ -558,6 +558,7 @@ HttpBootCheckUriScheme ( > > @param[in] FilePath Pointer to the device path which > contains a URI device path node. > @param[out] UriAddress The URI address string extract from > the device path. > + @param[out] EndPointUriAddress The URI address string for the > endpoint host if UriAddress contains the address of a proxy server > > @retval EFI_SUCCESS The URI string is returned. > @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. > @@ -566,7 +567,8 @@ HttpBootCheckUriScheme ( > EFI_STATUS > HttpBootParseFilePath ( > IN EFI_DEVICE_PATH_PROTOCOL *FilePath, > - OUT CHAR8 **UriAddress > + OUT CHAR8 **UriAddress, > + OUT CHAR8 **EndPointUriAddress > ) > { > EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; > @@ -578,8 +580,9 @@ HttpBootParseFilePath ( > return EFI_INVALID_PARAMETER; > } > > - *UriAddress = NULL; > - > + Uri = NULL; > + *UriAddress = NULL; > + *EndPointUriAddress = NULL; > // > // Extract the URI address from the FilePath > // > @@ -601,6 +604,15 @@ HttpBootParseFilePath ( > break; > } > > + if (Uri != NULL) { > + // > + // Device Path with Proxy Server will be described as > + > // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(EndPointSe rv > er)/Uri(ProxyServer/FilePath) > + > // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(EndPointSe rv > er)/Uri(ProxyServer/FilePath) > + // > + *EndPointUriAddress = Uri; > + } > + > Uri = AllocatePool (UriStrLength + 1); > if (Uri == NULL) { > return EFI_OUT_OF_RESOURCES; > diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.h > b/NetworkPkg/HttpBootDxe/HttpBootSupport.h > index 3698e5593642..6228f37e3676 100644 > --- a/NetworkPkg/HttpBootDxe/HttpBootSupport.h > +++ b/NetworkPkg/HttpBootDxe/HttpBootSupport.h > @@ -138,8 +138,9 @@ HttpBootCheckUriScheme ( > > Caller need to free the buffer in the UriAddress pointer. > > - @param[in] FilePath Pointer to the device path which > contains a URI device path node. > - @param[out] UriAddress The URI address string extract from > the device path. > + @param[in] FilePath Pointer to the device path which > contains a URI device path node. > + @param[out] UriAddress The URI address string extract from > the device path. > + @param[out] EndPointUriAddress The URI address string for the > endpoint host if UriAddress contains the address of a proxy server > > @retval EFI_SUCCESS The URI string is returned. > @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. > @@ -148,7 +149,8 @@ HttpBootCheckUriScheme ( > EFI_STATUS > HttpBootParseFilePath ( > IN EFI_DEVICE_PATH_PROTOCOL *FilePath, > - OUT CHAR8 **UriAddress > + OUT CHAR8 **UriAddress, > + OUT CHAR8 **EndPointUriAddress > ); > > /** > diff --git a/NetworkPkg/HttpDxe/HttpDriver.h > b/NetworkPkg/HttpDxe/HttpDriver.h > index 01a6bb7f4b7a..e0917f431e53 100644 > --- a/NetworkPkg/HttpDxe/HttpDriver.h > +++ b/NetworkPkg/HttpDxe/HttpDriver.h > @@ -26,6 +26,7 @@ > #include <Library/NetLib.h> > #include <Library/HttpLib.h> > #include <Library/DpcLib.h> > +#include <Library/PrintLib.h> > > // > // 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 c9502d0bb6d0..30b7de1951d1 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 7c5c925cf78b..24ce87fd7d14 100644 > --- a/NetworkPkg/HttpDxe/HttpImpl.c > +++ b/NetworkPkg/HttpDxe/HttpImpl.c > @@ -233,35 +233,45 @@ EfiHttpRequest ( > EFI_HTTP_MESSAGE *HttpMsg; > EFI_HTTP_REQUEST_DATA *Request; > VOID *UrlParser; > + VOID *EndPointUrlParser; > EFI_STATUS Status; > CHAR8 *HostName; > + CHAR8 *EndPointHostName; > UINTN HostNameSize; > UINT16 RemotePort; > + UINT16 EndPointRemotePort; > HTTP_PROTOCOL *HttpInstance; > BOOLEAN Configure; > BOOLEAN ReConfigure; > BOOLEAN TlsConfigure; > CHAR8 *RequestMsg; > CHAR8 *Url; > + CHAR8 *EndPointUrl; > UINTN UrlLen; > CHAR16 *HostNameStr; > HTTP_TOKEN_WRAP *Wrap; > CHAR8 *FileUrl; > UINTN RequestMsgSize; > EFI_HANDLE ImageHandle; > + CHAR8 *EndPointUrlMsg; > > // > // Initializations > // > - Url = NULL; > - UrlParser = NULL; > - RemotePort = 0; > - HostName = NULL; > - RequestMsg = NULL; > - HostNameStr = NULL; > - Wrap = NULL; > - FileUrl = NULL; > - TlsConfigure = FALSE; > + Url = NULL; > + UrlParser = NULL; > + EndPointUrlParser = NULL; > + RemotePort = 0; > + EndPointRemotePort = 0; > + HostName = NULL; > + EndPointHostName = NULL; > + RequestMsg = NULL; > + HostNameStr = NULL; > + Wrap = NULL; > + FileUrl = NULL; > + TlsConfigure = FALSE; > + EndPointUrl = NULL; > + EndPointUrlMsg = NULL; > > if ((This == NULL) || (Token == NULL)) { > return EFI_INVALID_PARAMETER; > @@ -275,16 +285,20 @@ EfiHttpRequest ( > Request = HttpMsg->Data.Request; > > // > - // Only support GET, HEAD, DELETE, PATCH, PUT and POST method in > current implementation. > + // Only support GET, HEAD, DELETE, PATCH, PUT, CONNECT and POST > method in current implementation. > // > if ((Request != NULL) && (Request->Method != HttpMethodGet) && > (Request->Method != HttpMethodHead) && (Request->Method != > HttpMethodDelete) && > (Request->Method != HttpMethodPut) && (Request->Method != > HttpMethodPost) && > - (Request->Method != HttpMethodPatch)) > + (Request->Method != HttpMethodPatch) && (Request->Method != > HttpMethodConnect)) > { > return EFI_UNSUPPORTED; > } > > + if ((Request->Method == HttpMethodConnect) && (Request->EndPointUrl > == NULL)) { > + return EFI_INVALID_PARAMETER; > + } > + > HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); > > // > @@ -353,11 +367,25 @@ EfiHttpRequest ( > > UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen); > > + if (Request->EndPointUrl != NULL) { > + UrlLen = StrLen (Request->EndPointUrl) + 1; > + EndPointUrl = AllocateZeroPool (UrlLen); > + if (EndPointUrl == NULL) { > + goto Error1; > + } > + > + UnicodeStrToAsciiStrS (Request->EndPointUrl, EndPointUrl, UrlLen); > + } > + > // > // From the information in Url, the HTTP instance will > // be able to determine whether to use http or https. > // > - HttpInstance->UseHttps = IsHttpsUrl (Url); > + if (HttpInstance->ProxyConnected) { > + HttpInstance->UseHttps = IsHttpsUrl (EndPointUrl); > + } else { > + HttpInstance->UseHttps = IsHttpsUrl (Url); > + } > > // > // HTTP is disabled, return directly if the URI is not HTTPS. > @@ -444,9 +472,10 @@ EfiHttpRequest ( > if ((HttpInstance->ConnectionClose == FALSE) && > (HttpInstance->RemotePort == RemotePort) && > (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) && > - (!HttpInstance->UseHttps || (HttpInstance->UseHttps && > - !TlsConfigure && > - > (HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring)))) > + (!HttpInstance->UseHttps || > + HttpInstance->ProxyConnected || (HttpInstance->UseHttps > && > + !TlsConfigure && > + > (HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring)))) > { > // > // Host Name and port number of the request URL are the same > with previous call to Request(). > @@ -599,7 +628,7 @@ EfiHttpRequest ( > goto Error2; > } > > - if (!Configure && !ReConfigure && !TlsConfigure) { > + if ((!Configure && !ReConfigure) && ((HttpInstance->ProxyConnected && > TlsConfigure) || (!TlsConfigure))) { > // > // For the new HTTP token, create TX TCP token events. > // > @@ -632,7 +661,48 @@ EfiHttpRequest ( > } > } > > - Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, > &RequestMsgSize); > + if (HttpInstance->Method == HttpMethodConnect) { > + Status = HttpParseUrl (EndPointUrl, (UINT32)AsciiStrLen (EndPointUrl), > FALSE, &EndPointUrlParser); > + if (EFI_ERROR (Status)) { > + goto Error3; > + } > + > + Status = HttpUrlGetHostName ( > + EndPointUrl, > + EndPointUrlParser, > + &EndPointHostName > + ); > + if (EFI_ERROR (Status)) { > + goto Error3; > + } > + > + Status = HttpUrlGetPort (EndPointUrl, EndPointUrlParser, > &EndPointRemotePort); > + if (EFI_ERROR (Status)) { > + if (IsHttpsUrl (EndPointUrl)) { > + EndPointRemotePort = HTTPS_DEFAULT_PORT; > + } else { > + EndPointRemotePort = HTTP_DEFAULT_PORT; > + } > + } > + > + EndPointUrlMsg = AllocateZeroPool (URI_STR_MAX_SIZE); > + if (EndPointUrlMsg == NULL) { > + Status = EFI_OUT_OF_RESOURCES; > + goto Error3; > + } > + > + AsciiSPrint ( > + EndPointUrlMsg, > + URI_STR_MAX_SIZE, > + "%a:%d", > + EndPointHostName, > + EndPointRemotePort > + ); > + > + Status = HttpGenRequestMessage (HttpMsg, EndPointUrlMsg, > &RequestMsg, &RequestMsgSize); > + } else { > + Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, > &RequestMsgSize); > + } > > if (EFI_ERROR (Status) || (NULL == RequestMsg)) { > goto Error3; > @@ -668,6 +738,23 @@ EfiHttpRequest ( > > DispatchDpc (); > > + if (HttpInstance->Method == HttpMethodConnect) { > + HttpInstance->ProxyConnected = TRUE; > + HttpInstance->EndPointRemoteHost = EndPointHostName; > + > + if (EndPointUrlParser != NULL) { > + HttpUrlFreeParser (EndPointUrlParser); > + } > + } > + > + if (EndPointUrlMsg != NULL) { > + FreePool (EndPointUrlMsg); > + } > + > + if (EndPointUrl != NULL) { > + FreePool (EndPointUrl); > + } > + > if (HostName != NULL) { > FreePool (HostName); > } > @@ -698,6 +785,20 @@ Error3: > TlsCloseTxRxEvent (HttpInstance); > } > > + if (HttpInstance->Method == HttpMethodConnect) { > + if (EndPointHostName != NULL) { > + FreePool (EndPointHostName); > + } > + > + if (EndPointUrlParser != NULL) { > + HttpUrlFreeParser (EndPointUrlParser); > + } > + } > + > + if (EndPointUrlMsg != NULL) { > + FreePool (EndPointUrlMsg); > + } > + > Error2: > HttpCloseConnection (HttpInstance); > > @@ -725,6 +826,10 @@ Error1: > HttpUrlFreeParser (UrlParser); > } > > + if (EndPointUrl != NULL) { > + FreePool (EndPointUrl); > + } > + > return Status; > } > > diff --git a/NetworkPkg/HttpDxe/HttpProto.c > b/NetworkPkg/HttpDxe/HttpProto.c > index 33ae622c3f0b..b87fbeeb543f 100644 > --- a/NetworkPkg/HttpDxe/HttpProto.c > +++ b/NetworkPkg/HttpDxe/HttpProto.c > @@ -849,6 +849,11 @@ HttpCleanProtocol ( > HttpInstance->Url = NULL; > } > > + if (HttpInstance->EndPointRemoteHost != NULL) { > + FreePool (HttpInstance->EndPointRemoteHost); > + HttpInstance->EndPointRemoteHost = NULL; > + } > + > NetMapClean (&HttpInstance->TxTokens); > NetMapClean (&HttpInstance->RxTokens); > > @@ -1206,6 +1211,7 @@ HttpConfigureTcp6 ( > connect one TLS session if required. > > @param[in] HttpInstance The HTTP instance private data. > + @param[in] TlsConfigure The Flag indicates whether it's the > new Tls session. > > @retval EFI_SUCCESS The TCP connection is established. > @retval EFI_NOT_READY TCP4 protocol child is not created or > configured. > @@ -1214,7 +1220,8 @@ HttpConfigureTcp6 ( > **/ > EFI_STATUS > HttpConnectTcp4 ( > - IN HTTP_PROTOCOL *HttpInstance > + IN HTTP_PROTOCOL *HttpInstance, > + IN BOOLEAN TlsConfigure > ) > { > EFI_STATUS Status; > @@ -1237,16 +1244,18 @@ HttpConnectTcp4 ( > return Status; > } > > - if (Tcp4State == Tcp4StateEstablished) { > + if ((Tcp4State == Tcp4StateEstablished) && > (!HttpInstance->ProxyConnected || !TlsConfigure)) { > return EFI_SUCCESS; > } else if (Tcp4State > Tcp4StateEstablished ) { > HttpCloseConnection (HttpInstance); > } > > - Status = HttpCreateConnection (HttpInstance); > - if (EFI_ERROR (Status)) { > - DEBUG ((DEBUG_ERROR, "Tcp4 Connection fail - %x\n", Status)); > - return Status; > + if (!HttpInstance->ProxyConnected) { > + Status = HttpCreateConnection (HttpInstance); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "Tcp4 Connection fail - %x\n", Status)); > + return Status; > + } > } > > // > @@ -1298,6 +1307,7 @@ HttpConnectTcp4 ( > connect one TLS session if required. > > @param[in] HttpInstance The HTTP instance private data. > + @param[in] TlsConfigure The Flag indicates whether it's the > new Tls session. > > @retval EFI_SUCCESS The TCP connection is established. > @retval EFI_NOT_READY TCP6 protocol child is not created or > configured. > @@ -1306,7 +1316,8 @@ HttpConnectTcp4 ( > **/ > EFI_STATUS > HttpConnectTcp6 ( > - IN HTTP_PROTOCOL *HttpInstance > + IN HTTP_PROTOCOL *HttpInstance, > + IN BOOLEAN TlsConfigure > ) > { > EFI_STATUS Status; > @@ -1330,16 +1341,18 @@ HttpConnectTcp6 ( > return Status; > } > > - if (Tcp6State == Tcp6StateEstablished) { > + if ((Tcp6State == Tcp6StateEstablished) && > (!HttpInstance->ProxyConnected || !TlsConfigure)) { > return EFI_SUCCESS; > } else if (Tcp6State > Tcp6StateEstablished ) { > HttpCloseConnection (HttpInstance); > } > > - Status = HttpCreateConnection (HttpInstance); > - if (EFI_ERROR (Status)) { > - DEBUG ((DEBUG_ERROR, "Tcp6 Connection fail - %x\n", Status)); > - return Status; > + if (!HttpInstance->ProxyConnected) { > + Status = HttpCreateConnection (HttpInstance); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "Tcp6 Connection fail - %x\n", Status)); > + return Status; > + } > } > > // > @@ -1434,7 +1447,7 @@ HttpInitSession ( > // > // Connect TCP. > // > - Status = HttpConnectTcp4 (HttpInstance); > + Status = HttpConnectTcp4 (HttpInstance, TlsConfigure); > if (EFI_ERROR (Status)) { > return Status; > } > @@ -1452,7 +1465,7 @@ HttpInitSession ( > // > // Connect TCP. > // > - Status = HttpConnectTcp6 (HttpInstance); > + Status = HttpConnectTcp6 (HttpInstance, TlsConfigure); > if (EFI_ERROR (Status)) { > return Status; > } > diff --git a/NetworkPkg/HttpDxe/HttpProto.h > b/NetworkPkg/HttpDxe/HttpProto.h > index 620eb3915843..2e8d51635993 100644 > --- a/NetworkPkg/HttpDxe/HttpProto.h > +++ b/NetworkPkg/HttpDxe/HttpProto.h > @@ -165,6 +165,12 @@ typedef struct _HTTP_PROTOCOL { > > CHAR8 *Url; > > + // > + // Proxy Server Support > + // > + CHAR8 *EndPointRemoteHost; > + BOOLEAN ProxyConnected; > + > // > // Https Support > // > @@ -398,6 +404,7 @@ HttpConfigureTcp6 ( > connect one TLS session if required. > > @param[in] HttpInstance The HTTP instance private data. > + @param[in] TlsConfigure The Flag indicates whether it's the > new Tls session. > > @retval EFI_SUCCESS The TCP connection is established. > @retval EFI_NOT_READY TCP4 protocol child is not created or > configured. > @@ -406,7 +413,8 @@ HttpConfigureTcp6 ( > **/ > EFI_STATUS > HttpConnectTcp4 ( > - IN HTTP_PROTOCOL *HttpInstance > + IN HTTP_PROTOCOL *HttpInstance, > + IN BOOLEAN TlsConfigure > ); > > /** > @@ -414,6 +422,7 @@ HttpConnectTcp4 ( > connect one TLS session if required. > > @param[in] HttpInstance The HTTP instance private data. > + @param[in] TlsConfigure The Flag indicates whether it's the > new Tls session. > > @retval EFI_SUCCESS The TCP connection is established. > @retval EFI_NOT_READY TCP6 protocol child is not created or > configured. > @@ -422,7 +431,8 @@ HttpConnectTcp4 ( > **/ > EFI_STATUS > HttpConnectTcp6 ( > - IN HTTP_PROTOCOL *HttpInstance > + IN HTTP_PROTOCOL *HttpInstance, > + IN BOOLEAN TlsConfigure > ); > > /** > diff --git a/NetworkPkg/HttpDxe/HttpsSupport.c > b/NetworkPkg/HttpDxe/HttpsSupport.c > index ad611e7c3836..7dc2b752ec47 100644 > --- a/NetworkPkg/HttpDxe/HttpsSupport.c > +++ b/NetworkPkg/HttpDxe/HttpsSupport.c > @@ -644,11 +644,15 @@ TlsConfigureSession ( > // > // TlsConfigData initialization > // > - HttpInstance->TlsConfigData.ConnectionEnd = EfiTlsClient; > - HttpInstance->TlsConfigData.VerifyMethod = > EFI_TLS_VERIFY_PEER; > - HttpInstance->TlsConfigData.VerifyHost.Flags = > EFI_TLS_VERIFY_FLAG_NONE; > - HttpInstance->TlsConfigData.VerifyHost.HostName = > HttpInstance->RemoteHost; > - HttpInstance->TlsConfigData.SessionState = > EfiTlsSessionNotStarted; > + HttpInstance->TlsConfigData.ConnectionEnd = EfiTlsClient; > + HttpInstance->TlsConfigData.SessionState = > EfiTlsSessionNotStarted; > + HttpInstance->TlsConfigData.VerifyMethod = > EFI_TLS_VERIFY_PEER; > + HttpInstance->TlsConfigData.VerifyHost.Flags = > EFI_TLS_VERIFY_FLAG_NONE; > + if (HttpInstance->ProxyConnected) { > + HttpInstance->TlsConfigData.VerifyHost.HostName = > HttpInstance->EndPointRemoteHost; > + } else { > + HttpInstance->TlsConfigData.VerifyHost.HostName = > HttpInstance->RemoteHost; > + } > > // > // EfiTlsConnectionEnd, > diff --git a/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c > b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c > index 6a5d78629bb3..45087a19350a 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 > > > > > > > -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#93802): https://edk2.groups.io/g/devel/message/93802 Mute This Topic: https://groups.io/mt/93694563/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-