/*  -*- C++ -*-
 *  Copyright (C) 2003 Thiago Macieira <thiago@kde.org>
 *
 *
 *  Permission is hereby granted, free of charge, to any person obtaining
 *  a copy of this software and associated documentation files (the
 *  "Software"), to deal in the Software without restriction, including
 *  without limitation the rights to use, copy, modify, merge, publish,
 *  distribute, sublicense, and/or sell copies of the Software, and to
 *  permit persons to whom the Software is furnished to do so, subject to
 *  the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "k3socketaddress.h"

#include <config-network.h>
#include <config-kde4support.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <QFile>
#include <QObject>

#include "klocalizedstring.h"

#ifndef Q_CC_MSVC
#include "netsupp.h"
#endif

using namespace KNetwork;

#if 0
class KIpAddress_localhostV4 : public KIpAddress
{
public:
    KIpAddress_localhostV4()
    {
        *m_data = htonl(0x7f000001);
        m_version = 4;
    }
};

class KIpAddress_localhostV6 : public KIpAddress
{
public:
    KIpAddress_localhostV6()
        : KIpAddress(0L, 6)
    {
        m_data[3] = htonl(1);
    }
};
#endif

static const char localhostV4_data[] = { 127, 0, 0, 1 };
static const char localhostV6_data[] = { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 1 };

const KIpAddress KIpAddress::localhostV4(&localhostV4_data, 4);
const KIpAddress KIpAddress::localhostV6(&localhostV6_data, 6);
const KIpAddress KIpAddress::anyhostV4(0L, 4);
const KIpAddress KIpAddress::anyhostV6(0L, 6);

// helper function to test if an IPv6 v4-mapped address is equal to its IPv4 counterpart
static bool check_v4mapped(const quint32 *v6addr, quint32 v4addr)
{
    // check that the v6 is a v4-mapped address
    if (!(v6addr[0] == 0 && v6addr[1] == 0 && v6addr[2] == htonl(0x0000ffff))) {
        return false;    // not a v4-mapped address
    }

    return v6addr[3] == v4addr;
}

// copy operator
KIpAddress &KIpAddress::operator =(const KIpAddress &other)
{
    m_version = other.m_version;
    if (m_version == 4 || m_version == 6) {
        memcpy(m_data, other.m_data, sizeof(m_data));
    }
    return *this;
}

// comparison
bool KIpAddress::compare(const KIpAddress &other, bool checkMapped) const
{
    if (m_version == other.m_version)
        switch (m_version) {
        case 0:
            // both objects are empty
            return true;

        case 4:
            // IPv4 address
            return *m_data == *other.m_data;

        case 6:
            // IPv6 address
            // they are 128-bit long, that is, 16 bytes
            return memcmp(m_data, other.m_data, 16) == 0;
        }

    if (checkMapped) {
        // check the possibility of a v4-mapped address being compared to an IPv4 one
        if (m_version == 6 && other.m_version == 4 && check_v4mapped(m_data, *other.m_data)) {
            return true;
        }

        if (other.m_version == 6 && m_version == 4 && check_v4mapped(other.m_data, *m_data)) {
            return true;
        }
    }

    return false;
}

// sets the address to the given address
bool KIpAddress::setAddress(const QString &address)
{
    m_version = 0;

    // try to guess the address version
    if (address.indexOf(QLatin1Char(':')) != -1) {
#ifdef AF_INET6
        // guessing IPv6

        quint32 buf[4];
        if (inet_pton(AF_INET6, address.toLatin1(), buf)) {
            memcpy(m_data, buf, sizeof(m_data));
            m_version = 6;
            return true;
        }
#endif

        return false;
    } else {
        quint32 buf;
        if (inet_pton(AF_INET, address.toLatin1(), &buf)) {
            *m_data = buf;
            m_version = 4;
            return true;
        }

        return false;
    }

    return false;         // can never happen!
}

bool KIpAddress::setAddress(const char *address)
{
    return setAddress(QLatin1String(address));
}

// set from binary data
bool KIpAddress::setAddress(const void *raw, int version)
{
    // this always succeeds
    // except if version is invalid
    if (version != 4 && version != 6) {
        return false;
    }

    m_version = version;
    if (raw != 0L) {
        memcpy(m_data, raw, version == 4 ? 4 : 16);
    } else {
        memset(m_data, 0, 16);
    }

    return true;
}

// presentation form
QString KIpAddress::toString() const
{
    char buf[sizeof "1111:2222:3333:4444:5555:6666:255.255.255.255" + 2];
    buf[0] = '\0';
    switch (m_version) {
    case 4:
        inet_ntop(AF_INET, (void *)m_data, buf, sizeof(buf) - 1);
        return QLatin1String(buf);

    case 6:
#ifdef AF_INET6
        inet_ntop(AF_INET6, (void *)m_data, buf, sizeof(buf) - 1);
#endif
        return QLatin1String(buf);
    }

    return QString();
}

/*
 * An IPv6 socket address
 * This is taken from RFC 2553.
 */
