// ndppd - NDP Proxy Daemon
// Copyright (C) 2011  Daniel Adolfsson <daniel@priv.nu>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#include <string>
#include <vector>
#include <map>

#include <cstring>
#include <cstdio>
#include <cstdlib>

#include <netinet/ip6.h>
#include <arpa/inet.h>

#include "ndppd.h"
#include "address.h"

__NDPPD_NS_BEGIN

address::address()
{
   _addr.s6_addr32[0] = 0;
   _addr.s6_addr32[1] = 0;
   _addr.s6_addr32[2] = 0;
   _addr.s6_addr32[3] = 0;

   _mask.s6_addr32[0] = 0xffffffff;
   _mask.s6_addr32[1] = 0xffffffff;
   _mask.s6_addr32[2] = 0xffffffff;
   _mask.s6_addr32[3] = 0xffffffff;
}

address::address(const address& addr)
{
   _addr.s6_addr32[0] = addr._addr.s6_addr32[0];
   _addr.s6_addr32[1] = addr._addr.s6_addr32[1];
   _addr.s6_addr32[2] = addr._addr.s6_addr32[2];
   _addr.s6_addr32[3] = addr._addr.s6_addr32[3];

   _mask.s6_addr32[0] = addr._mask.s6_addr32[0];
   _mask.s6_addr32[1] = addr._mask.s6_addr32[1];
   _mask.s6_addr32[2] = addr._mask.s6_addr32[2];
   _mask.s6_addr32[3] = addr._mask.s6_addr32[3];
}

address::address(const std::string& str)
{
   if(!parse_string(str))
   {
      _addr.s6_addr32[0] = 0;
      _addr.s6_addr32[1] = 0;
      _addr.s6_addr32[2] = 0;
      _addr.s6_addr32[3] = 0;

      _mask.s6_addr32[0] = 0xffffffff;
      _mask.s6_addr32[1] = 0xffffffff;
      _mask.s6_addr32[2] = 0xffffffff;
      _mask.s6_addr32[3] = 0xffffffff;
   }
}

address::address(const char *str)
{
   if(!parse_string(str))
   {
      _addr.s6_addr32[0] = 0;
      _addr.s6_addr32[1] = 0;
      _addr.s6_addr32[2] = 0;
      _addr.s6_addr32[3] = 0;

      _mask.s6_addr32[0] = 0xffffffff;
      _mask.s6_addr32[1] = 0xffffffff;
      _mask.s6_addr32[2] = 0xffffffff;
      _mask.s6_addr32[3] = 0xffffffff;
   }
}

address::address(const in6_addr& addr)
{
   _addr.s6_addr32[0] = addr.s6_addr32[0];
   _addr.s6_addr32[1] = addr.s6_addr32[1];
   _addr.s6_addr32[2] = addr.s6_addr32[2];
   _addr.s6_addr32[3] = addr.s6_addr32[3];

   _mask.s6_addr32[0] = 0xffffffff;
   _mask.s6_addr32[1] = 0xffffffff;
   _mask.s6_addr32[2] = 0xffffffff;
   _mask.s6_addr32[3] = 0xffffffff;
}

address::address(const in6_addr& addr, const in6_addr& mask)
{
   _addr.s6_addr32[0] = addr.s6_addr32[0];
   _addr.s6_addr32[1] = addr.s6_addr32[1];
   _addr.s6_addr32[2] = addr.s6_addr32[2];
   _addr.s6_addr32[3] = addr.s6_addr32[3];

   _mask.s6_addr32[0] = mask.s6_addr32[0];
   _mask.s6_addr32[1] = mask.s6_addr32[1];
   _mask.s6_addr32[2] = mask.s6_addr32[2];
   _mask.s6_addr32[3] = mask.s6_addr32[3];
}

address::address(const in6_addr& addr, int pf)
{
   _addr.s6_addr32[0] = addr.s6_addr32[0];
   _addr.s6_addr32[1] = addr.s6_addr32[1];
   _addr.s6_addr32[2] = addr.s6_addr32[2];
   _addr.s6_addr32[3] = addr.s6_addr32[3];

   prefix(pf);
}

