#include "Ipv6Dissector.h"

Ipv6Dissector::Ipv6Dissector() :
	m_ipHdr(0)
{
}

Ipv6Dissector::~Ipv6Dissector() {
  delete m_ipHdr;
}

Ipv6Dissector * Ipv6Dissector::Clone() const {
  return new Ipv6Dissector();
}

bool Ipv6Dissector::Dissect(const unsigned char * pData, ssize_t nDataLen) {
	std::clog << ">>> Ipv6Dissector::Dissect: " << nDataLen << std::endl;
	
  if (!m_ipHdr)
	  m_ipHdr = new IpHeader;
  memcpy(m_ipHdr, pData, sizeof(IpHeader));
  pData += sizeof(IpHeader);
#if BYTE_ORDER == LITTLE_ENDIAN
  m_ipHdr->payloadLength = ntohs(m_ipHdr->payloadLength);
#endif
	std::clog << "Ipv6Dissector::Dissect: payload length=" << m_ipHdr->payloadLength << std::endl;
  SavePayload(pData, m_ipHdr->payloadLength);
  SaveLayerInfo();
  return true;
}

bool Ipv6Dissector::NeedReassembly() const {
	std::clog << ">>> Ipv6Dissector::NeedReassembly" << std::endl;
	
  return false;
}

bool Ipv6Dissector::Reassemble(Dissector * pDissector, ProtocolInfo * pProtocolInfo) {
	std::clog << ">>> Ipv6Dissector::Reassemble" << std::endl;
	
  return false;
}

const EProtocolType Ipv6Dissector::GetUpperLayerType() const 
{
	EProtocolType res = EProtocolType_Unknown;
	switch(m_ipHdr->tos)
	{
	case 6:
		res = EProtocolType_Tcp;
		break;
	case 17:
		res = EProtocolType_Udp;
		break;
	}

	std::clog << ">>> Ipv6Dissector::GetUpperLayerType: " << res << std::endl;
	return res;
}

ProtocolInfoElement * Ipv6Dissector::CreateLayerInfo()
{
	std::clog << ">>> Ipv6Dissector::CreateLayerInfo" << std::endl;
	
	IPv6Info * pRes = new IPv6Info();
	/*std::vector<unsigned char> nSrcAddr(16), nDstAddr(16);
	for (int i = 0; i < nSrcAddr.size(); i++)
	{
		nSrcAddr[i] = m_ipHdr->srcAddr[i];
		nDstAddr[i] = m_ipHdr->destAddr[i];
	}*/
	char buffer[128] = {0};
  InetNtop((const unsigned char *)&m_ipHdr->srcAddr[0], buffer, 128);
	std::clog << "Ipv6Dissector::CreateLayerInfo (src): " << buffer << std::endl;
	pRes->SetSourceAddress(std::string(buffer));
	memset(buffer, 0x00, 128);
  InetNtop((const unsigned char *)m_ipHdr->srcAddr, buffer, 128);
	std::clog << "Ipv6Dissector::CreateLayerInfo (dest): " << buffer << std::endl;
	pRes->SetDestinationAddress(std::string(buffer));
	return pRes;
}

/**
	This is a copy of IPv6 inet_ntop C function
	Warning: encapsulated IPv4 not supported
	http://www.koders.com/c/fid8E3CB9FE9913C1E6C2E8316BAB79203D6241AED9.aspx
  **/
#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
const char *Ipv6Dissector::InetNtop (const unsigned char *src, char *dst, socklen_t size)
{
  /*
   * Note that int32_t and int16_t need only be "at least" large enough
   * to contain a value of the specified size.  On some systems, like
   * Crays, there is no such thing as an integer variable with 16 bits.
   * Keep this in mind if you think this function should have been coded
   * to use pointer overlays.  All the world's not a VAX.
   */
  char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
  struct
  {
    int base, len;
  } best, cur;
  unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ];
  int i;

  /*
   * Preprocess:
   *      Copy the input (bytewise) array into a wordwise array.
   *      Find the longest run of 0x00's in src[] for :: shorthanding.
   */
  memset (words, '\0', sizeof words);
  for (i = 0; i < NS_IN6ADDRSZ; i += 2)
    words[i / 2] = (src[i] << 8) | src[i + 1];
  best.base = -1;
  cur.base = -1;
  for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++)
    {
      if (words[i] == 0)
	{
	  if (cur.base == -1)
	    cur.base = i, cur.len = 1;
	  else
	    cur.len++;
	}
      else
	{
	  if (cur.base != -1)
	    {
	      if (best.base == -1 || cur.len > best.len)
		best = cur;
	      cur.base = -1;
	    }
	}
    }
  if (cur.base != -1)
    {
      if (best.base == -1 || cur.len > best.len)
	best = cur;
    }
  if (best.base != -1 && best.len < 2)
    best.base = -1;

  /*
   * Format the result.
   */
  tp = tmp;
  for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++)
    {
      /* Are we inside the best run of 0x00's? */
      if (best.base != -1 && i >= best.base && i < (best.base + best.len))
	{
	  if (i == best.base)
	    *tp++ = ':';
	  continue;
	}
      /* Are we following an initial run of 0x00s or any real hex? */
      if (i != 0)
	*tp++ = ':';
      /* Is this address an encapsulated IPv4? */
      if (i == 6 && best.base == 0 &&
	  (best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
	{
	  //if (!inet_ntop4 (src + 12, tp, sizeof tmp - (tp - tmp)))
	    return (NULL);
	  //tp += strlen (tp);
	  //break;
	}
      {
	int len = sprintf (tp, "%x", words[i]);
	if (len < 0)
	  return NULL;
	tp += len;
      }
    }
  /* Was it a trailing run of 0x00's? */
  if (best.base != -1 && (best.base + best.len) ==
      (NS_IN6ADDRSZ / NS_INT16SZ))
    *tp++ = ':';
  *tp++ = '\0';

  /*
   * Check for overflow, copy, and we're done.
   */
  if ((socklen_t) (tp - tmp) > size)
    {
      errno = ENOSPC;
      return NULL;
    }

  return strcpy (dst, tmp);
}
