From: "Ni, Ray" <ray.ni@intel.com>
To: "devel@edk2.groups.io" <devel@edk2.groups.io>,
"lersek@redhat.com" <lersek@redhat.com>
Cc: "Wu, Jiaxin" <jiaxin.wu@intel.com>,
"Maciej Rabeda" <maciej.rabeda@linux.intel.com>,
"Philippe Mathieu-Daudé" <philmd@redhat.com>,
"Fu, Siyuan" <siyuan.fu@intel.com>
Subject: Re: [edk2-devel] [PUBLIC edk2 PATCH v2 01/10] NetworkPkg/IScsiDxe: wrap IScsiCHAP source files to 80 characters
Date: Fri, 11 Jun 2021 11:37:50 +0000 [thread overview]
Message-ID: <PH0PR11MB4936B25ED5D8405894A6CFED8C349@PH0PR11MB4936.namprd11.prod.outlook.com> (raw)
In-Reply-To: <20210608121259.32451-2-lersek@redhat.com>
No objection on the patch. Just curious what editor you are using😊
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo Ersek
> Sent: Tuesday, June 8, 2021 8:13 PM
> To: edk2-devel-groups-io <devel@edk2.groups.io>
> Cc: Wu, Jiaxin <jiaxin.wu@intel.com>; Maciej Rabeda <maciej.rabeda@linux.intel.com>; Philippe Mathieu-Daudé
> <philmd@redhat.com>; Fu, Siyuan <siyuan.fu@intel.com>
> Subject: [edk2-devel] [PUBLIC edk2 PATCH v2 01/10] NetworkPkg/IScsiDxe: wrap IScsiCHAP source files to 80 characters
>
> Working with overlong lines is difficult for me; rewrap the CHAP-related
> source files in IScsiDxe to 80 characters width. No functional changes.
>
> Cc: Jiaxin Wu <jiaxin.wu@intel.com>
> Cc: Maciej Rabeda <maciej.rabeda@linux.intel.com>
> Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
> Cc: Siyuan Fu <siyuan.fu@intel.com>
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3356
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com>
> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> ---
> NetworkPkg/IScsiDxe/IScsiCHAP.h | 3 +-
> NetworkPkg/IScsiDxe/IScsiCHAP.c | 90 +++++++++++++++-----
> 2 files changed, 71 insertions(+), 22 deletions(-)
>
> diff --git a/NetworkPkg/IScsiDxe/IScsiCHAP.h b/NetworkPkg/IScsiDxe/IScsiCHAP.h
> index 140bba0dcd76..5e59fb678bd7 100644
> --- a/NetworkPkg/IScsiDxe/IScsiCHAP.h
> +++ b/NetworkPkg/IScsiDxe/IScsiCHAP.h
> @@ -72,31 +72,32 @@ typedef struct _ISCSI_CHAP_AUTH_DATA {
>
> @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 occurred.
> @retval Others Other errors as indicated.
>
> **/
> EFI_STATUS
> IScsiCHAPOnRspReceived (
> IN ISCSI_CONNECTION *Conn
> );
> /**
> This function fills the CHAP authentication information into the login PDU
> during the security negotiation stage in the iSCSI connection login.
>
> @param[in] Conn The iSCSI connection.
> @param[in, out] Pdu The PDU to send out.
>
> @retval EFI_SUCCESS All check passed and the phase-related CHAP
> - authentication info is filled into the iSCSI PDU.
> + authentication info is filled into the iSCSI
> + PDU.
> @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
> @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
>
> **/
> EFI_STATUS
> IScsiCHAPToSendReq (
> IN ISCSI_CONNECTION *Conn,
> IN OUT NET_BUF *Pdu
> );
>
> #endif
> diff --git a/NetworkPkg/IScsiDxe/IScsiCHAP.c b/NetworkPkg/IScsiDxe/IScsiCHAP.c
> index 355c6f129f68..cbbc56ae5b43 100644
> --- a/NetworkPkg/IScsiDxe/IScsiCHAP.c
> +++ b/NetworkPkg/IScsiDxe/IScsiCHAP.c
> @@ -1,42 +1,45 @@
> /** @file
> - This file is for Challenge-Handshake Authentication Protocol (CHAP) Configuration.
> + This file is for Challenge-Handshake Authentication Protocol (CHAP)
> + Configuration.
>
> Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
> SPDX-License-Identifier: BSD-2-Clause-Patent
>
> **/
>
> #include "IScsiImpl.h"
>
> /**
> Initiator calculates its own expected hash value.
>
> @param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator.
> @param[in] ChapSecret iSCSI CHAP secret of the authenticator.
> @param[in] SecretLength The length of iSCSI CHAP secret.
> @param[in] ChapChallenge The challenge message sent by authenticator.
> @param[in] ChallengeLength The length of iSCSI CHAP challenge message.
> @param[out] ChapResponse The calculation of the expected hash value.
>
> - @retval EFI_SUCCESS The expected hash value was calculatedly successfully.
> - @retval EFI_PROTOCOL_ERROR The length of the secret should be at least the
> - length of the hash value for the hashing algorithm chosen.
> + @retval EFI_SUCCESS The expected hash value was calculatedly
> + successfully.
> + @retval EFI_PROTOCOL_ERROR The length of the secret should be at least
> + the length of the hash value for the hashing
> + algorithm chosen.
> @retval EFI_PROTOCOL_ERROR MD5 hash operation fail.
> @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete MD5.
>
> **/
> EFI_STATUS
> IScsiCHAPCalculateResponse (
> IN UINT32 ChapIdentifier,
> IN CHAR8 *ChapSecret,
> IN UINT32 SecretLength,
> IN UINT8 *ChapChallenge,
> IN UINT32 ChallengeLength,
> OUT UINT8 *ChapResponse
> )
> {
> UINTN Md5ContextSize;
> VOID *Md5Ctx;
> CHAR8 IdByte[1];
> EFI_STATUS Status;
>
> @@ -78,40 +81,42 @@ IScsiCHAPCalculateResponse (
> goto Exit;
> }
>
> if (Md5Final (Md5Ctx, ChapResponse)) {
> Status = EFI_SUCCESS;
> }
>
> Exit:
> FreePool (Md5Ctx);
> return Status;
> }
>
> /**
> The initiator checks the CHAP response replied by target against its own
> calculation of the expected hash value.
>
> @param[in] AuthData iSCSI CHAP authentication data.
> @param[in] TargetResponse The response from target.
>
> - @retval EFI_SUCCESS The response from target passed authentication.
> - @retval EFI_SECURITY_VIOLATION The response from target was not expected value.
> + @retval EFI_SUCCESS The response from target passed
> + authentication.
> + @retval EFI_SECURITY_VIOLATION The response from target was not expected
> + value.
> @retval Others Other errors as indicated.
>
> **/
> EFI_STATUS
> IScsiCHAPAuthTarget (
> IN ISCSI_CHAP_AUTH_DATA *AuthData,
> IN UINT8 *TargetResponse
> )
> {
> EFI_STATUS Status;
> UINT32 SecretSize;
> UINT8 VerifyRsp[ISCSI_CHAP_RSP_LEN];
>
> Status = EFI_SUCCESS;
>
> SecretSize = (UINT32) AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);
> Status = IScsiCHAPCalculateResponse (
> AuthData->OutIdentifier,
> AuthData->AuthConfig->ReverseCHAPSecret,
> @@ -177,187 +182,211 @@ IScsiCHAPOnRspReceived (
> //
> 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) {
> Status = EFI_OUT_OF_RESOURCES;
> goto ON_EXIT;
> }
>
> Status = EFI_PROTOCOL_ERROR;
>
> switch (Conn->AuthStep) {
> case ISCSI_AUTH_INITIAL:
> //
> // The first Login Response.
> //
> - Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
> + Value = IScsiGetValueByKeyFromList (
> + KeyValueList,
> + ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
> + );
> if (Value == NULL) {
> goto ON_EXIT;
> }
>
> Result = IScsiNetNtoi (Value);
> if (Result > 0xFFFF) {
> goto ON_EXIT;
> }
>
> Session->TargetPortalGroupTag = (UINT16) Result;
>
> - Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);
> + 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.
> + // Initiator mandates CHAP authentication but target replies without
> + // "CHAP", or initiator suggets "None" but target replies with some kind of
> + // auth method.
> //
> if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
> if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {
> goto ON_EXIT;
> }
> } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {
> if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {
> goto ON_EXIT;
> }
> } else {
> goto ON_EXIT;
> }
>
> //
> // Transit to CHAP step one.
> //
> Conn->AuthStep = 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);
> + Value = IScsiGetValueByKeyFromList (
> + KeyValueList,
> + ISCSI_KEY_CHAP_ALGORITHM
> + );
> if (Value == NULL) {
> goto ON_EXIT;
> }
>
> Algorithm = IScsiNetNtoi (Value);
> if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {
> //
> // Unsupported algorithm is chosen by target.
> //
> goto ON_EXIT;
> }
>
> - Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);
> + Identifier = IScsiGetValueByKeyFromList (
> + KeyValueList,
> + ISCSI_KEY_CHAP_IDENTIFIER
> + );
> if (Identifier == NULL) {
> goto ON_EXIT;
> }
>
> - Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);
> + 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.
> //
> Result = IScsiNetNtoi (Identifier);
> if (Result > 0xFF) {
> goto ON_EXIT;
> }
>
> AuthData->InIdentifier = (UINT32) Result;
> AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;
> - IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);
> + 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->AuthStep = 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);
> + 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 Name and Response replied by Target.
> //
> Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
> break;
>
> default:
> break;
> }
>
> ON_EXIT:
>
> if (KeyValueList != NULL) {
> IScsiFreeKeyValueList (KeyValueList);
> }
>
> FreePool (Data);
>
> return Status;
> }
>
>
> /**
> This function fills the CHAP authentication information into the login PDU
> during the security negotiation stage in the iSCSI connection login.
>
> @param[in] Conn The iSCSI connection.
> @param[in, out] Pdu The PDU to send out.
>
> @retval EFI_SUCCESS All check passed and the phase-related CHAP
> - authentication info is filled into the iSCSI PDU.
> + authentication info is filled into the iSCSI
> + PDU.
> @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
> @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
>
> **/
> EFI_STATUS
> IScsiCHAPToSendReq (
> IN ISCSI_CONNECTION *Conn,
> IN OUT NET_BUF *Pdu
> )
> {
> EFI_STATUS Status;
> ISCSI_SESSION *Session;
> ISCSI_LOGIN_REQUEST *LoginReq;
> ISCSI_CHAP_AUTH_DATA *AuthData;
> CHAR8 *Value;
> CHAR8 ValueStr[256];
> CHAR8 *Response;
> UINT32 RspLen;
> CHAR8 *Challenge;
> @@ -376,95 +405,114 @@ IScsiCHAPToSendReq (
> RspLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
> Response = AllocateZeroPool (RspLen);
> if (Response == NULL) {
> return EFI_OUT_OF_RESOURCES;
> }
>
> ChallengeLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
> Challenge = AllocateZeroPool (ChallengeLen);
> if (Challenge == NULL) {
> FreePool (Response);
> return EFI_OUT_OF_RESOURCES;
> }
>
> switch (Conn->AuthStep) {
> case ISCSI_AUTH_INITIAL:
> //
> // It's the initial Login Request. Fill in the key=value pairs mandatory
> // for the initial Login Request.
> //
> - IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, mPrivate->InitiatorName);
> + IScsiAddKeyValuePair (
> + Pdu,
> + ISCSI_KEY_INITIATOR_NAME,
> + mPrivate->InitiatorName
> + );
> IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
> IScsiAddKeyValuePair (
> Pdu,
> ISCSI_KEY_TARGET_NAME,
> Session->ConfigData->SessionConfigData.TargetName
> );
>
> if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
> Value = ISCSI_KEY_VALUE_NONE;
> ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
> } else {
> Value = ISCSI_AUTH_METHOD_CHAP;
> }
>
> IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
>
> break;
>
> case ISCSI_CHAP_STEP_ONE:
> //
> - // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.
> + // First step, send the Login Request with CHAP_A=<A1,A2...> key-value
> + // pair.
> //
> AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);
> IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);
>
> Conn->AuthStep = ISCSI_CHAP_STEP_TWO;
> break;
>
> case ISCSI_CHAP_STEP_THREE:
> //
> // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
> // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
> // required too.
> //
> // CHAP_N=<N>
> //
> - IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig->CHAPName);
> + IScsiAddKeyValuePair (
> + Pdu,
> + ISCSI_KEY_CHAP_NAME,
> + (CHAR8 *) &AuthData->AuthConfig->CHAPName
> + );
> //
> // CHAP_R=<R>
> //
> - IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);
> + IScsiBinToHex (
> + (UINT8 *) AuthData->CHAPResponse,
> + ISCSI_CHAP_RSP_LEN,
> + Response,
> + &RspLen
> + );
> IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
>
> if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {
> //
> // CHAP_I=<I>
> //
> IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);
> AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
> IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
> //
> // CHAP_C=<C>
> //
> IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);
> AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN;
> - IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen);
> + IScsiBinToHex (
> + (UINT8 *) AuthData->OutChallenge,
> + ISCSI_CHAP_RSP_LEN,
> + Challenge,
> + &ChallengeLen
> + );
> IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
>
> Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;
> }
> //
> // Set the stage transition flag.
> //
> ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
> break;
>
> default:
> Status = EFI_PROTOCOL_ERROR;
> break;
> }
>
> FreePool (Response);
> FreePool (Challenge);
>
> return Status;
> --
> 2.19.1.3.g30247aa5d201
>
>
>
>
>
>
next prev parent reply other threads:[~2021-06-11 11:37 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-06-08 12:12 [PUBLIC edk2 PATCH v2 00/10] NetworkPkg/IScsiDxe: fix IScsiHexToBin() security and functionality bugs Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 01/10] NetworkPkg/IScsiDxe: wrap IScsiCHAP source files to 80 characters Laszlo Ersek
2021-06-11 11:37 ` Ni, Ray [this message]
2021-06-11 11:39 ` [edk2-devel] " Maciej Rabeda
2021-06-22 10:39 ` Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 02/10] NetworkPkg/IScsiDxe: simplify "ISCSI_CHAP_AUTH_DATA.InChallenge" size Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 03/10] NetworkPkg/IScsiDxe: clean up "ISCSI_CHAP_AUTH_DATA.OutChallengeLength" Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 04/10] NetworkPkg/IScsiDxe: clean up library class dependencies Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 05/10] NetworkPkg/IScsiDxe: fix potential integer overflow in IScsiBinToHex() Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 06/10] NetworkPkg/IScsiDxe: assert that IScsiBinToHex() always succeeds Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 07/10] NetworkPkg/IScsiDxe: reformat IScsiHexToBin() leading comment block Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 08/10] NetworkPkg/IScsiDxe: fix IScsiHexToBin() hex parsing Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 09/10] NetworkPkg/IScsiDxe: fix IScsiHexToBin() buffer overflow Laszlo Ersek
2021-06-08 12:12 ` [PUBLIC edk2 PATCH v2 10/10] NetworkPkg/IScsiDxe: check IScsiHexToBin() return values Laszlo Ersek
2021-06-09 17:33 ` [edk2-devel] [PUBLIC edk2 PATCH v2 00/10] NetworkPkg/IScsiDxe: fix IScsiHexToBin() security and functionality bugs Laszlo Ersek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=PH0PR11MB4936B25ED5D8405894A6CFED8C349@PH0PR11MB4936.namprd11.prod.outlook.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox