/** @file
  Tests for Dhcp6Io.c.

  Copyright (c) Microsoft Corporation
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/GoogleTestLib.h>

extern "C" {
  #include <Uefi.h>
  #include <Library/BaseLib.h>
  #include <Library/DebugLib.h>
  #include <Library/BaseMemoryLib.h>
  #include "../Dhcp6Impl.h"
  #include "../Dhcp6Utility.h"
  #include "Dhcp6IoGoogleTest.h"
}

////////////////////////////////////////////////////////////////////////
// Defines
////////////////////////////////////////////////////////////////////////

#define DHCP6_PACKET_MAX_LEN  1500

// This definition is used by this test but is also required to compile
// by Dhcp6Io.c
#define DHCPV6_OPTION_IA_NA  3
#define DHCPV6_OPTION_IA_TA  4

#define SEARCH_PATTERN      0xDEADC0DE
#define SEARCH_PATTERN_LEN  sizeof(SEARCH_PATTERN)

////////////////////////////////////////////////////////////////////////
// Test structures for IA_NA and IA_TA options
////////////////////////////////////////////////////////////////////////
typedef struct {
  UINT16    Code;
  UINT16    Len;
  UINT32    IAID;
} DHCPv6_OPTION;

typedef struct {
  DHCPv6_OPTION    Header;
  UINT32           T1;
  UINT32           T2;
  UINT8            InnerOptions[0];
} DHCPv6_OPTION_IA_NA;

typedef struct {
  DHCPv6_OPTION    Header;
  UINT8            InnerOptions[0];
} DHCPv6_OPTION_IA_TA;

////////////////////////////////////////////////////////////////////////
// Symbol Definitions
// These functions are not directly under test - but required to compile
////////////////////////////////////////////////////////////////////////

// This definition is used by this test but is also required to compile
// by Dhcp6Io.c
EFI_IPv6_ADDRESS  mAllDhcpRelayAndServersAddress = {
  { 0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2 }
};

EFI_STATUS
EFIAPI
UdpIoSendDatagram (
  IN  UDP_IO           *UdpIo,
  IN  NET_BUF          *Packet,
  IN  UDP_END_POINT    *EndPoint OPTIONAL,
  IN  EFI_IP_ADDRESS   *Gateway  OPTIONAL,
  IN  UDP_IO_CALLBACK  CallBack,
  IN  VOID             *Context
  )
{
  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
UdpIoRecvDatagram (
  IN  UDP_IO           *UdpIo,
  IN  UDP_IO_CALLBACK  CallBack,
  IN  VOID             *Context,
  IN  UINT32           HeadLen
  )
{
  return EFI_SUCCESS;
}

////////////////////////////////////////////////////////////////////////
// Dhcp6AppendOptionTest Tests
////////////////////////////////////////////////////////////////////////

class Dhcp6AppendOptionTest : public ::testing::Test {
public:
  UINT8 *Buffer = NULL;
  EFI_DHCP6_PACKET *Packet;

protected:
  // Add any setup code if needed
  virtual void
  SetUp (
    )
  {
    // Initialize any resources or variables
    Buffer = (UINT8 *)AllocateZeroPool (DHCP6_PACKET_MAX_LEN);
    ASSERT_NE (Buffer, (UINT8 *)NULL);

    Packet       = (EFI_DHCP6_PACKET *)Buffer;
    Packet->Size = DHCP6_PACKET_MAX_LEN;
  }

  // Add any cleanup code if needed
  virtual void
  TearDown (
    )
  {
    // Clean up any resources or variables
    if (Buffer != NULL) {
      FreePool (Buffer);
    }
  }
};

// Test Description:
// Attempt to append an option to a packet that is too small by a duid that is too large
TEST_F (Dhcp6AppendOptionTest, InvalidDataExpectBufferTooSmall) {
  UINT8           *Cursor;
  EFI_DHCP6_DUID  *UntrustedDuid;
  EFI_STATUS      Status;

  UntrustedDuid = (EFI_DHCP6_DUID *)AllocateZeroPool (sizeof (EFI_DHCP6_DUID));
  ASSERT_NE (UntrustedDuid, (EFI_DHCP6_DUID *)NULL);

  UntrustedDuid->Length = NTOHS (0xFFFF);

  Cursor = Dhcp6AppendOptionTest::Packet->Dhcp6.Option;

  Status = Dhcp6AppendOption (
             Dhcp6AppendOptionTest::Packet,
             &Cursor,
             HTONS (Dhcp6OptServerId),
             UntrustedDuid->Length,
             UntrustedDuid->Duid
             );

  ASSERT_EQ (Status, EFI_BUFFER_TOO_SMALL);
}

// Test Description:
// Attempt to append an option to a packet that is large enough
TEST_F (Dhcp6AppendOptionTest, ValidDataExpectSuccess) {
  UINT8           *Cursor;
  EFI_DHCP6_DUID  *UntrustedDuid;
  EFI_STATUS      Status;
  UINTN           OriginalLength;

  UINT8  Duid[6] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };

  Packet->Length = sizeof (EFI_DHCP6_HEADER);
  OriginalLength = Packet->Length;

  UntrustedDuid = (EFI_DHCP6_DUID *)AllocateZeroPool (OFFSET_OF (EFI_DHCP6_DUID, Duid) + sizeof (Duid));
  ASSERT_NE (UntrustedDuid, (EFI_DHCP6_DUID *)NULL);

  UntrustedDuid->Length = NTOHS (sizeof (Duid));
  CopyMem (UntrustedDuid->Duid, Duid, sizeof (Duid));

  Cursor = Dhcp6AppendOptionTest::Packet->Dhcp6.Option;

  Status = Dhcp6AppendOption (
             Dhcp6AppendOptionTest::Packet,
             &Cursor,
             HTONS (Dhcp6OptServerId),
             UntrustedDuid->Length,
             UntrustedDuid->Duid
             );

  ASSERT_EQ (Status, EFI_SUCCESS);

  // verify that the pointer to cursor moved by the expected amount
  ASSERT_EQ (Cursor, (UINT8 *)Dhcp6AppendOptionTest::Packet->Dhcp6.Option + sizeof (Duid) + 4);

  // verify that the length of the packet is now the expected amount
  ASSERT_EQ (Dhcp6AppendOptionTest::Packet->Length, OriginalLength + sizeof (Duid) + 4);
}

////////////////////////////////////////////////////////////////////////
// Dhcp6AppendETOption Tests
////////////////////////////////////////////////////////////////////////

class Dhcp6AppendETOptionTest : public ::testing::Test {
public:
  UINT8 *Buffer = NULL;
  EFI_DHCP6_PACKET *Packet;

protected:
  // Add any setup code if needed
  virtual void
  SetUp (
    )
  {
    // Initialize any resources or variables
    Buffer = (UINT8 *)AllocateZeroPool (DHCP6_PACKET_MAX_LEN);
    ASSERT_NE (Buffer, (UINT8 *)NULL);

    Packet         = (EFI_DHCP6_PACKET *)Buffer;
    Packet->Size   = DHCP6_PACKET_MAX_LEN;
    Packet->Length = sizeof (EFI_DHCP6_HEADER);
  }

  // Add any cleanup code if needed
  virtual void
  TearDown (
    )
  {
    // Clean up any resources or variables
    if (Buffer != NULL) {
      FreePool (Buffer);
    }
  }
};

// Test Description:
// Attempt to append an option to a packet that is too small by a duid that is too large
TEST_F (Dhcp6AppendETOptionTest, InvalidDataExpectBufferTooSmall) {
  UINT8           *Cursor;
  EFI_STATUS      Status;
  DHCP6_INSTANCE  Instance;
  UINT16          ElapsedTimeVal;
  UINT16          *ElapsedTime;

  Cursor      = Dhcp6AppendETOptionTest::Packet->Dhcp6.Option;
  ElapsedTime = &ElapsedTimeVal;

  Packet->Length = Packet->Size - 2;

  Status = Dhcp6AppendETOption (
             Dhcp6AppendETOptionTest::Packet,
             &Cursor,
             &Instance,                    // Instance is not used in this function
             &ElapsedTime
             );

  // verify that we error out because the packet is too small for the option header
  ASSERT_EQ (Status, EFI_BUFFER_TOO_SMALL);

  // reset the length
  Packet->Length = sizeof (EFI_DHCP6_HEADER);
}

// Test Description:
// Attempt to append an option to a packet that is large enough
TEST_F (Dhcp6AppendETOptionTest, ValidDataExpectSuccess) {
  UINT8           *Cursor;
  EFI_STATUS      Status;
  DHCP6_INSTANCE  Instance;
  UINT16          ElapsedTimeVal;
  UINT16          *ElapsedTime;
  UINTN           ExpectedSize;
  UINTN           OriginalLength;

  Cursor         = Dhcp6AppendETOptionTest::Packet->Dhcp6.Option;
  ElapsedTime    = &ElapsedTimeVal;
  ExpectedSize   = 6;
  OriginalLength = Packet->Length;

  Status = Dhcp6AppendETOption (
             Dhcp6AppendETOptionTest::Packet,
             &Cursor,
             &Instance,                    // Instance is not used in this function
             &ElapsedTime
             );

  // verify that the status is EFI_SUCCESS
  ASSERT_EQ (Status, EFI_SUCCESS);

  // verify that the pointer to cursor moved by the expected amount
  ASSERT_EQ (Cursor, (UINT8 *)Dhcp6AppendETOptionTest::Packet->Dhcp6.Option + ExpectedSize);

  // verify that the length of the packet is now the expected amount
  ASSERT_EQ (Dhcp6AppendETOptionTest::Packet->Length, OriginalLength + ExpectedSize);
}

////////////////////////////////////////////////////////////////////////
// Dhcp6AppendIaOption Tests
////////////////////////////////////////////////////////////////////////

class Dhcp6AppendIaOptionTest : public ::testing::Test {
public:
  UINT8 *Buffer = NULL;
  EFI_DHCP6_PACKET *Packet;
  EFI_DHCP6_IA *Ia;

protected:
  // Add any setup code if needed
  virtual void
  SetUp (
    )
  {
    // Initialize any resources or variables
    Buffer = (UINT8 *)AllocateZeroPool (DHCP6_PACKET_MAX_LEN);
    ASSERT_NE (Buffer, (UINT8 *)NULL);

    Packet       = (EFI_DHCP6_PACKET *)Buffer;
    Packet->Size = DHCP6_PACKET_MAX_LEN;

    Ia = (EFI_DHCP6_IA *)AllocateZeroPool (sizeof (EFI_DHCP6_IA) + sizeof (EFI_DHCP6_IA_ADDRESS) * 2);
    ASSERT_NE (Ia, (EFI_DHCP6_IA *)NULL);

    CopyMem (Ia->IaAddress, mAllDhcpRelayAndServersAddress.Addr, sizeof (EFI_IPv6_ADDRESS));
    CopyMem (Ia->IaAddress + 1, mAllDhcpRelayAndServersAddress.Addr, sizeof (EFI_IPv6_ADDRESS));

    Ia->IaAddressCount = 2;
  }

  // Add any cleanup code if needed
  virtual void
  TearDown (
    )
  {
    // Clean up any resources or variables
    if (Buffer != NULL) {
      FreePool (Buffer);
    }

    if (Ia != NULL) {
      FreePool (Ia);
    }
  }
};

// Test Description:
// Attempt to append an option to a packet that doesn't have enough space
// for the option header
TEST_F (Dhcp6AppendIaOptionTest, IaNaInvalidDataExpectBufferTooSmall) {
  UINT8       *Cursor;
  EFI_STATUS  Status;

  Packet->Length = Packet->Size - 2;

  Ia->Descriptor.Type = Dhcp6OptIana;
  Ia->Descriptor.IaId = 0x12345678;

  Cursor = Dhcp6AppendIaOptionTest::Packet->Dhcp6.Option;

  Status = Dhcp6AppendIaOption (
             Dhcp6AppendIaOptionTest::Packet,
             &Cursor,
             Ia,
             0x12345678,
             0x11111111,
             Dhcp6OptIana
             );

  // verify that we error out because the packet is too small for the option header
  ASSERT_EQ (Status, EFI_BUFFER_TOO_SMALL);

  // reset the length
  Packet->Length = sizeof (EFI_DHCP6_HEADER);
}

// Test Description:
// Attempt to append an option to a packet that doesn't have enough space
// for the option header
TEST_F (Dhcp6AppendIaOptionTest, IaTaInvalidDataExpectBufferTooSmall) {
  UINT8       *Cursor;
  EFI_STATUS  Status;

  // Use up nearly all the space in the packet
  Packet->Length = Packet->Size - 2;

  Ia->Descriptor.Type = Dhcp6OptIata;
  Ia->Descriptor.IaId = 0x12345678;

  Cursor = Dhcp6AppendIaOptionTest::Packet->Dhcp6.Option;

  Status = Dhcp6AppendIaOption (
             Dhcp6AppendIaOptionTest::Packet,
             &Cursor,
             Ia,
             0,
             0,
             Dhcp6OptIata
             );

  // verify that we error out because the packet is too small for the option header
  ASSERT_EQ (Status, EFI_BUFFER_TOO_SMALL);

  // reset the length
  Packet->Length = sizeof (EFI_DHCP6_HEADER);
}

TEST_F (Dhcp6AppendIaOptionTest, IaNaValidDataExpectSuccess) {
  UINT8       *Cursor;
  EFI_STATUS  Status;
  UINTN       ExpectedSize;
  UINTN       OriginalLength;

  //
  // 2 bytes for the option header type
  //
  ExpectedSize = 2;
  //
  // 2 bytes for the option header length
  //
  ExpectedSize += 2;
  //
  // 4 bytes for the IAID
  //
  ExpectedSize += 4;
  //
  // + 4 bytes for the T1
  //
  ExpectedSize += 4;
  //
  // + 4 bytes for the T2
  //
  ExpectedSize += 4;
  //
  // + (4 + sizeof (EFI_DHCP6_IA_ADDRESS)) * 2;
  //   + 2 bytes for the option header type
  //   + 2 bytes for the option header length
  //   + sizeof (EFI_DHCP6_IA_ADDRESS) for the IA Address
  //
  ExpectedSize += (4 + sizeof (EFI_DHCP6_IA_ADDRESS)) * 2;

  Cursor = Dhcp6AppendIaOptionTest::Packet->Dhcp6.Option;

  Packet->Length = sizeof (EFI_DHCP6_HEADER);
  OriginalLength = Packet->Length;

  Ia->Descriptor.Type = Dhcp6OptIana;
  Ia->Descriptor.IaId = 0x12345678;

  Status = Dhcp6AppendIaOption (
             Dhcp6AppendIaOptionTest::Packet,
             &Cursor,
             Ia,
             0x12345678,
             0x12345678,
             Dhcp6OptIana
             );

  // verify that the pointer to cursor moved by the expected amount
  ASSERT_EQ (Cursor, (UINT8 *)Dhcp6AppendIaOptionTest::Packet->Dhcp6.Option + ExpectedSize);

  // verify that the length of the packet is now the expected amount
  ASSERT_EQ (Dhcp6AppendIaOptionTest::Packet->Length, OriginalLength + ExpectedSize);

  // verify that the status is EFI_SUCCESS
  ASSERT_EQ (Status, EFI_SUCCESS);
}

TEST_F (Dhcp6AppendIaOptionTest, IaTaValidDataExpectSuccess) {
  UINT8       *Cursor;
  EFI_STATUS  Status;
  UINTN       ExpectedSize;
  UINTN       OriginalLength;

  //
  // 2 bytes for the option header type
  //
  ExpectedSize = 2;
  //
  // 2 bytes for the option header length
  //
  ExpectedSize += 2;
  //
  // 4 bytes for the IAID
  //
  ExpectedSize += 4;
  //
  // + (4 + sizeof (EFI_DHCP6_IA_ADDRESS)) * 2;
  //   + 2 bytes for the option header type
  //   + 2 bytes for the option header length
  //   + sizeof (EFI_DHCP6_IA_ADDRESS) for the IA Address
  //
  ExpectedSize += (4 + sizeof (EFI_DHCP6_IA_ADDRESS)) * 2;

  Cursor = Dhcp6AppendIaOptionTest::Packet->Dhcp6.Option;

  Packet->Length = sizeof (EFI_DHCP6_HEADER);
  OriginalLength = Packet->Length;

  Ia->Descriptor.Type = Dhcp6OptIata;
  Ia->Descriptor.IaId = 0x12345678;

  Status = Dhcp6AppendIaOption (
             Dhcp6AppendIaOptionTest::Packet,
             &Cursor,
             Ia,
             0,
             0,
             Dhcp6OptIata
             );

  // verify that the pointer to cursor moved by the expected amount
  ASSERT_EQ (Cursor, (UINT8 *)Dhcp6AppendIaOptionTest::Packet->Dhcp6.Option + ExpectedSize);

  // verify that the length of the packet is now the expected amount
  ASSERT_EQ (Dhcp6AppendIaOptionTest::Packet->Length, OriginalLength + ExpectedSize);

  // verify that the status is EFI_SUCCESS
  ASSERT_EQ (Status, EFI_SUCCESS);
}

////////////////////////////////////////////////////////////////////////
// Dhcp6SeekInnerOptionSafe Tests
////////////////////////////////////////////////////////////////////////

// Define a fixture for your tests if needed
class Dhcp6SeekInnerOptionSafeTest : public ::testing::Test {
protected:
  // Add any setup code if needed
  virtual void
  SetUp (
    )
  {
    // Initialize any resources or variables
  }

  // Add any cleanup code if needed
  virtual void
  TearDown (
    )
  {
    // Clean up any resources or variables
  }
};

// Test Description:
// This test verifies that Dhcp6SeekInnerOptionSafe returns EFI_SUCCESS when the IANA option is found.
TEST_F (Dhcp6SeekInnerOptionSafeTest, IANAValidOptionExpectSuccess) {
  EFI_STATUS           Result;
  UINT8                Option[sizeof (DHCPv6_OPTION_IA_NA) + SEARCH_PATTERN_LEN] = { 0 };
  UINT32               OptionLength                                              = sizeof (Option);
  DHCPv6_OPTION_IA_NA  *OptionPtr                                                = (DHCPv6_OPTION_IA_NA *)Option;
  UINT32               SearchPattern                                             = SEARCH_PATTERN;

  UINTN   SearchPatternLength = SEARCH_PATTERN_LEN;
  UINT8   *InnerOptionPtr     = NULL;
  UINT16  InnerOptionLength   = 0;

  OptionPtr->Header.Code = Dhcp6OptIana;
  OptionPtr->Header.Len  = HTONS (4 + 12); // Valid length has to be more than 12
  OptionPtr->Header.IAID = 0x12345678;
  OptionPtr->T1          = 0x11111111;
  OptionPtr->T2          = 0x22222222;
  CopyMem (OptionPtr->InnerOptions, &SearchPattern, SearchPatternLength);

  Result = Dhcp6SeekInnerOptionSafe (
             Dhcp6OptIana,
             Option,
             OptionLength,
             &InnerOptionPtr,
             &InnerOptionLength
             );
  ASSERT_EQ (Result, EFI_SUCCESS);
  ASSERT_EQ (InnerOptionLength, 4);
  ASSERT_EQ (CompareMem (InnerOptionPtr, &SearchPattern, SearchPatternLength), 0);
}

// Test Description:
// This test verifies that Dhcp6SeekInnerOptionSafe returns EFI_DEIVCE_ERROR when the IANA option size is invalid.
TEST_F (Dhcp6SeekInnerOptionSafeTest, IANAInvalidSizeExpectFail) {
  // Lets add an inner option of bytes we expect to find
  EFI_STATUS           Status;
  UINT8                Option[sizeof (DHCPv6_OPTION_IA_NA) + SEARCH_PATTERN_LEN] = { 0 };
  UINT32               OptionLength                                              = sizeof (Option);
  DHCPv6_OPTION_IA_NA  *OptionPtr                                                = (DHCPv6_OPTION_IA_NA *)Option;
  UINT32               SearchPattern                                             = SEARCH_PATTERN;

  UINTN   SearchPatternLength = SEARCH_PATTERN_LEN;
  UINT8   *InnerOptionPtr     = NULL;
  UINT16  InnerOptionLength   = 0;

  OptionPtr->Header.Code = Dhcp6OptIana;
  OptionPtr->Header.Len  = HTONS (4); // Set the length to lower than expected (12)
  OptionPtr->Header.IAID = 0x12345678;
  OptionPtr->T1          = 0x11111111;
  OptionPtr->T2          = 0x22222222;
  CopyMem (OptionPtr->InnerOptions, &SearchPattern, SearchPatternLength);

  // Set the InnerOptionLength to be less than the size of the option
  Status = Dhcp6SeekInnerOptionSafe (
             Dhcp6OptIana,
             Option,
             OptionLength,
             &InnerOptionPtr,
             &InnerOptionLength
             );
  ASSERT_EQ (Status, EFI_DEVICE_ERROR);

  // Now set the OptionLength to be less than the size of the option
  OptionLength = sizeof (DHCPv6_OPTION_IA_NA) - 1;
  Status       = Dhcp6SeekInnerOptionSafe (
                   Dhcp6OptIana,
                   Option,
                   OptionLength,
                   &InnerOptionPtr,
                   &InnerOptionLength
                   );
  ASSERT_EQ (Status, EFI_DEVICE_ERROR);
}

// Test Description:
// This test verifies that Dhcp6SeekInnerOptionSafe returns EFI_SUCCESS when the IATA option is found
TEST_F (Dhcp6SeekInnerOptionSafeTest, IATAValidOptionExpectSuccess) {
  // Lets add an inner option of bytes we expect to find
  EFI_STATUS           Status;
  UINT8                Option[sizeof (DHCPv6_OPTION_IA_TA) + SEARCH_PATTERN_LEN] = { 0 };
  UINT32               OptionLength                                              = sizeof (Option);
  DHCPv6_OPTION_IA_TA  *OptionPtr                                                = (DHCPv6_OPTION_IA_TA *)Option;
  UINT32               SearchPattern                                             = SEARCH_PATTERN;

  UINTN   SearchPatternLength = SEARCH_PATTERN_LEN;
  UINT8   *InnerOptionPtr     = NULL;
  UINT16  InnerOptionLength   = 0;

  OptionPtr->Header.Code = Dhcp6OptIata;
  OptionPtr->Header.Len  = HTONS (4 + 4); // Valid length has to be more than 4
  OptionPtr->Header.IAID = 0x12345678;
  CopyMem (OptionPtr->InnerOptions, &SearchPattern, SearchPatternLength);

  Status = Dhcp6SeekInnerOptionSafe (
             Dhcp6OptIata,
             Option,
             OptionLength,
             &InnerOptionPtr,
             &InnerOptionLength
             );
  ASSERT_EQ (Status, EFI_SUCCESS);
  ASSERT_EQ (InnerOptionLength, 4);
  ASSERT_EQ (CompareMem (InnerOptionPtr, &SearchPattern, SearchPatternLength), 0);
}

// Test Description:
// This test verifies that Dhcp6SeekInnerOptionSafe returns EFI_SUCCESS when the IATA option size is invalid.
TEST_F (Dhcp6SeekInnerOptionSafeTest, IATAInvalidSizeExpectFail) {
  // Lets add an inner option of bytes we expect to find
  EFI_STATUS           Status;
  UINT8                Option[sizeof (DHCPv6_OPTION_IA_TA) + SEARCH_PATTERN_LEN] = { 0 };
  UINT32               OptionLength                                              = sizeof (Option);
  DHCPv6_OPTION_IA_TA  *OptionPtr                                                = (DHCPv6_OPTION_IA_TA *)Option;
  UINT32               SearchPattern                                             = SEARCH_PATTERN;

  UINTN   SearchPatternLength = SEARCH_PATTERN_LEN;
  UINT8   *InnerOptionPtr     = NULL;
  UINT16  InnerOptionLength   = 0;

  OptionPtr->Header.Code = Dhcp6OptIata;
  OptionPtr->Header.Len  = HTONS (2); // Set the length to lower than expected (4)
  OptionPtr->Header.IAID = 0x12345678;
  CopyMem (OptionPtr->InnerOptions, &SearchPattern, SearchPatternLength);

  Status = Dhcp6SeekInnerOptionSafe (
             Dhcp6OptIata,
             Option,
             OptionLength,
             &InnerOptionPtr,
             &InnerOptionLength
             );
  ASSERT_EQ (Status, EFI_DEVICE_ERROR);

  // Now lets try modifying the OptionLength to be less than the size of the option
  OptionLength = sizeof (DHCPv6_OPTION_IA_TA) - 1;
  Status       = Dhcp6SeekInnerOptionSafe (
                   Dhcp6OptIata,
                   Option,
                   OptionLength,
                   &InnerOptionPtr,
                   &InnerOptionLength
                   );
  ASSERT_EQ (Status, EFI_DEVICE_ERROR);
}

// Test Description:
// This test verifies that any other Option Type fails
TEST_F (Dhcp6SeekInnerOptionSafeTest, InvalidOption) {
  // Lets add an inner option of bytes we expect to find
  EFI_STATUS           Result;
  UINT8                Option[sizeof (DHCPv6_OPTION_IA_TA) + SEARCH_PATTERN_LEN] = { 0 };
  UINT32               OptionLength                                              = sizeof (Option);
  DHCPv6_OPTION_IA_TA  *OptionPtr                                                = (DHCPv6_OPTION_IA_TA *)Option;
  UINT32               SearchPattern                                             = SEARCH_PATTERN;

  UINTN   SearchPatternLength = SEARCH_PATTERN_LEN;
  UINT8   *InnerOptionPtr     = NULL;
  UINT16  InnerOptionLength   = 0;

  OptionPtr->Header.Code = 0xC0DE;
  OptionPtr->Header.Len  = HTONS (2); // Set the length to lower than expected (4)
  OptionPtr->Header.IAID = 0x12345678;
  CopyMem (OptionPtr->InnerOptions, &SearchPattern, SearchPatternLength);

  Result = Dhcp6SeekInnerOptionSafe (0xC0DE, Option, OptionLength, &InnerOptionPtr, &InnerOptionLength);
  ASSERT_EQ (Result, EFI_DEVICE_ERROR);
}

////////////////////////////////////////////////////////////////////////
// Dhcp6SeekStsOption Tests
////////////////////////////////////////////////////////////////////////

#define PACKET_SIZE  (1500)

class Dhcp6SeekStsOptionTest : public ::testing::Test {
public:
  DHCP6_INSTANCE Instance      = { 0 };
  EFI_DHCP6_PACKET *Packet     = NULL;
  EFI_DHCP6_CONFIG_DATA Config = { 0 };

protected:
  // Add any setup code if needed
  virtual void
  SetUp (
    )
  {
    // Allocate a packet
    Packet = (EFI_DHCP6_PACKET *)AllocateZeroPool (PACKET_SIZE);
    ASSERT_NE (Packet, nullptr);

    // Initialize the packet
    Packet->Size = PACKET_SIZE;

    Instance.Config = &Config;
  }

  // Add any cleanup code if needed
  virtual void
  TearDown (
    )
  {
    // Clean up any resources or variables
    FreePool (Packet);
  }
};

// Test Description:
// This test verifies that Dhcp6SeekStsOption returns EFI_DEVICE_ERROR when the option is invalid
// This verifies that the calling function is working as expected
TEST_F (Dhcp6SeekStsOptionTest, SeekIATAOptionExpectFail) {
  EFI_STATUS    Status;
  UINT8         *Option             = NULL;
  UINT32        SearchPattern       = SEARCH_PATTERN;
  UINT16        SearchPatternLength = SEARCH_PATTERN_LEN;
  UINT16        *Len                = NULL;
  EFI_DHCP6_IA  Ia;

  ZeroMem (&Ia, sizeof (Ia));
  Ia.Descriptor.Type                = DHCPV6_OPTION_IA_TA;
  Ia.IaAddressCount                 = 1;
  Ia.IaAddress[0].PreferredLifetime = 0xDEADBEEF;
  Ia.IaAddress[0].ValidLifetime     = 0xDEADAAAA;
  Ia.IaAddress[0].IpAddress         = mAllDhcpRelayAndServersAddress;

  Packet->Length = sizeof (EFI_DHCP6_HEADER);

  Option = Dhcp6SeekStsOptionTest::Packet->Dhcp6.Option;

  // Let's append the option to the packet
  Status = Dhcp6AppendOption (
             Dhcp6SeekStsOptionTest::Packet,
             &Option,
             Dhcp6OptStatusCode,
             HTONS (SearchPatternLength),
             (UINT8 *)&SearchPattern
             );
  ASSERT_EQ (Status, EFI_SUCCESS);

  // Inner option length - this will be overwritten later
  Len = (UINT16 *)(Option + 2);

  // Fill in the inner IA option
  Status = Dhcp6AppendIaOption (
             Dhcp6SeekStsOptionTest::Packet,
             &Option,
             &Ia,
             0x12345678,
             0x11111111,
             0x22222222
             );
  ASSERT_EQ (Status, EFI_SUCCESS);

  // overwrite the len of inner Ia option
  *Len = HTONS (3);

  Dhcp6SeekStsOptionTest::Instance.Config->IaDescriptor.Type = DHCPV6_OPTION_IA_TA;

  Option = NULL;
  Status = Dhcp6SeekStsOption (&(Dhcp6SeekStsOptionTest::Instance), Dhcp6SeekStsOptionTest::Packet, &Option);

  ASSERT_EQ (Status, EFI_DEVICE_ERROR);
}

// Test Description:
// This test verifies that Dhcp6SeekInnerOptionSafe returns EFI_SUCCESS when the IATA option size is invalid.
TEST_F (Dhcp6SeekStsOptionTest, SeekIANAOptionExpectSuccess) {
  EFI_STATUS    Status              = EFI_NOT_FOUND;
  UINT8         *Option             = NULL;
  UINT32        SearchPattern       = SEARCH_PATTERN;
  UINT16        SearchPatternLength = SEARCH_PATTERN_LEN;
  EFI_DHCP6_IA  Ia;

  ZeroMem (&Ia, sizeof (Ia));
  Ia.Descriptor.Type                = DHCPV6_OPTION_IA_NA;
  Ia.IaAddressCount                 = 1;
  Ia.IaAddress[0].PreferredLifetime = 0x11111111;
  Ia.IaAddress[0].ValidLifetime     = 0x22222222;
  Ia.IaAddress[0].IpAddress         = mAllDhcpRelayAndServersAddress;
  Packet->Length                    = sizeof (EFI_DHCP6_HEADER);

  Option = Dhcp6SeekStsOptionTest::Packet->Dhcp6.Option;

  Status = Dhcp6AppendOption (
             Dhcp6SeekStsOptionTest::Packet,
             &Option,
             Dhcp6OptStatusCode,
             HTONS (SearchPatternLength),
             (UINT8 *)&SearchPattern
             );
  ASSERT_EQ (Status, EFI_SUCCESS);

  Status = Dhcp6AppendIaOption (
             Dhcp6SeekStsOptionTest::Packet,
             &Option,
             &Ia,
             0x12345678,
             0x11111111,
             0x22222222
             );
  ASSERT_EQ (Status, EFI_SUCCESS);

  Dhcp6SeekStsOptionTest::Instance.Config->IaDescriptor.Type = DHCPV6_OPTION_IA_NA;

  Option = NULL;
  Status = Dhcp6SeekStsOption (&(Dhcp6SeekStsOptionTest::Instance), Dhcp6SeekStsOptionTest::Packet, &Option);

  ASSERT_EQ (Status, EFI_SUCCESS);
}
