/*
 * Copyright (C) 2009 - 2010 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * 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 Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

/* $Id$ */

#include "serverexchange/DNSQuery.h"
#include <commontypes.h>

#include <resolv.h>

using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_DNS_Query;

static bool dns_get_string(const unsigned char* s, const unsigned char* msgstart, const unsigned char* msgend,
    String& str)
{
    char expanded[2*NS_MAXDNAME+1];

    int cs = dn_expand(msgstart, msgend, s, expanded, 2*NS_MAXDNAME);
    if (cs < 0) {
        return false;
    }

    str = expanded;
    return true;
}


Header::Header(const DNS_QUERY_HEADER& hdr)
    : m_id(ntohs(hdr.id)), m_resp(hdr.qr), m_opcode(static_cast<opcode_t>(hdr.opcode)),
      m_authoritative(hdr.aa), m_truncated(hdr.tc), m_rd(hdr.rd), m_ra(hdr.ra),
      m_ad(hdr.ad), m_cd(hdr.cd), m_rcode(static_cast<ret_code_t>(hdr.rcode)),
      m_qdcount(ntohs(hdr.qdcount)), m_ancount(ntohs(hdr.ancount)),
      m_nscount(ntohs(hdr.nscount)), m_arcount(ntohs(hdr.arcount))
{
}

unsigned Header::id() const
{
    return m_id;
}

bool Header::isResponse() const
{
    return m_resp;
}
/*
opcode_t Header::opcode() const
{
    return m_opcode;
}
*/
bool Header::isAuthoritative() const
{
    return m_authoritative;
}

bool Header::isTruncated() const
{
    return m_truncated;
}

bool Header::isRecursionDesired() const
{
    return m_rd;
}

bool Header::isRecursionAvailable() const
{
    return m_ra;
}

bool Header::dataAuthentic() const
{
    return m_ad;
}

bool Header::isCheckDisabled() const
{
    return m_cd;
}
/*
ret_code_t Header::rcode() const
{
    return m_rcode;
}
*/
unsigned Header::questionCount() const
{
    return m_qdcount;
}

unsigned Header::answerCount() const
{
    return m_ancount;
}

unsigned Header::nsCount() const
{
    return m_nscount;
}

unsigned Header::additionalCount() const
{
    return m_arcount;
}

unsigned Header::size() const
{
    return sizeof(DNS_QUERY_HEADER);
}


DnsQuestion::DnsQuestion()
: m_qtype(e_TY_Undefined),
m_qclass(e_CL_Undefined),
m_full_length(0)
{
}

bool DnsQuestion::Set(const unsigned char* data, size_t max_len,
    const unsigned char* msgstart, const unsigned char* msgend)
{
    int len = dn_skipname(data, msgend);
    if (len < 0)
    {
        return false;
    }

    if (!dns_get_string(data, msgstart, msgend, m_qname))
    {
        return false;
    }

    uint16_t tmp = 0;
    memcpy(&tmp, data+len, sizeof(uint16_t));
    m_qtype = static_cast<EnumType>(ntohs(tmp));

    memcpy(&tmp, data+len+sizeof(uint16_t), sizeof(uint16_t));
    m_qclass = static_cast<EnumClass>(ntohs(tmp));

    m_full_length = len + 2*sizeof(uint16_t);

    return true;
}

size_t DnsQuestion::GetLength() const
{
    return m_full_length;
}

const String& DnsQuestion::GetName() const
{
    return m_qname;
}

EnumType DnsQuestion::GetType() const
{
    return m_qtype;
}

EnumClass DnsQuestion::GetClass() const
{
    return m_qclass;
}

DnsSrvRr::DnsSrvRr()
//:m_data(0)
{
}

bool DnsSrvRr::Set(const unsigned char* data, size_t max_len,
    const unsigned char* msgstart, const unsigned char* msgend)
{
    int len = dn_skipname(data, msgend);

    if (len < 0)
    {
        return false;
    }

    const unsigned char* p = data + len;

    m_full_length = len;
    if (!dns_get_string(data, msgstart, msgend, m_name))
    {
        return false;
    }

    uint16_t tmp16;
    uint32_t tmp32;

    memcpy(&tmp16, p, sizeof(uint16_t));
    m_type = static_cast<EnumType>(ntohs(tmp16));
    p += sizeof(uint16_t);
    m_full_length += sizeof(uint16_t);

    memcpy(&tmp16, p, sizeof(uint16_t));
    m_class = static_cast<EnumClass>(ntohs(tmp16));
    p += sizeof(uint16_t);
    m_full_length += sizeof(uint16_t);

    memcpy(&tmp32, p, sizeof(uint32_t));
    m_ttl = ntohl(tmp32);
    p += sizeof(uint32_t);
    m_full_length += sizeof(uint32_t);

    memcpy(&tmp16, p, sizeof(uint16_t));
    m_length = ntohs(tmp16);
    p += sizeof(uint16_t);
    m_full_length += sizeof(uint16_t) + m_length;

    if (m_full_length > max_len) {
        return false;
    }

    m_data_ptr = p;
//    m_data = new uint8_t[m_length];
//    memcpy(m_data, p, m_length);

//-------------------
    p = m_data_ptr;

    memcpy(&m_priority, p, sizeof(uint16_t));
    m_priority = ntohs(m_priority);
    p += sizeof(uint16_t);

    memcpy(&m_weight, p, sizeof(uint16_t));
    m_weight = ntohs(m_weight);
    p += sizeof(uint16_t);

    memcpy(&m_port, p, sizeof(uint16_t));
    m_port = ntohs(m_port);
    p += sizeof(uint16_t);

    if (!dns_get_string(p, msgstart, msgend, m_target))
    {
        return false;
    }

    return true;
}


const String& DnsSrvRr::name() const
{
    return m_name;
}

EnumType DnsSrvRr::type() const
{
    return m_type;
}

EnumClass DnsSrvRr::getClass() const
{
    return m_class;
}

uint32_t DnsSrvRr::ttl() const
{
    return m_ttl;
}

uint16_t DnsSrvRr::length() const
{
    return m_length;
}
/*
const uint8_t* DnsSrvRr::data() const
{
    return m_data;
}
*/

size_t  DnsSrvRr::getLength() const
{
    return m_full_length;
}

const String& DnsSrvRr::target() const
{
    return this->m_target;
}

uint16_t DnsSrvRr::priority() const
{
    return this->m_priority;
}

uint16_t DnsSrvRr::weight() const
{
    return this->m_weight;
}

uint16_t DnsSrvRr::port() const
{
    return this->m_port;
}