struct our_sockaddr_in6 {
# if HAVE_STRUCT_SOCKADDR_SA_LEN
    quint8        sin6_len;
    quint8        sin6_family;
# else  //!HAVE_STRUCT_SOCKADDR_SA_LEN
    quint16       sin6_family;
# endif
    quint16           sin6_port;  /* RFC says in_port_t */
    quint32       sin6_flowinfo;
    quint8        sin6_addr[16]; // 24 bytes up to here
    quint32       sin6_scope_id; // 28 bytes total
};

// useful definitions
#define MIN_SOCKADDR_LEN    sizeof(quint16)
#define SOCKADDR_IN_LEN     sizeof(sockaddr_in)
#define MIN_SOCKADDR_IN6_LEN    ((quintptr) &(((our_sockaddr_in6*)0)->sin6_scope_id))
#define SOCKADDR_IN6_LEN    sizeof(our_sockaddr_in6)
#define MIN_SOCKADDR_UN_LEN (sizeof(quint16) + sizeof(char))

class KNetwork::KSocketAddressData
{
public:
    /*
     * Note: maybe this should be virtual
     * But since the data is shared via the d pointer, it doesn't really matter
     * what one class sees, so will the other
     */
    class QMixSocketAddressRef : public KInetSocketAddress, public KUnixSocketAddress
    {
    public:
        QMixSocketAddressRef(KSocketAddressData *d)
            : KInetSocketAddress(d), KUnixSocketAddress(d)
        {
        }
    };
    QMixSocketAddressRef ref;

    union {
        struct sockaddr         *generic;
        struct sockaddr_in      *in;
        struct our_sockaddr_in6 *in6;
        struct sockaddr_un      *un;
    } addr;
    quint16 curlen, reallen;

    KSocketAddressData()
        : ref(this)
    {
        addr.generic = 0L;
        curlen = 0;
        invalidate();
    }

    ~KSocketAddressData()
    {
        if (addr.generic != 0L) {
            free(addr.generic);
        }
    }

    inline bool invalid() const
    {
        return reallen == 0;
    }

    inline void invalidate()
    {
        reallen = 0;
    }

    void dup(const sockaddr *sa, quint16 len, bool clear = true);

    void makeipv4()
    {
        short oldport = 0;
        if (!invalid())
            switch (addr.generic->sa_family) {
            case AF_INET:
                return;       // nothing to do here
#ifdef AF_INET6
            case AF_INET6:
                oldport = addr.in6->sin6_port;
                break;
#endif
            }

        // create new space
        dup(0L, SOCKADDR_IN_LEN);

        addr.in->sin_family = AF_INET;
#if HAVE_STRUCT_SOCKADDR_SA_LEN
        addr.in->sin_len = SOCKADDR_IN_LEN;
#endif
        addr.in->sin_port = oldport;
    }

