/** @file
  The implementation of dump policy entry function in IpSecConfig application.

  Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>

  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php.

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "IpSecConfig.h"
#include "Dump.h"
#include "ForEach.h"
#include "Helper.h"

/**
  Private function called to get the version infomation from an EFI_IP_ADDRESS_INFO structure.

  @param[in] AddressInfo    The pointer to the EFI_IP_ADDRESS_INFO structure.

  @return the value of version.
**/
UINTN
GetVerFromAddrInfo (
  IN EFI_IP_ADDRESS_INFO    *AddressInfo
)
{
  if((AddressInfo->PrefixLength <= 32) && (AddressInfo->Address.Addr[1] == 0) &&
     (AddressInfo->Address.Addr[2] == 0) && (AddressInfo->Address.Addr[3] == 0)) {
    return IP_VERSION_4;
  } else {
    return IP_VERSION_6;
  }
}

/**
  Private function called to get the version information from a EFI_IP_ADDRESS structure.

  @param[in] Address    The pointer to the EFI_IP_ADDRESS structure.

  @return The value of the version.
**/
UINTN
GetVerFromIpAddr (
  IN EFI_IP_ADDRESS    *Address
)
{
  if ((Address->Addr[1] == 0) && (Address->Addr[2] == 0) && (Address->Addr[3] == 0)) {
    return IP_VERSION_4;
  } else {
    return IP_VERSION_6;
  }
}

/**
  Private function called to print an ASCII string in unicode char format.

  @param[in] Str       The pointer to the ASCII string.
  @param[in] Length    The value of the ASCII string length.
**/
VOID
DumpAsciiString (
  IN CHAR8    *Str,
  IN UINTN    Length
  )
{
  UINTN    Index;
  Print (L"\"");
  for (Index = 0; Index < Length; Index++) {
    Print (L"%c", (CHAR16) Str[Index]);
  }
  Print (L"\"");
}

/**
  Private function called to print a buffer in Hex format.

  @param[in] Data      The pointer to the buffer.
  @param[in] Length    The size of the buffer.

**/
VOID
DumpBuf (
  IN UINT8    *Data,
  IN UINTN    Length
  )
{
  UINTN    Index;
  for (Index = 0; Index < Length; Index++) {
    Print (L"%02x ", Data[Index]);
  }
}

