/*
 * Copyright 2009 Funambol, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id$ */

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

#if ! defined(PLATFORM_ANDROID)
#include <resolv.h>
#endif

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];
    memset(expanded, '\0', 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;
}