    void makeipv6()
    {
        short oldport = 0;
        if (!invalid())
            switch (addr.generic->sa_family) {
            case AF_INET:
                oldport = addr.in->sin_port;
                break;

#ifdef AF_INET6
            case AF_INET6:
                return;       // nothing to do here
#endif
            }

        // make room
        dup(0L, SOCKADDR_IN6_LEN);
#ifdef AF_INET6
        addr.in6->sin6_family = AF_INET6;
#endif
#if HAVE_STRUCT_SOCKADDR_SA_LEN
        addr.in6->sin6_len = SOCKADDR_IN6_LEN;
#endif
        addr.in6->sin6_port = oldport;
        // sin6_scope_id and sin6_flowid are zero
    }

};

// create duplicates of
void KSocketAddressData::dup(const sockaddr *sa, quint16 len, bool clear)
{
    if (len < MIN_SOCKADDR_LEN) {
        // certainly invalid
        invalidate();
        return;
    }

    if (sa && ((sa->sa_family == AF_INET && len < SOCKADDR_IN_LEN) ||
#ifdef AF_INET6
               (sa->sa_family == AF_INET6 && len < MIN_SOCKADDR_IN6_LEN) ||
#endif
               (sa->sa_family == AF_UNIX && len < MIN_SOCKADDR_UN_LEN))) {
        // also invalid
        invalidate();
        return;
    }

    // good
    reallen = len;
    if (len > curlen) {
        if (len < 32) {
            curlen = 32;    // big enough for sockaddr_in and sockaddr_in6
        } else {
            curlen = len;
        }
        addr.generic = (sockaddr *)realloc(addr.generic, curlen);
    }

    if (sa != 0L) {
        memcpy(addr.generic, sa, len); // copy

        // now, normalise the data
        if (addr.generic->sa_family == AF_INET) {
            reallen = SOCKADDR_IN_LEN;    // no need to be larger
        }
#ifdef AF_INET6
        else if (addr.generic->sa_family == AF_INET6) {
            // set the extra field (sin6_scope_id)

            // the buffer is never smaller than 32 bytes, so this is always
            // allowed
            if (reallen < SOCKADDR_IN6_LEN) {
                addr.in6->sin6_scope_id = 0;
            }

            reallen = SOCKADDR_IN6_LEN;
        }
#endif
        else if (addr.generic->sa_family == AF_UNIX) {
            reallen = MIN_SOCKADDR_UN_LEN + strlen(addr.un->sun_path);
        }
    } else if (clear) {
        memset(addr.generic, 0, len);
        addr.generic->sa_family = AF_UNSPEC;
    }
}

// default constructor
KSocketAddress::KSocketAddress()
    : d(new KSocketAddressData)
{
}

// constructor from binary data
KSocketAddress::KSocketAddress(const sockaddr *sa, quint16 len)
    : d(new KSocketAddressData)
{
    setAddress(sa, len);
}

KSocketAddress::KSocketAddress(const KSocketAddress &other)
    : d(new(KSocketAddressData))
{
    *this = other;
}

KSocketAddress::KSocketAddress(KSocketAddressData *d2)
    : d(d2)
{
}

KSocketAddress::~KSocketAddress()
{
    // prevent double-deletion, since we're already being deleted
    if (d) {
        d->ref.KInetSocketAddress::d = 0L;
        d->ref.KUnixSocketAddress::d = 0L;
        delete d;
    }
}

KSocketAddress &KSocketAddress::operator =(const KSocketAddress &other)
{
    if (other.d && !other.d->invalid()) {
        d->dup(other.d->addr.generic, other.d->reallen);
    } else {
        d->invalidate();
    }
    return *this;
}

const sockaddr *KSocketAddress::address() const
{
    if (d->invalid()) {
        return 0L;
    }
    return d->addr.generic;
}

sockaddr *KSocketAddress::address()
{
    if (d->invalid()) {
        return 0L;
    }
    return d->addr.generic;
}

KSocketAddress &KSocketAddress::setAddress(const sockaddr *sa, quint16 len)
{
    if (sa != 0L && len >= MIN_SOCKADDR_LEN) {
        d->dup(sa, len);
    } else {
        d->invalidate();
    }

    return *this;
}

quint16 KSocketAddress::length() const
{
    if (d->invalid()) {
        return 0;
    }
    return d->reallen;
}