/**
  Private function called to print EFI_IP_ADDRESS_INFO content.

  @param[in] AddressInfo    The pointer to the EFI_IP_ADDRESS_INFO structure.
**/
VOID
DumpAddressInfo (
  IN EFI_IP_ADDRESS_INFO    *AddressInfo
  )
{
  if (IP_VERSION_4 == GetVerFromAddrInfo (AddressInfo)) {
    Print (
      L"%d.%d.%d.%d",
      (UINTN) AddressInfo->Address.v4.Addr[0],
      (UINTN) AddressInfo->Address.v4.Addr[1],
      (UINTN) AddressInfo->Address.v4.Addr[2],
      (UINTN) AddressInfo->Address.v4.Addr[3]
      );
    if (AddressInfo->PrefixLength != 32) {
      Print (L"/%d", (UINTN) AddressInfo->PrefixLength);
    }
  }

  if (IP_VERSION_6 == GetVerFromAddrInfo (AddressInfo)) {
    Print (
      L"%x:%x:%x:%x:%x:%x:%x:%x",
      (((UINT16) AddressInfo->Address.v6.Addr[0]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[1]),
      (((UINT16) AddressInfo->Address.v6.Addr[2]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[3]),
      (((UINT16) AddressInfo->Address.v6.Addr[4]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[5]),
      (((UINT16) AddressInfo->Address.v6.Addr[6]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[7]),
      (((UINT16) AddressInfo->Address.v6.Addr[8]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[9]),
      (((UINT16) AddressInfo->Address.v6.Addr[10]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[11]),
      (((UINT16) AddressInfo->Address.v6.Addr[12]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[13]),
      (((UINT16) AddressInfo->Address.v6.Addr[14]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[15])
      );
    if (AddressInfo->PrefixLength != 128) {
      Print (L"/%d", AddressInfo->PrefixLength);
    }
  }
}

/**
  Private function called to print EFI_IP_ADDRESS content.

  @param[in] IpAddress    The pointer to the EFI_IP_ADDRESS structure.
**/
VOID
DumpIpAddress (
  IN EFI_IP_ADDRESS    *IpAddress
  )
{
  if (IP_VERSION_4 == GetVerFromIpAddr (IpAddress)) {
    Print (
      L"%d.%d.%d.%d",
      (UINTN) IpAddress->v4.Addr[0],
      (UINTN) IpAddress->v4.Addr[1],
      (UINTN) IpAddress->v4.Addr[2],
      (UINTN) IpAddress->v4.Addr[3]
      );
  }

  if (IP_VERSION_6 == GetVerFromIpAddr (IpAddress)) {
    Print (
      L"%x:%x:%x:%x:%x:%x:%x:%x",
      (((UINT16) IpAddress->v6.Addr[0]) << 8) | ((UINT16) IpAddress->v6.Addr[1]),
      (((UINT16) IpAddress->v6.Addr[2]) << 8) | ((UINT16) IpAddress->v6.Addr[3]),
      (((UINT16) IpAddress->v6.Addr[4]) << 8) | ((UINT16) IpAddress->v6.Addr[5]),
      (((UINT16) IpAddress->v6.Addr[6]) << 8) | ((UINT16) IpAddress->v6.Addr[7]),
      (((UINT16) IpAddress->v6.Addr[8]) << 8) | ((UINT16) IpAddress->v6.Addr[9]),
      (((UINT16) IpAddress->v6.Addr[10]) << 8) | ((UINT16) IpAddress->v6.Addr[11]),
      (((UINT16) IpAddress->v6.Addr[12]) << 8) | ((UINT16) IpAddress->v6.Addr[13]),
      (((UINT16) IpAddress->v6.Addr[14]) << 8) | ((UINT16) IpAddress->v6.Addr[15])
      );
  }

}

/**
  Private function called to print EFI_IPSEC_SPD_SELECTOR content.

  @param[in] Selector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
**/
VOID
DumpSpdSelector (
  IN EFI_IPSEC_SPD_SELECTOR    *Selector
  )
{
  UINT32    Index;
  CHAR16    *Str;

  for (Index = 0; Index < Selector->LocalAddressCount; Index++) {
    if (Index > 0) {
      Print (L",");
    }

    DumpAddressInfo (&Selector->LocalAddress[Index]);
  }

  if (Index == 0) {
    Print (L"localhost");
  }

  Print (L" -> ");

  for (Index = 0; Index < Selector->RemoteAddressCount; Index++) {
    if (Index > 0) {
      Print (L",");
    }

    DumpAddressInfo (&Selector->RemoteAddress[Index]);
  }

  Str = MapIntegerToString (Selector->NextLayerProtocol, mMapIpProtocol);
  if (Str != NULL) {
    Print (L" %s", Str);
  } else {
    Print (L" proto:%d", (UINTN) Selector->NextLayerProtocol);
  }

  if ((Selector->NextLayerProtocol == EFI_IP4_PROTO_TCP) || (Selector->NextLayerProtocol == EFI_IP4_PROTO_UDP)) {
    Print (L" port:");
    if (Selector->LocalPort != EFI_IPSEC_ANY_PORT) {
      Print (L"%d", Selector->LocalPort);
      if (Selector->LocalPortRange != 0) {
        Print (L"~%d", (UINTN) Selector->LocalPort + Selector->LocalPortRange);
      }
    } else {
      Print (L"any");
    }

    Print (L" -> ");
    if (Selector->RemotePort != EFI_IPSEC_ANY_PORT) {
      Print (L"%d", Selector->RemotePort);
      if (Selector->RemotePortRange != 0) {
        Print (L"~%d", (UINTN) Selector->RemotePort + Selector->RemotePortRange);
      }
    } else {
      Print (L"any");
    }
  } else if (Selector->NextLayerProtocol == EFI_IP4_PROTO_ICMP) {
    Print (L" class/code:");
    if (Selector->LocalPort != 0) {
      Print (L"%d", (UINTN) (UINT8) Selector->LocalPort);
    } else {
      Print (L"any");
    }

    Print (L"/");
    if (Selector->RemotePort != 0) {
      Print (L"%d", (UINTN) (UINT8) Selector->RemotePort);
    } else {
      Print (L"any");
    }
  }
}

/**
  Print EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA content.

  @param[in] Selector      The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
  @param[in] Data          The pointer to the EFI_IPSEC_SPD_DATA structure.
  @param[in] EntryIndex    The pointer to the Index in SPD Database.

  @retval EFI_SUCCESS    Dump SPD information successfully.
**/
EFI_STATUS
DumpSpdEntry (
  IN EFI_IPSEC_SPD_SELECTOR    *Selector,
  IN EFI_IPSEC_SPD_DATA        *Data,
  IN UINTN                     *EntryIndex
  )
{
  BOOLEAN    HasPre;
  CHAR16     DataName[128];
  CHAR16     *String1;
  CHAR16     *String2;
  CHAR16     *String3;
  UINT8      Index;

  Print (L"%d.", (*EntryIndex)++);

  //
  // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400
  // Protect  PF:0x34323423 Name:First Entry
  // ext-sequence sequence-overflow fragcheck life:[B0,S1024,H3600]
  // ESP algo1 algo2 Tunnel [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx set]
  //

  DumpSpdSelector (Selector);
  Print (L"\n  ");

  Print (L"%s ", MapIntegerToString (Data->Action, mMapIpSecAction));
  Print (L"PF:%08x ", Data->PackageFlag);

  Index = 0;
  while (Data->Name[Index] != 0) {
    DataName[Index] = (CHAR16) Data->Name[Index];
    Index++;
    ASSERT (Index < 128);
  }
  DataName[Index] = L'\0';

  Print (L"Name:%s", DataName);

  if (Data->Action == EfiIPsecActionProtect) {
    Print (L"\n  ");
    if (Data->ProcessingPolicy->ExtSeqNum) {
      Print (L"ext-sequence ");
    }

    if (Data->ProcessingPolicy->SeqOverflow) {
      Print (L"sequence-overflow ");
    }

    if (Data->ProcessingPolicy->FragCheck) {
      Print (L"fragment-check ");
    }

    HasPre = FALSE;
    if (Data->ProcessingPolicy->SaLifetime.ByteCount != 0) {
      Print (HasPre ? L"," : L"life:[");
      Print (L"%lxB", Data->ProcessingPolicy->SaLifetime.ByteCount);
      HasPre = TRUE;
    }

    if (Data->ProcessingPolicy->SaLifetime.SoftLifetime != 0) {
      Print (HasPre ? L"," : L"life:[");
      Print (L"%lxs", Data->ProcessingPolicy->SaLifetime.SoftLifetime);
      HasPre = TRUE;
    }

    if (Data->ProcessingPolicy->SaLifetime.HardLifetime != 0) {
      Print (HasPre ? L"," : L"life:[");
      Print (L"%lxS", Data->ProcessingPolicy->SaLifetime.HardLifetime);
      HasPre = TRUE;
    }

    if (HasPre) {
      Print (L"]");
    }

    if (HasPre || Data->ProcessingPolicy->ExtSeqNum ||
        Data->ProcessingPolicy->SeqOverflow || Data->ProcessingPolicy->FragCheck) {
      Print (L"\n  ");
    }

    String1 = MapIntegerToString (Data->ProcessingPolicy->Proto, mMapIpSecProtocol);
    String2 = MapIntegerToString (Data->ProcessingPolicy->AuthAlgoId, mMapAuthAlgo);
    String3 = MapIntegerToString (Data->ProcessingPolicy->EncAlgoId, mMapEncAlgo);
    Print (
      L"%s Auth:%s Encrypt:%s ",
      String1,
      String2,
      String3
      );

    Print (L"%s ", MapIntegerToString (Data->ProcessingPolicy->Mode, mMapIpSecMode));
    if (Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {
      Print (L"[");
      DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress);
      Print (L" -> ");
      DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);
      Print (L" %s]", MapIntegerToString (Data->ProcessingPolicy->TunnelOption->DF, mMapDfOption));
    }
  }

  Print (L"\n");

  return EFI_SUCCESS;
}

/**
  Print EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA2 content.

  @param[in] SaId          The pointer to the EFI_IPSEC_SA_ID structure.
  @param[in] Data          The pointer to the EFI_IPSEC_SA_DATA2 structure.
  @param[in] EntryIndex    The pointer to the Index in the SAD Database.

  @retval EFI_SUCCESS    Dump SAD information successfully.
**/
EFI_STATUS
DumpSadEntry (
  IN EFI_IPSEC_SA_ID      *SaId,
  IN EFI_IPSEC_SA_DATA2   *Data,
  IN UINTN                *EntryIndex
  )
{
  BOOLEAN    HasPre;
  CHAR16     *AuthAlgoStr;
  CHAR16     *EncAlgoStr;

  AuthAlgoStr      = NULL;
  EncAlgoStr       = NULL;

  //
  // SPI:1234 ESP Destination:xxx.xxx.xxx.xxx
  //  Mode:Transport SeqNum:134 AntiReplayWin:64 life:[0B,1023s,3400S] PathMTU:34
  //  Auth:xxxx/password Encrypt:yyyy/password
  //  xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400
  //

  Print (L"%d.", (*EntryIndex)++);
  Print (L"0x%x %s ", (UINTN) SaId->Spi, MapIntegerToString (SaId->Proto, mMapIpSecProtocol));
  if (Data->Mode == EfiIPsecTunnel) {
    Print (L"TunnelSourceAddress:");
    DumpIpAddress (&Data->TunnelSourceAddress);
    Print (L"\n");
    Print (L"  TunnelDestination:");
    DumpIpAddress (&Data->TunnelDestinationAddress);
    Print (L"\n");
  }

  Print (
    L"  Mode:%s SeqNum:%lx AntiReplayWin:%d ",
    MapIntegerToString (Data->Mode, mMapIpSecMode),
    Data->SNCount,
    (UINTN) Data->AntiReplayWindows
    );

  HasPre = FALSE;
  if (Data->SaLifetime.ByteCount != 0) {
    Print (HasPre ? L"," : L"life:[");
    Print (L"%lxB", Data->SaLifetime.ByteCount);
    HasPre = TRUE;
  }

  if (Data->SaLifetime.SoftLifetime != 0) {
    Print (HasPre ? L"," : L"life:[");
    Print (L"%lxs", Data->SaLifetime.SoftLifetime);
    HasPre = TRUE;
  }

  if (Data->SaLifetime.HardLifetime != 0) {
    Print (HasPre ? L"," : L"life:[");
    Print (L"%lxS", Data->SaLifetime.HardLifetime);
    HasPre = TRUE;
  }

  if (HasPre) {
    Print (L"] ");
  }

  Print (L"PathMTU:%d\n", (UINTN) Data->PathMTU);

  if (SaId->Proto == EfiIPsecAH) {
    Print (
      L"  Auth:%s/%s\n",
      MapIntegerToString (Data->AlgoInfo.AhAlgoInfo.AuthAlgoId, mMapAuthAlgo),
      Data->AlgoInfo.AhAlgoInfo.AuthKey
      );
  } else {
    AuthAlgoStr = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, mMapAuthAlgo);
    EncAlgoStr  = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.EncAlgoId, mMapEncAlgo);

    if (Data->ManualSet) {
      //
      // if the SAD is set manually the key is a Ascii string in most of time.
      // Print the Key in Ascii string format.
      //
      Print (L"  Auth:%s/",AuthAlgoStr);
      DumpAsciiString (
        Data->AlgoInfo.EspAlgoInfo.AuthKey,
        Data->AlgoInfo.EspAlgoInfo.AuthKeyLength
        );
      Print (L"\n  Encrypt:%s/",EncAlgoStr);
      DumpAsciiString (
        Data->AlgoInfo.EspAlgoInfo.EncKey,
        Data->AlgoInfo.EspAlgoInfo.EncKeyLength
        );
    } else {
      //
      // if the SAD is created by IKE, the key is a set of hex value in buffer.
      // Print the Key in Hex format.
      //
      Print (L"  Auth:%s/",AuthAlgoStr);
      DumpBuf ((UINT8 *)(Data->AlgoInfo.EspAlgoInfo.AuthKey), Data->AlgoInfo.EspAlgoInfo.AuthKeyLength);

      Print (L"\n  Encrypt:%s/",EncAlgoStr);
      DumpBuf ((UINT8 *)(Data->AlgoInfo.EspAlgoInfo.EncKey), Data->AlgoInfo.EspAlgoInfo.EncKeyLength);
    }
  }
  Print (L"\n");
  if (Data->SpdSelector != NULL) {
    Print (L"  ");
    DumpSpdSelector (Data->SpdSelector);
    Print (L"\n");
  }

  return EFI_SUCCESS;
}

/**
  Print EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA content.

  @param[in] PadId         The pointer to the EFI_IPSEC_PAD_ID structure.
  @param[in] Data          The pointer to the EFI_IPSEC_PAD_DATA structure.
  @param[in] EntryIndex    The pointer to the Index in the PAD Database.

  @retval EFI_SUCCESS    Dump PAD information successfully.
**/
EFI_STATUS
DumpPadEntry (
  IN EFI_IPSEC_PAD_ID      *PadId,
  IN EFI_IPSEC_PAD_DATA    *Data,
  IN UINTN                 *EntryIndex
  )
{
  CHAR16    *String1;
  CHAR16    *String2;

  //
  // ADDR:10.23.17.34/15
  // IDEv1 PreSharedSecret IKE-ID
  // password
  //

  Print (L"%d.", (*EntryIndex)++);

  if (PadId->PeerIdValid) {
    Print (L"ID:%s", PadId->Id.PeerId);
  } else {
    Print (L"ADDR:");
    DumpAddressInfo (&PadId->Id.IpAddress);
  }

  Print (L"\n");

  String1 = MapIntegerToString (Data->AuthProtocol, mMapAuthProto);
  String2 = MapIntegerToString (Data->AuthMethod, mMapAuthMethod);
  Print (
    L"  %s %s",
    String1,
    String2
    );

  if (Data->IkeIdFlag) {
    Print (L"IKE-ID");
  }

  Print (L"\n");

  if (Data->AuthData != NULL) {
    DumpAsciiString (Data->AuthData, Data->AuthDataSize);
    Print (L"\n");
  }

  if (Data->RevocationData != NULL) {
    Print (L"  %s\n", Data->RevocationData);
  }

  return EFI_SUCCESS;

}

VISIT_POLICY_ENTRY  mDumpPolicyEntry[] = {
  (VISIT_POLICY_ENTRY) DumpSpdEntry,
  (VISIT_POLICY_ENTRY) DumpSadEntry,
  (VISIT_POLICY_ENTRY) DumpPadEntry
};

/**
  Print all entry information in the database according to datatype.

  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.
  @param[in] ParamPackage    The pointer to the ParamPackage list.

  @retval EFI_SUCCESS    Dump all information successfully.
  @retval Others         Some mistaken case.
**/
EFI_STATUS
ListPolicyEntry (
  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,
  IN LIST_ENTRY                    *ParamPackage
  )
{
  UINTN  EntryIndex;

  EntryIndex = 0;
  return ForeachPolicyEntry (DataType, mDumpPolicyEntry[DataType], &EntryIndex);
}