bool address::operator==(const address& addr) const
{
   return !(((_addr.s6_addr32[0] ^ addr._addr.s6_addr32[0]) & _mask.s6_addr32[0]) |
            ((_addr.s6_addr32[1] ^ addr._addr.s6_addr32[1]) & _mask.s6_addr32[1]) |
            ((_addr.s6_addr32[2] ^ addr._addr.s6_addr32[2]) & _mask.s6_addr32[2]) |
            ((_addr.s6_addr32[3] ^ addr._addr.s6_addr32[3]) & _mask.s6_addr32[3]));
}

bool address::operator!=(const address& addr) const
{
   return !!(((_addr.s6_addr32[0] ^ addr._addr.s6_addr32[0]) & _mask.s6_addr32[0]) |
             ((_addr.s6_addr32[1] ^ addr._addr.s6_addr32[1]) & _mask.s6_addr32[1]) |
             ((_addr.s6_addr32[2] ^ addr._addr.s6_addr32[2]) & _mask.s6_addr32[2]) |
             ((_addr.s6_addr32[3] ^ addr._addr.s6_addr32[3]) & _mask.s6_addr32[3]));
}

int address::prefix() const
{
   if(!_mask.s6_addr[0])
      return 0;

   for(int p = 0; p < 128; p++)
   {
      int byi = p / 8, bii = 7 - (p % 8);
      if(!(_mask.s6_addr[byi] & (1 << bii)))
         return p;
   }

   return 128;
}

void address::prefix(int pf)
{
   if((pf < 0) || (pf > 128))
      return;

   _mask.s6_addr32[0] = 0;
   _mask.s6_addr32[1] = 0;
   _mask.s6_addr32[2] = 0;
   _mask.s6_addr32[3] = 0;

   while(pf--)
   {
      int byi = pf / 8, bii = 7 - (pf % 8);
      _mask.s6_addr[byi] |= 1 << bii;
   }
}

const std::string address::to_string() const
{
   char buf[INET6_ADDRSTRLEN + 8];

   if(!inet_ntop(AF_INET6, &_addr, buf, INET6_ADDRSTRLEN))
      return "::1";

   // TODO: What to do about invalid ip?

   int p;

   if((p = prefix()) < 128)
      sprintf(buf + strlen(buf), "/%d", p);

   return buf;
}

bool address::parse_string(const std::string& str)
{
   char buf[INET6_ADDRSTRLEN], *b;
   int sz, pfx;

   sz = 0;
   b  = buf;

   const char *p = str.c_str();

   while(*p && isspace(*p))
      p++;

   while(*p)
   {
      if((*p == '/') || isspace(*p))
         break;

      if((*p != ':') && !isxdigit(*p))
         return false;

      if(sz >= (INET6_ADDRSTRLEN - 1))
         return false;

      *b++ = *p++;

      sz++;
   }

   *b = '\0';

   if(inet_pton(AF_INET6, buf, &_addr) <= 0)
      return false;

   while(*p && isspace(*p))
      p++;

   if(*p == '\0')
   {
      _mask.s6_addr32[0] = 0xffffffff;
      _mask.s6_addr32[1] = 0xffffffff;
      _mask.s6_addr32[2] = 0xffffffff;
      _mask.s6_addr32[3] = 0xffffffff;
      return true;
   }

   if(*p++ != '/')
      return false;

   while(*p && isspace(*p))
      p++;

   sz = 0;
   b  = buf;

   while(*p)
   {
      if(!isdigit(*p))
         return false;

      if(sz > 3)
         return false;

      *b++ = *p++;
      sz++;
   }

   *b = '\0';

   prefix(atoi(buf));

   return true;
}

address::operator std::string() const
{
   return to_string();
}

struct in6_addr& address::addr()
{
   return _addr;
}

const struct in6_addr& address::const_addr() const
{
   return _addr;
}

struct in6_addr& address::mask()
{
   return _mask;
}

bool address::is_multicast() const
{
   return _addr.s6_addr[0] == 0xff;
}

bool address::is_unicast() const
{
   return _addr.s6_addr[0] != 0xff;
}

__NDPPD_NS_END