KSocketAddress &KSocketAddress::setLength(quint16 len)
{
    d->dup((sockaddr *)0L, len, false);

    return *this;
}

int KSocketAddress::family() const
{
    if (d->invalid()) {
        return AF_UNSPEC;
    }
    return d->addr.generic->sa_family;
}

KSocketAddress &KSocketAddress::setFamily(int family)
{
    if (d->invalid()) {
        d->dup((sockaddr *)0L, MIN_SOCKADDR_LEN);
    }
    d->addr.generic->sa_family = family;

    return *this;
}

bool KSocketAddress::operator ==(const KSocketAddress &other) const
{
    // if this is invalid, it's only equal if the other one is invalid as well
    if (d->invalid()) {
        return other.d->invalid();
    }

    // check the family to make sure we don't do unnecessary comparison
    if (d->addr.generic->sa_family != other.d->addr.generic->sa_family) {
        return false;    // not the same family, not equal
    }

    // same family then
    // check the ones we know already
    switch (d->addr.generic->sa_family) {
    case AF_INET:
        Q_ASSERT(d->reallen == SOCKADDR_IN_LEN);
        Q_ASSERT(other.d->reallen == SOCKADDR_IN_LEN);
        return memcmp(d->addr.in, other.d->addr.in, SOCKADDR_IN_LEN) == 0;

#ifdef AF_INET6
    case AF_INET6:
        Q_ASSERT(d->reallen >= MIN_SOCKADDR_IN6_LEN);
        Q_ASSERT(other.d->reallen >= MIN_SOCKADDR_IN6_LEN);

# if !HAVE_STRUCT_SOCKADDR_IN6 || defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)
        // check for the case where sin6_scope_id isn't present
        if (d->reallen != other.d->reallen) {
            if (memcmp(d->addr.in6, other.d->addr.in6, MIN_SOCKADDR_IN6_LEN) != 0) {
                return false;    // not equal
            }
            if (d->reallen > other.d->reallen) {
                return d->addr.in6->sin6_scope_id == 0;
            } else {
                return other.d->addr.in6->sin6_scope_id == 0;
            }
        }
# endif

        return memcmp(d->addr.in6, other.d->addr.in6, d->reallen) == 0;
#endif

    case AF_UNIX:
        Q_ASSERT(d->reallen >= MIN_SOCKADDR_UN_LEN);
        Q_ASSERT(other.d->reallen >= MIN_SOCKADDR_UN_LEN);

        // do a string comparison here
        return strcmp(d->addr.un->sun_path, other.d->addr.un->sun_path) == 0;

    default:
        // something else we don't know about
        // they are equal if and only if they are exactly equal
        if (d->reallen == other.d->reallen) {
            return memcmp(d->addr.generic, other.d->addr.generic, d->reallen) == 0;
        }
    }

    return false;     // not equal in any other case
}

QString KSocketAddress::nodeName() const
{
    if (d->invalid()) {
        return QString();
    }

    switch (d->addr.generic->sa_family) {
    case AF_INET:
#ifdef AF_INET6
    case AF_INET6:

        QString scopeid(QLatin1Char('%'));
        if (d->addr.generic->sa_family == AF_INET6 && d->addr.in6->sin6_scope_id) {
            scopeid += QString::number(d->addr.in6->sin6_scope_id);
        } else {
            scopeid.truncate(0);
        }
        return d->ref.ipAddress().toString() + scopeid;
#else
        return d->ref.ipAddress().toString();
#endif
    }

    // any other case, including AF_UNIX
    return QString();
}

QString KSocketAddress::serviceName() const
{
    if (d->invalid()) {
        return QString();
    }

    switch (d->addr.generic->sa_family) {
    case AF_INET:
#ifdef AF_INET6
    case AF_INET6:
#endif
        return QString::number(d->ref.port());

    case AF_UNIX:
        return d->ref.pathname();
    }

    return QString();
}

QString KSocketAddress::toString() const
{
    if (d->invalid()) {
        return QString();
    }

    QString fmt;

    if (d->addr.generic->sa_family == AF_INET) {
        fmt = QLatin1String("%1:%2");
    }
#ifdef AF_INET6
    else if (d->addr.generic->sa_family == AF_INET6) {
        fmt = QLatin1String("[%1]:%2");
    }
#endif
    else if (d->addr.generic->sa_family == AF_UNIX) {
        return QString(QLatin1String("unix:%1")).arg(serviceName());
    } else
        return i18nc("1: the unknown socket address family number",
                     "Unknown family %1", d->addr.generic->sa_family);

    return fmt.arg(nodeName()).arg(serviceName());
}

KInetSocketAddress &KSocketAddress::asInet()
{
    return d->ref;
}

KInetSocketAddress KSocketAddress::asInet() const
{
    return d->ref;
}

KUnixSocketAddress &KSocketAddress::asUnix()
{
    return d->ref;
}

KUnixSocketAddress KSocketAddress::asUnix() const
{
    return d->ref;
}

int KSocketAddress::ianaFamily(int af)
{
    switch (af) {
    case AF_INET:
        return 1;

#ifdef AF_INET6
    case AF_INET6:
        return 2;
#endif

    default:
        return 0;
    }
}

int KSocketAddress::fromIanaFamily(int iana)
{
    switch (iana) {
    case 1:
        return AF_INET;

#ifdef AF_INET6
    case 2:
        return AF_INET6;
#endif

    default:
        return AF_UNSPEC;
    }
}

// default constructor
KInetSocketAddress::KInetSocketAddress()
{
}

// binary data constructor
KInetSocketAddress::KInetSocketAddress(const sockaddr *sa, quint16 len)
    : KSocketAddress(sa, len)
{
    if (!d->invalid()) {
        update();
    }
}

// create from IP and port
KInetSocketAddress::KInetSocketAddress(const KIpAddress &host, quint16 port)
{
    setHost(host);
    setPort(port);
}

// copy constructor
KInetSocketAddress::KInetSocketAddress(const KInetSocketAddress &other)
    : KSocketAddress(other)
{
}

// special copy constructor
KInetSocketAddress::KInetSocketAddress(const KSocketAddress &other)
    : KSocketAddress(other)
{
    if (!d->invalid()) {
        update();
    }
}

// special constructor
KInetSocketAddress::KInetSocketAddress(KSocketAddressData *d)
    : KSocketAddress(d)
{
}

// destructor
KInetSocketAddress::~KInetSocketAddress()
{
    /* nothing to do */
}

// copy operator
KInetSocketAddress &KInetSocketAddress::operator =(const KInetSocketAddress &other)
{
    KSocketAddress::operator =(other);
    return *this;
}

// IP version
int KInetSocketAddress::ipVersion() const
{
    if (d->invalid()) {
        return 0;
    }

    switch (d->addr.generic->sa_family) {
    case AF_INET:
        return 4;

#ifdef AF_INET6
    case AF_INET6:
        return 6;
#endif
    }

    return 0;         // for all other cases
}

KIpAddress KInetSocketAddress::ipAddress() const
{
    if (d->invalid()) {
        return KIpAddress();    // return an empty address as well
    }

    switch (d->addr.generic->sa_family) {
    case AF_INET:
        return KIpAddress(&d->addr.in->sin_addr, 4);
#ifdef AF_INET6
    case AF_INET6:
        return KIpAddress(&d->addr.in6->sin6_addr, 6);
#endif
    }

    return KIpAddress();      // empty in all other cases
}

KInetSocketAddress &KInetSocketAddress::setHost(const KIpAddress &ip)
{
    switch (ip.version()) {
    case 4:
        makeIPv4();
        memcpy(&d->addr.in->sin_addr, ip.addr(), sizeof(d->addr.in->sin_addr));
        break;

    case 6:
        makeIPv6();
        memcpy(&d->addr.in6->sin6_addr, ip.addr(), sizeof(d->addr.in6->sin6_addr));
        break;

    default:
        // empty
        d->invalidate();
    }

    return *this;
}

// returns the port
quint16 KInetSocketAddress::port() const
{
    if (d->invalid()) {
        return 0;
    }

    switch (d->addr.generic->sa_family) {
    case AF_INET:
        return ntohs(d->addr.in->sin_port);

#ifdef AF_INET6
    case AF_INET6:
        return ntohs(d->addr.in6->sin6_port);
#endif
    }

    return 0;
}

KInetSocketAddress &KInetSocketAddress::setPort(quint16 port)
{
    if (d->invalid()) {
        makeIPv4();
    }

    switch (d->addr.generic->sa_family) {
    case AF_INET:
        d->addr.in->sin_port = htons(port);
        break;

#ifdef AF_INET6
    case AF_INET6:
        d->addr.in6->sin6_port = htons(port);
        break;
#endif

    default:
        d->invalidate();      // setting the port on something else
    }

    return *this;
}

KInetSocketAddress &KInetSocketAddress::makeIPv4()
{
    d->makeipv4();
    return *this;
}

KInetSocketAddress &KInetSocketAddress::makeIPv6()
{
    d->makeipv6();
    return *this;
}

quint32 KInetSocketAddress::flowinfo() const
{
#ifndef AF_INET6
    return 0;
#else

    if (!d->invalid() && d->addr.in6->sin6_family == AF_INET6) {
        return d->addr.in6->sin6_flowinfo;
    }
    return 0;
#endif
}

KInetSocketAddress &KInetSocketAddress::setFlowinfo(quint32 flowinfo)
{
    makeIPv6();           // must set here
    d->addr.in6->sin6_flowinfo = flowinfo;
    return *this;
}

int KInetSocketAddress::scopeId() const
{
#ifndef AF_INET6
    return 0;
#else

    if (!d->invalid() && d->addr.in6->sin6_family == AF_INET6) {
        return d->addr.in6->sin6_scope_id;
    }
    return 0;
#endif
}

KInetSocketAddress &KInetSocketAddress::setScopeId(int scopeid)
{
    makeIPv6();           // must set here
    d->addr.in6->sin6_scope_id = scopeid;
    return *this;
}

void KInetSocketAddress::update()
{
    if (d->addr.generic->sa_family == AF_INET) {
        return;
    }
#ifdef AF_INET6
    else if (d->addr.generic->sa_family == AF_INET6) {
        return;
    }
#endif
    else {
        d->invalidate();
    }
}

KUnixSocketAddress::KUnixSocketAddress()
{
}

KUnixSocketAddress::KUnixSocketAddress(const sockaddr *sa, quint16 len)
    : KSocketAddress(sa, len)
{
    if (!d->invalid() && d->addr.un->sun_family != AF_UNIX) {
        d->invalidate();
    }
}

KUnixSocketAddress::KUnixSocketAddress(const KUnixSocketAddress &other)
    : KSocketAddress(other)
{
}

KUnixSocketAddress::KUnixSocketAddress(const QString &pathname)
{
    setPathname(pathname);
}

KUnixSocketAddress::KUnixSocketAddress(KSocketAddressData *d)
    : KSocketAddress(d)
{
}

KUnixSocketAddress::~KUnixSocketAddress()
{
}

KUnixSocketAddress &KUnixSocketAddress::operator =(const KUnixSocketAddress &other)
{
    KSocketAddress::operator =(other);
    return *this;
}

QString KUnixSocketAddress::pathname() const
{
    if (!d->invalid() && d->addr.un->sun_family == AF_UNIX) {
        return QFile::decodeName(d->addr.un->sun_path);
    }
    return QString();
}

KUnixSocketAddress &KUnixSocketAddress::setPathname(const QString &path)
{
    d->dup(0L, MIN_SOCKADDR_UN_LEN + path.length());
    d->addr.un->sun_family = AF_UNIX;
    strcpy(d->addr.un->sun_path, QFile::encodeName(path));

#if HAVE_STRUCT_SOCKADDR_SA_LEN
    d->addr.un->sun_len = d->reallen;
#endif

    return *this;
}
