/*
    LEAmDNS_Structs.cpp

    License (MIT license):
      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 "ESP8266mDNS.h"
#include "LEAmDNS_Priv.h"
#include "LEAmDNS_lwIPdefs.h"

namespace esp8266
{

/*
    LEAmDNS
*/
namespace MDNSImplementation
{

/**
    STRUCTS
*/

/**
    MDNSResponder::stcMDNSServiceTxt

    One MDNS TXT item.
    m_pcValue may be '\0'.
    Objects can be chained together (list, m_pNext).
    A 'm_bTemp' flag differentiates between static and dynamic items.
    Output as byte array 'c#=1' is supported.
*/

/*
    MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt constructor
*/
MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const char* p_pcKey /*= 0*/,
        const char* p_pcValue /*= 0*/,
        bool p_bTemp /*= false*/)
    :   m_pNext(0),
        m_pcKey(0),
        m_pcValue(0),
        m_bTemp(p_bTemp)
{

    setKey(p_pcKey);
    setValue(p_pcValue);
}

/*
    MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt copy-constructor
*/
MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const MDNSResponder::stcMDNSServiceTxt& p_Other)
    :   m_pNext(0),
        m_pcKey(0),
        m_pcValue(0),
        m_bTemp(false)
{

    operator=(p_Other);
}

/*
    MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt destructor
*/
MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNSServiceTxt::operator=
*/
MDNSResponder::stcMDNSServiceTxt& MDNSResponder::stcMDNSServiceTxt::operator=(const MDNSResponder::stcMDNSServiceTxt& p_Other)
{

    if (&p_Other != this)
    {
        clear();
        set(p_Other.m_pcKey, p_Other.m_pcValue, p_Other.m_bTemp);
    }
    return *this;
}

/*
    MDNSResponder::stcMDNSServiceTxt::clear
*/
bool MDNSResponder::stcMDNSServiceTxt::clear(void)
{

    releaseKey();
    releaseValue();
    return true;
}

/*
    MDNSResponder::stcMDNSServiceTxt::allocKey
*/
char* MDNSResponder::stcMDNSServiceTxt::allocKey(size_t p_stLength)
{

    releaseKey();
    if (p_stLength)
    {
        m_pcKey = new char[p_stLength + 1];
    }
    return m_pcKey;
}

/*
    MDNSResponder::stcMDNSServiceTxt::setKey
*/
bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey,
        size_t p_stLength)
{

    bool bResult = false;

    releaseKey();
    if (p_stLength)
    {
        if (allocKey(p_stLength))
        {
            strncpy(m_pcKey, p_pcKey, p_stLength);
            m_pcKey[p_stLength] = 0;
            bResult = true;
        }
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceTxt::setKey
*/
bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey)
{

    return setKey(p_pcKey, (p_pcKey ? strlen(p_pcKey) : 0));
}

/*
    MDNSResponder::stcMDNSServiceTxt::releaseKey
*/
bool MDNSResponder::stcMDNSServiceTxt::releaseKey(void)
{

    if (m_pcKey)
    {
        delete[] m_pcKey;
        m_pcKey = 0;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSServiceTxt::allocValue
*/
char* MDNSResponder::stcMDNSServiceTxt::allocValue(size_t p_stLength)
{

    releaseValue();
    if (p_stLength)
    {
        m_pcValue = new char[p_stLength + 1];
    }
    return m_pcValue;
}

/*
    MDNSResponder::stcMDNSServiceTxt::setValue
*/
bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue,
        size_t p_stLength)
{

    bool bResult = false;

    releaseValue();
    if (p_stLength)
    {
        if (allocValue(p_stLength))
        {
            strncpy(m_pcValue, p_pcValue, p_stLength);
            m_pcValue[p_stLength] = 0;
            bResult = true;
        }
    }
    else    // No value -> also OK
    {
        bResult = true;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceTxt::setValue
*/
bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue)
{

    return setValue(p_pcValue, (p_pcValue ? strlen(p_pcValue) : 0));
}

/*
    MDNSResponder::stcMDNSServiceTxt::releaseValue
*/
bool MDNSResponder::stcMDNSServiceTxt::releaseValue(void)
{

    if (m_pcValue)
    {
        delete[] m_pcValue;
        m_pcValue = 0;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSServiceTxt::set
*/
bool MDNSResponder::stcMDNSServiceTxt::set(const char* p_pcKey,
        const char* p_pcValue,
        bool p_bTemp /*= false*/)
{

    m_bTemp = p_bTemp;
    return ((setKey(p_pcKey)) &&
            (setValue(p_pcValue)));
}

/*
    MDNSResponder::stcMDNSServiceTxt::update
*/
bool MDNSResponder::stcMDNSServiceTxt::update(const char* p_pcValue)
{

    return setValue(p_pcValue);
}

/*
    MDNSResponder::stcMDNSServiceTxt::length

    length of eg. 'c#=1' without any closing '\0'
*/
size_t MDNSResponder::stcMDNSServiceTxt::length(void) const
{

    size_t  stLength = 0;
    if (m_pcKey)
    {
        stLength += strlen(m_pcKey);                     // Key
        stLength += 1;                                      // '='
        stLength += (m_pcValue ? strlen(m_pcValue) : 0); // Value
    }
    return stLength;
}


/**
    MDNSResponder::stcMDNSServiceTxts

    A list of zero or more MDNS TXT items.
    Dynamic TXT items can be removed by 'removeTempTxts'.
    A TXT item can be looke up by its 'key' member.
    Export as ';'-separated byte array is supported.
    Export as 'length byte coded' byte array is supported.
    Comparison ((all A TXT items in B and equal) AND (all B TXT items in A and equal)) is supported.

*/

/*
    MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts constructor
*/
MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(void)
    :   m_pTxts(0)
{

}

/*
    MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts copy-constructor
*/
MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other)
    :   m_pTxts(0)
{

    operator=(p_Other);
}

/*
    MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts destructor
*/
MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNSServiceTxts::operator=
*/
MDNSResponder::stcMDNSServiceTxts& MDNSResponder::stcMDNSServiceTxts::operator=(const stcMDNSServiceTxts& p_Other)
{

    if (this != &p_Other)
    {
        clear();

        for (stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; pOtherTxt; pOtherTxt = pOtherTxt->m_pNext)
        {
            add(new stcMDNSServiceTxt(*pOtherTxt));
        }
    }
    return *this;
}

/*
    MDNSResponder::stcMDNSServiceTxts::clear
*/
bool MDNSResponder::stcMDNSServiceTxts::clear(void)
{

    while (m_pTxts)
    {
        stcMDNSServiceTxt* pNext = m_pTxts->m_pNext;
        delete m_pTxts;
        m_pTxts = pNext;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSServiceTxts::add
*/
bool MDNSResponder::stcMDNSServiceTxts::add(MDNSResponder::stcMDNSServiceTxt* p_pTxt)
{

    bool bResult = false;

    if (p_pTxt)
    {
        p_pTxt->m_pNext = m_pTxts;
        m_pTxts = p_pTxt;
        bResult = true;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceTxts::remove
*/
bool MDNSResponder::stcMDNSServiceTxts::remove(stcMDNSServiceTxt* p_pTxt)
{

    bool    bResult = false;

    if (p_pTxt)
    {
        stcMDNSServiceTxt*  pPred = m_pTxts;
        while ((pPred) &&
                (pPred->m_pNext != p_pTxt))
        {
            pPred = pPred->m_pNext;
        }
        if (pPred)
        {
            pPred->m_pNext = p_pTxt->m_pNext;
            delete p_pTxt;
            bResult = true;
        }
        else if (m_pTxts == p_pTxt)     // No predecessor, but first item
        {
            m_pTxts = p_pTxt->m_pNext;
            delete p_pTxt;
            bResult = true;
        }
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceTxts::removeTempTxts
*/
bool MDNSResponder::stcMDNSServiceTxts::removeTempTxts(void)
{

    bool    bResult = true;

    stcMDNSServiceTxt*  pTxt = m_pTxts;
    while ((bResult) &&
            (pTxt))
    {
        stcMDNSServiceTxt*  pNext = pTxt->m_pNext;
        if (pTxt->m_bTemp)
        {
            bResult = remove(pTxt);
        }
        pTxt = pNext;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceTxts::find
*/
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey)
{

    stcMDNSServiceTxt* pResult = 0;

    for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext)
    {
        if ((p_pcKey) &&
                (0 == strcmp(pTxt->m_pcKey, p_pcKey)))
        {
            pResult = pTxt;
            break;
        }
    }
    return pResult;
}

/*
    MDNSResponder::stcMDNSServiceTxts::find
*/
const MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) const
{

    const stcMDNSServiceTxt*   pResult = 0;

    for (const stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext)
    {
        if ((p_pcKey) &&
                (0 == strcmp(pTxt->m_pcKey, p_pcKey)))
        {

            pResult = pTxt;
            break;
        }
    }
    return pResult;
}

/*
    MDNSResponder::stcMDNSServiceTxts::find
*/
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const stcMDNSServiceTxt* p_pTxt)
{

    stcMDNSServiceTxt* pResult = 0;

    for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext)
    {
        if (p_pTxt == pTxt)
        {
            pResult = pTxt;
            break;
        }
    }
    return pResult;
}

/*
    MDNSResponder::stcMDNSServiceTxts::length
*/
uint16_t MDNSResponder::stcMDNSServiceTxts::length(void) const
{

    uint16_t    u16Length = 0;

    stcMDNSServiceTxt*  pTxt = m_pTxts;
    while (pTxt)
    {
        u16Length += 1;                 // Length byte
        u16Length += pTxt->length();    // Text
        pTxt = pTxt->m_pNext;
    }
    return u16Length;
}

/*
    MDNSResponder::stcMDNSServiceTxts::c_strLength

    (incl. closing '\0'). Length bytes place is used for delimiting ';' and closing '\0'
*/
size_t MDNSResponder::stcMDNSServiceTxts::c_strLength(void) const
{

    return length();
}

/*
    MDNSResponder::stcMDNSServiceTxts::c_str
*/
bool MDNSResponder::stcMDNSServiceTxts::c_str(char* p_pcBuffer)
{

    bool bResult = false;

    if (p_pcBuffer)
    {
        bResult = true;

        *p_pcBuffer = 0;
        for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext)
        {
            size_t  stLength;
            if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0)))))
            {
                if (pTxt != m_pTxts)
                {
                    *p_pcBuffer++ = ';';
                }
                strncpy(p_pcBuffer, pTxt->m_pcKey, stLength); p_pcBuffer[stLength] = 0;
                p_pcBuffer += stLength;
                *p_pcBuffer++ = '=';
                if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0)))
                {
                    strncpy(p_pcBuffer, pTxt->m_pcValue, stLength); p_pcBuffer[stLength] = 0;
                    p_pcBuffer += stLength;
                }
            }
        }
        *p_pcBuffer++ = 0;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceTxts::bufferLength

    (incl. closing '\0').
*/
size_t MDNSResponder::stcMDNSServiceTxts::bufferLength(void) const
{

    return (length() + 1);
}

/*
    MDNSResponder::stcMDNSServiceTxts::toBuffer
*/
bool MDNSResponder::stcMDNSServiceTxts::buffer(char* p_pcBuffer)
{

    bool bResult = false;

    if (p_pcBuffer)
    {
        bResult = true;

        *p_pcBuffer = 0;
        for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext)
        {
            *(unsigned char*)p_pcBuffer++ = pTxt->length();
            size_t  stLength;
            if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0)))))
            {
                memcpy(p_pcBuffer, pTxt->m_pcKey, stLength);
                p_pcBuffer += stLength;
                *p_pcBuffer++ = '=';
                if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0)))
                {
                    memcpy(p_pcBuffer, pTxt->m_pcValue, stLength);
                    p_pcBuffer += stLength;
                }
            }
        }
        *p_pcBuffer++ = 0;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceTxts::compare
*/
bool MDNSResponder::stcMDNSServiceTxts::compare(const MDNSResponder::stcMDNSServiceTxts& p_Other) const
{

    bool    bResult = false;

    if ((bResult = (length() == p_Other.length())))
    {
        // Compare A->B
        for (const stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext)
        {
            const stcMDNSServiceTxt*    pOtherTxt = p_Other.find(pTxt->m_pcKey);
            bResult = ((pOtherTxt) &&
                       (pTxt->m_pcValue) &&
                       (pOtherTxt->m_pcValue) &&
                       (strlen(pTxt->m_pcValue) == strlen(pOtherTxt->m_pcValue)) &&
                       (0 == strcmp(pTxt->m_pcValue, pOtherTxt->m_pcValue)));
        }
        // Compare B->A
        for (const stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; ((bResult) && (pOtherTxt)); pOtherTxt = pOtherTxt->m_pNext)
        {
            const stcMDNSServiceTxt*    pTxt = find(pOtherTxt->m_pcKey);
            bResult = ((pTxt) &&
                       (pOtherTxt->m_pcValue) &&
                       (pTxt->m_pcValue) &&
                       (strlen(pOtherTxt->m_pcValue) == strlen(pTxt->m_pcValue)) &&
                       (0 == strcmp(pOtherTxt->m_pcValue, pTxt->m_pcValue)));
        }
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceTxts::operator==
*/
bool MDNSResponder::stcMDNSServiceTxts::operator==(const stcMDNSServiceTxts& p_Other) const
{

    return compare(p_Other);
}

/*
    MDNSResponder::stcMDNSServiceTxts::operator!=
*/
bool MDNSResponder::stcMDNSServiceTxts::operator!=(const stcMDNSServiceTxts& p_Other) const
{

    return !compare(p_Other);
}


/**
    MDNSResponder::stcMDNS_MsgHeader

    A MDNS message header.

*/

/*
    MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader
*/
MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader(uint16_t p_u16ID /*= 0*/,
        bool p_bQR /*= false*/,
        unsigned char p_ucOpcode /*= 0*/,
        bool p_bAA /*= false*/,
        bool p_bTC /*= false*/,
        bool p_bRD /*= false*/,
        bool p_bRA /*= false*/,
        unsigned char p_ucRCode /*= 0*/,
        uint16_t p_u16QDCount /*= 0*/,
        uint16_t p_u16ANCount /*= 0*/,
        uint16_t p_u16NSCount /*= 0*/,
        uint16_t p_u16ARCount /*= 0*/)
    :   m_u16ID(p_u16ID),
        m_1bQR(p_bQR), m_4bOpcode(p_ucOpcode), m_1bAA(p_bAA), m_1bTC(p_bTC), m_1bRD(p_bRD),
        m_1bRA(p_bRA), m_3bZ(0), m_4bRCode(p_ucRCode),
        m_u16QDCount(p_u16QDCount),
        m_u16ANCount(p_u16ANCount),
        m_u16NSCount(p_u16NSCount),
        m_u16ARCount(p_u16ARCount)
{

}


/**
    MDNSResponder::stcMDNS_RRDomain

    A MDNS domain object.
    The labels of the domain are stored (DNS-like encoded) in 'm_acName':
    [length byte]varlength label[length byte]varlength label[0]
    'm_u16NameLength' stores the used length of 'm_acName'.
    Dynamic label addition is supported.
    Comparison is supported.
    Export as byte array 'esp8266.local' is supported.

*/

/*
    MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain constructor
*/
MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(void)
    :   m_u16NameLength(0)
{

    clear();
}

/*
    MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain copy-constructor
*/
MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other)
    :   m_u16NameLength(0)
{

    operator=(p_Other);
}

/*
    MDNSResponder::stcMDNS_RRDomain::operator =
*/
MDNSResponder::stcMDNS_RRDomain& MDNSResponder::stcMDNS_RRDomain::operator=(const stcMDNS_RRDomain& p_Other)
{

    if (&p_Other != this)
    {
        memcpy(m_acName, p_Other.m_acName, sizeof(m_acName));
        m_u16NameLength = p_Other.m_u16NameLength;
    }
    return *this;
}

/*
    MDNSResponder::stcMDNS_RRDomain::clear
*/
bool MDNSResponder::stcMDNS_RRDomain::clear(void)
{

    memset(m_acName, 0, sizeof(m_acName));
    m_u16NameLength = 0;
    return true;
}

/*
    MDNSResponder::stcMDNS_RRDomain::addLabel
*/
bool MDNSResponder::stcMDNS_RRDomain::addLabel(const char* p_pcLabel,
        bool p_bPrependUnderline /*= false*/)
{

    bool    bResult = false;

    size_t  stLength = (p_pcLabel
                        ? (strlen(p_pcLabel) + (p_bPrependUnderline ? 1 : 0))
                        : 0);
    if ((MDNS_DOMAIN_LABEL_MAXLENGTH >= stLength) &&
            (MDNS_DOMAIN_MAXLENGTH >= (m_u16NameLength + (1 + stLength))))
    {
        // Length byte
        m_acName[m_u16NameLength] = (unsigned char)stLength;    // Might be 0!
        ++m_u16NameLength;
        // Label
        if (stLength)
        {
            if (p_bPrependUnderline)
            {
                m_acName[m_u16NameLength++] = '_';
                --stLength;
            }
            strncpy(&(m_acName[m_u16NameLength]), p_pcLabel, stLength); m_acName[m_u16NameLength + stLength] = 0;
            m_u16NameLength += stLength;
        }
        bResult = true;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNS_RRDomain::compare
*/
bool MDNSResponder::stcMDNS_RRDomain::compare(const stcMDNS_RRDomain& p_Other) const
{

    bool    bResult = false;

    if (m_u16NameLength == p_Other.m_u16NameLength)
    {
        const char* pT = m_acName;
        const char* pO = p_Other.m_acName;
        while ((pT) &&
                (pO) &&
                (*((unsigned char*)pT) == *((unsigned char*)pO)) &&                  // Same length AND
                (0 == strncasecmp((pT + 1), (pO + 1), *((unsigned char*)pT))))     // Same content
        {
            if (*((unsigned char*)pT))              // Not 0
            {
                pT += (1 + * ((unsigned char*)pT)); // Shift by length byte and length
                pO += (1 + * ((unsigned char*)pO));
            }
            else                                    // Is 0 -> Successfully reached the end
            {
                bResult = true;
                break;
            }
        }
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNS_RRDomain::operator ==
*/
bool MDNSResponder::stcMDNS_RRDomain::operator==(const stcMDNS_RRDomain& p_Other) const
{

    return compare(p_Other);
}

/*
    MDNSResponder::stcMDNS_RRDomain::operator !=
*/
bool MDNSResponder::stcMDNS_RRDomain::operator!=(const stcMDNS_RRDomain& p_Other) const
{

    return !compare(p_Other);
}

/*
    MDNSResponder::stcMDNS_RRDomain::operator >
*/
bool MDNSResponder::stcMDNS_RRDomain::operator>(const stcMDNS_RRDomain& p_Other) const
{

    // TODO: Check, if this is a good idea...
    return !compare(p_Other);
}

/*
    MDNSResponder::stcMDNS_RRDomain::c_strLength
*/
size_t MDNSResponder::stcMDNS_RRDomain::c_strLength(void) const
{

    size_t          stLength = 0;

    unsigned char*  pucLabelLength = (unsigned char*)m_acName;
    while (*pucLabelLength)
    {
        stLength += (*pucLabelLength + 1 /* +1 for '.' or '\0'*/);
        pucLabelLength += (*pucLabelLength + 1);
    }
    return stLength;
}

/*
    MDNSResponder::stcMDNS_RRDomain::c_str
*/
bool MDNSResponder::stcMDNS_RRDomain::c_str(char* p_pcBuffer)
{

    bool bResult = false;

    if (p_pcBuffer)
    {
        *p_pcBuffer = 0;
        unsigned char* pucLabelLength = (unsigned char*)m_acName;
        while (*pucLabelLength)
        {
            memcpy(p_pcBuffer, (const char*)(pucLabelLength + 1), *pucLabelLength);
            p_pcBuffer += *pucLabelLength;
            pucLabelLength += (*pucLabelLength + 1);
            *p_pcBuffer++ = (*pucLabelLength ? '.' : '\0');
        }
        bResult = true;
    }
    return bResult;
}


/**
    MDNSResponder::stcMDNS_RRAttributes

    A MDNS attributes object.

*/

/*
    MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes constructor
*/
MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(uint16_t p_u16Type /*= 0*/,
        uint16_t p_u16Class /*= 1 DNS_RRCLASS_IN Internet*/)
    :   m_u16Type(p_u16Type),
        m_u16Class(p_u16Class)
{

}

/*
    MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes copy-constructor
*/
MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Other)
{

    operator=(p_Other);
}

/*
    MDNSResponder::stcMDNS_RRAttributes::operator =
*/
MDNSResponder::stcMDNS_RRAttributes& MDNSResponder::stcMDNS_RRAttributes::operator=(const MDNSResponder::stcMDNS_RRAttributes& p_Other)
{

    if (&p_Other != this)
    {
        m_u16Type = p_Other.m_u16Type;
        m_u16Class = p_Other.m_u16Class;
    }
    return *this;
}


/**
    MDNSResponder::stcMDNS_RRHeader

    A MDNS record header (domain and attributes) object.

*/

/*
    MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader constructor
*/
MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(void)
{

}

/*
    MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader copy-constructor
*/
MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other)
{

    operator=(p_Other);
}

/*
    MDNSResponder::stcMDNS_RRHeader::operator =
*/
MDNSResponder::stcMDNS_RRHeader& MDNSResponder::stcMDNS_RRHeader::operator=(const MDNSResponder::stcMDNS_RRHeader& p_Other)
{

    if (&p_Other != this)
    {
        m_Domain = p_Other.m_Domain;
        m_Attributes = p_Other.m_Attributes;
    }
    return *this;
}

/*
    MDNSResponder::stcMDNS_RRHeader::clear
*/
bool MDNSResponder::stcMDNS_RRHeader::clear(void)
{

    m_Domain.clear();
    return true;
}


/**
    MDNSResponder::stcMDNS_RRQuestion

    A MDNS question record object (header + question flags)

*/

/*
    MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion constructor
*/
MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion(void)
    :   m_pNext(0),
        m_bUnicast(false)
{

}


/**
    MDNSResponder::stcMDNS_RRAnswer

    A MDNS answer record object (header + answer content).
    This is a 'virtual' base class for all other MDNS answer classes.

*/

/*
    MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer constructor
*/
MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer(enuAnswerType p_AnswerType,
        const MDNSResponder::stcMDNS_RRHeader& p_Header,
        uint32_t p_u32TTL)
    :   m_pNext(0),
        m_AnswerType(p_AnswerType),
        m_Header(p_Header),
        m_u32TTL(p_u32TTL)
{

    // Extract 'cache flush'-bit
    m_bCacheFlush = (m_Header.m_Attributes.m_u16Class & 0x8000);
    m_Header.m_Attributes.m_u16Class &= (~0x8000);
}

/*
    MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer destructor
*/
MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer(void)
{

}

/*
    MDNSResponder::stcMDNS_RRAnswer::answerType
*/
MDNSResponder::enuAnswerType MDNSResponder::stcMDNS_RRAnswer::answerType(void) const
{

    return m_AnswerType;
}

/*
    MDNSResponder::stcMDNS_RRAnswer::clear
*/
bool MDNSResponder::stcMDNS_RRAnswer::clear(void)
{

    m_pNext = 0;
    m_Header.clear();
    return true;
}


/**
    MDNSResponder::stcMDNS_RRAnswerA

    A MDNS A answer object.
    Extends the base class by an IP4 address member.

*/

#ifdef MDNS_IP4_SUPPORT
/*
    MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA constructor
*/
MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA(const MDNSResponder::stcMDNS_RRHeader& p_Header,
        uint32_t p_u32TTL)
    :   stcMDNS_RRAnswer(AnswerType_A, p_Header, p_u32TTL),
        m_IPAddress(0, 0, 0, 0)
{

}

/*
    MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA destructor
*/
MDNSResponder::stcMDNS_RRAnswerA::~stcMDNS_RRAnswerA(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNS_RRAnswerA::clear
*/
bool MDNSResponder::stcMDNS_RRAnswerA::clear(void)
{

    m_IPAddress = IPAddress(0, 0, 0, 0);
    return true;
}
#endif


/**
    MDNSResponder::stcMDNS_RRAnswerPTR

    A MDNS PTR answer object.
    Extends the base class by a MDNS domain member.

*/

/*
    MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR constructor
*/
MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR(const MDNSResponder::stcMDNS_RRHeader& p_Header,
        uint32_t p_u32TTL)
    :   stcMDNS_RRAnswer(AnswerType_PTR, p_Header, p_u32TTL)
{

}

/*
    MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR destructor
*/
MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNS_RRAnswerPTR::clear
*/
bool MDNSResponder::stcMDNS_RRAnswerPTR::clear(void)
{

    m_PTRDomain.clear();
    return true;
}


/**
    MDNSResponder::stcMDNS_RRAnswerTXT

    A MDNS TXT answer object.
    Extends the base class by a MDNS TXT items list member.

*/

/*
    MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT constructor
*/
MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT(const MDNSResponder::stcMDNS_RRHeader& p_Header,
        uint32_t p_u32TTL)
    :   stcMDNS_RRAnswer(AnswerType_TXT, p_Header, p_u32TTL)
{

}

/*
    MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT destructor
*/
MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNS_RRAnswerTXT::clear
*/
bool MDNSResponder::stcMDNS_RRAnswerTXT::clear(void)
{

    m_Txts.clear();
    return true;
}


/**
    MDNSResponder::stcMDNS_RRAnswerAAAA

    A MDNS AAAA answer object.
    (Should) extend the base class by an IP6 address member.

*/

#ifdef MDNS_IP6_SUPPORT
/*
    MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA constructor
*/
MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA(const MDNSResponder::stcMDNS_RRHeader& p_Header,
        uint32_t p_u32TTL)
    :   stcMDNS_RRAnswer(AnswerType_AAAA, p_Header, p_u32TTL)
{

}

/*
    MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA destructor
*/
MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNS_RRAnswerAAAA::clear
*/
bool MDNSResponder::stcMDNS_RRAnswerAAAA::clear(void)
{

    return true;
}
#endif


/**
    MDNSResponder::stcMDNS_RRAnswerSRV

    A MDNS SRV answer object.
    Extends the base class by a port member.

*/

/*
    MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV constructor
*/
MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV(const MDNSResponder::stcMDNS_RRHeader& p_Header,
        uint32_t p_u32TTL)
    :   stcMDNS_RRAnswer(AnswerType_SRV, p_Header, p_u32TTL),
        m_u16Priority(0),
        m_u16Weight(0),
        m_u16Port(0)
{

}

/*
    MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV destructor
*/
MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNS_RRAnswerSRV::clear
*/
bool MDNSResponder::stcMDNS_RRAnswerSRV::clear(void)
{

    m_u16Priority = 0;
    m_u16Weight = 0;
    m_u16Port = 0;
    m_SRVDomain.clear();
    return true;
}


/**
    MDNSResponder::stcMDNS_RRAnswerGeneric

    An unknown (generic) MDNS answer object.
    Extends the base class by a RDATA buffer member.

*/

/*
    MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric constructor
*/
MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header,
        uint32_t p_u32TTL)
    :   stcMDNS_RRAnswer(AnswerType_Generic, p_Header, p_u32TTL),
        m_u16RDLength(0),
        m_pu8RDData(0)
{

}

/*
    MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric destructor
*/
MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNS_RRAnswerGeneric::clear
*/
bool MDNSResponder::stcMDNS_RRAnswerGeneric::clear(void)
{

    if (m_pu8RDData)
    {
        delete[] m_pu8RDData;
        m_pu8RDData = 0;
    }
    m_u16RDLength = 0;

    return true;
}


/**
    MDNSResponder::stcProbeInformation

    Probing status information for a host or service domain

*/

/*
    MDNSResponder::stcProbeInformation::stcProbeInformation constructor
*/
MDNSResponder::stcProbeInformation::stcProbeInformation(void)
    :   m_ProbingStatus(ProbingStatus_WaitingForData),
        m_u8SentCount(0),
        m_Timeout(esp8266::polledTimeout::oneShotMs::neverExpires),
        m_bConflict(false),
        m_bTiebreakNeeded(false),
        m_fnHostProbeResultCallback(0),
        m_fnServiceProbeResultCallback(0)
{
}

/*
    MDNSResponder::stcProbeInformation::clear
*/
bool MDNSResponder::stcProbeInformation::clear(bool p_bClearUserdata /*= false*/)
{

    m_ProbingStatus = ProbingStatus_WaitingForData;
    m_u8SentCount = 0;
    m_Timeout.resetToNeverExpires();
    m_bConflict = false;
    m_bTiebreakNeeded = false;
    if (p_bClearUserdata)
    {
        m_fnHostProbeResultCallback = 0;
        m_fnServiceProbeResultCallback = 0;
    }
    return true;
}

/**
    MDNSResponder::stcMDNSService

    A MDNS service object (to be announced by the MDNS responder)
    The service instance may be '\0'; in this case the hostname is used
    and the flag m_bAutoName is set. If the hostname changes, all 'auto-
    named' services are renamed also.
    m_u8Replymask is used while preparing a response to a MDNS query. It is
    reset in '_sendMDNSMessage' afterwards.
*/

/*
    MDNSResponder::stcMDNSService::stcMDNSService constructor
*/
MDNSResponder::stcMDNSService::stcMDNSService(const char* p_pcName /*= 0*/,
        const char* p_pcService /*= 0*/,
        const char* p_pcProtocol /*= 0*/)
    :   m_pNext(0),
        m_pcName(0),
        m_bAutoName(false),
        m_pcService(0),
        m_pcProtocol(0),
        m_u16Port(0),
        m_u8ReplyMask(0),
        m_fnTxtCallback(0)
{

    setName(p_pcName);
    setService(p_pcService);
    setProtocol(p_pcProtocol);
}

/*
    MDNSResponder::stcMDNSService::~stcMDNSService destructor
*/
MDNSResponder::stcMDNSService::~stcMDNSService(void)
{

    releaseName();
    releaseService();
    releaseProtocol();
}

/*
    MDNSResponder::stcMDNSService::setName
*/
bool MDNSResponder::stcMDNSService::setName(const char* p_pcName)
{

    bool bResult = false;

    releaseName();
    size_t stLength = (p_pcName ? strlen(p_pcName) : 0);
    if (stLength)
    {
        if ((bResult = (0 != (m_pcName = new char[stLength + 1]))))
        {
            strncpy(m_pcName, p_pcName, stLength);
            m_pcName[stLength] = 0;
        }
    }
    else
    {
        bResult = true;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSService::releaseName
*/
bool MDNSResponder::stcMDNSService::releaseName(void)
{

    if (m_pcName)
    {
        delete[] m_pcName;
        m_pcName = 0;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSService::setService
*/
bool MDNSResponder::stcMDNSService::setService(const char* p_pcService)
{

    bool bResult = false;

    releaseService();
    size_t stLength = (p_pcService ? strlen(p_pcService) : 0);
    if (stLength)
    {
        if ((bResult = (0 != (m_pcService = new char[stLength + 1]))))
        {
            strncpy(m_pcService, p_pcService, stLength);
            m_pcService[stLength] = 0;
        }
    }
    else
    {
        bResult = true;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSService::releaseService
*/
bool MDNSResponder::stcMDNSService::releaseService(void)
{

    if (m_pcService)
    {
        delete[] m_pcService;
        m_pcService = 0;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSService::setProtocol
*/
bool MDNSResponder::stcMDNSService::setProtocol(const char* p_pcProtocol)
{

    bool bResult = false;

    releaseProtocol();
    size_t stLength = (p_pcProtocol ? strlen(p_pcProtocol) : 0);
    if (stLength)
    {
        if ((bResult = (0 != (m_pcProtocol = new char[stLength + 1]))))
        {
            strncpy(m_pcProtocol, p_pcProtocol, stLength);
            m_pcProtocol[stLength] = 0;
        }
    }
    else
    {
        bResult = true;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSService::releaseProtocol
*/
bool MDNSResponder::stcMDNSService::releaseProtocol(void)
{

    if (m_pcProtocol)
    {
        delete[] m_pcProtocol;
        m_pcProtocol = 0;
    }
    return true;
}


/**
    MDNSResponder::stcMDNSServiceQuery

    A MDNS service query object.
    Service queries may be static or dynamic.
    As the static service query is processed in the blocking function 'queryService',
    only one static service service may exist. The processing of the answers is done
    on the WiFi-stack side of the ESP stack structure (via 'UDPContext.onRx(_update)').

*/

/**
    MDNSResponder::stcMDNSServiceQuery::stcAnswer

    One answer for a service query.
    Every answer must contain
    - a service instance entry (pivot),
    and may contain
    - a host domain,
    - a port
    - an IP4 address
    (- an IP6 address)
    - a MDNS TXTs
    The existence of a component is flagged in 'm_u32ContentFlags'.
    For every answer component a TTL value is maintained.
    Answer objects can be connected to a linked list.

    For the host domain, service domain and TXTs components, a char array
    representation can be retrieved (which is created on demand).

*/

/**
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL

    The TTL (Time-To-Live) for an specific answer content.
    The 80% and outdated states are calculated based on the current time (millis)
    and the 'set' time (also millis).
    If the answer is scheduled for an update, the corresponding flag should be set.

    /

    / *
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor
    /
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(uint32_t p_u32TTL / *= 0* /)
    :   m_bUpdateScheduled(false) {

    set(p_u32TTL * 1000);
    }

    / *
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set
    /
    bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) {

    m_TTLTimeFlag.restart(p_u32TTL * 1000);
    m_bUpdateScheduled = false;

    return true;
    }

    / *
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent
    /
    bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent(void) const {

    return ((m_TTLTimeFlag.getTimeout()) &&
            (!m_bUpdateScheduled) &&
            (m_TTLTimeFlag.hypotheticalTimeout((m_TTLTimeFlag.getTimeout() * 800) / 1000)));
    }

    / *
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated
    /
    bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated(void) const {

    return ((m_TTLTimeFlag.getTimeout()) &&
            (m_TTLTimeFlag.flagged()));
    }*/


/**
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL

    The TTL (Time-To-Live) for an specific answer content.
    The 80% and outdated states are calculated based on the current time (millis)
    and the 'set' time (also millis).
    If the answer is scheduled for an update, the corresponding flag should be set.

*/

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void)
    :   m_u32TTL(0),
        m_TTLTimeout(esp8266::polledTimeout::oneShotMs::neverExpires),
        m_timeoutLevel(TIMEOUTLEVEL_UNSET)
{

}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL)
{

    m_u32TTL = p_u32TTL;
    if (m_u32TTL)
    {
        m_timeoutLevel = TIMEOUTLEVEL_BASE;             // Set to 80%
        m_TTLTimeout.reset(timeout());
    }
    else
    {
        m_timeoutLevel = TIMEOUTLEVEL_UNSET;            // undef
        m_TTLTimeout.resetToNeverExpires();
    }
    return true;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void)
{

    return ((m_u32TTL) &&
            (TIMEOUTLEVEL_UNSET != m_timeoutLevel) &&
            (m_TTLTimeout.expired()));
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart(void)
{

    bool    bResult = true;

    if ((TIMEOUTLEVEL_BASE <= m_timeoutLevel) &&    // >= 80% AND
            (TIMEOUTLEVEL_FINAL > m_timeoutLevel))      // < 100%
    {

        m_timeoutLevel += TIMEOUTLEVEL_INTERVAL;    // increment by 5%
        m_TTLTimeout.reset(timeout());
    }
    else
    {
        bResult = false;
        m_TTLTimeout.resetToNeverExpires();
        m_timeoutLevel = TIMEOUTLEVEL_UNSET;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion(void)
{

    m_timeoutLevel = TIMEOUTLEVEL_FINAL;
    m_TTLTimeout.reset(1 * 1000);   // See RFC 6762, 10.1

    return true;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel(void) const
{

    return (TIMEOUTLEVEL_FINAL == m_timeoutLevel);
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout
*/
unsigned long MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const
{

    uint32_t    u32Timeout = esp8266::polledTimeout::oneShotMs::neverExpires;

    if (TIMEOUTLEVEL_BASE == m_timeoutLevel)            // 80%
    {
        u32Timeout = (m_u32TTL * 800);                  // to milliseconds
    }
    else if ((TIMEOUTLEVEL_BASE < m_timeoutLevel) &&    // >80% AND
             (TIMEOUTLEVEL_FINAL >= m_timeoutLevel))    // <= 100%
    {

        u32Timeout = (m_u32TTL * 50);
    }   // else: invalid
    return u32Timeout;
}


#ifdef MDNS_IP4_SUPPORT
/**
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address

*/

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address constructor
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address(IPAddress p_IPAddress,
        uint32_t p_u32TTL /*= 0*/)
    :   m_pNext(0),
        m_IPAddress(p_IPAddress)
{

    m_TTL.set(p_u32TTL);
}
#endif


/**
    MDNSResponder::stcMDNSServiceQuery::stcAnswer
*/

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer constructor
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer(void)
    :   m_pNext(0),
        m_pcServiceDomain(0),
        m_pcHostDomain(0),
        m_u16Port(0),
        m_pcTxts(0),
#ifdef MDNS_IP4_SUPPORT
        m_pIP4Addresses(0),
#endif
#ifdef MDNS_IP6_SUPPORT
        m_pIP6Addresses(0),
#endif
        m_u32ContentFlags(0)
{
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer destructor
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear(void)
{

    return ((releaseTxts()) &&
#ifdef MDNS_IP4_SUPPORT
            (releaseIP4Addresses()) &&
#endif
#ifdef MDNS_IP6_SUPPORT
            (releaseIP6Addresses())
#endif
            (releaseHostDomain()) &&
            (releaseServiceDomain()));
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain

    Alloc memory for the char array representation of the service domain.

*/
char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain(size_t p_stLength)
{

    releaseServiceDomain();
    if (p_stLength)
    {
        m_pcServiceDomain = new char[p_stLength];
    }
    return m_pcServiceDomain;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain(void)
{

    if (m_pcServiceDomain)
    {
        delete[] m_pcServiceDomain;
        m_pcServiceDomain = 0;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain

    Alloc memory for the char array representation of the host domain.

*/
char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain(size_t p_stLength)
{

    releaseHostDomain();
    if (p_stLength)
    {
        m_pcHostDomain = new char[p_stLength];
    }
    return m_pcHostDomain;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain(void)
{

    if (m_pcHostDomain)
    {
        delete[] m_pcHostDomain;
        m_pcHostDomain = 0;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts

    Alloc memory for the char array representation of the TXT items.

*/
char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts(size_t p_stLength)
{

    releaseTxts();
    if (p_stLength)
    {
        m_pcTxts = new char[p_stLength];
    }
    return m_pcTxts;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts(void)
{

    if (m_pcTxts)
    {
        delete[] m_pcTxts;
        m_pcTxts = 0;
    }
    return true;
}

#ifdef MDNS_IP4_SUPPORT
/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses(void)
{

    while (m_pIP4Addresses)
    {
        stcIP4Address*  pNext = m_pIP4Addresses->m_pNext;
        delete m_pIP4Addresses;
        m_pIP4Addresses = pNext;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address)
{

    bool bResult = false;

    if (p_pIP4Address)
    {
        p_pIP4Address->m_pNext = m_pIP4Addresses;
        m_pIP4Addresses = p_pIP4Address;
        bResult = true;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address)
{

    bool    bResult = false;

    if (p_pIP4Address)
    {
        stcIP4Address*  pPred = m_pIP4Addresses;
        while ((pPred) &&
                (pPred->m_pNext != p_pIP4Address))
        {
            pPred = pPred->m_pNext;
        }
        if (pPred)
        {
            pPred->m_pNext = p_pIP4Address->m_pNext;
            delete p_pIP4Address;
            bResult = true;
        }
        else if (m_pIP4Addresses == p_pIP4Address)     // No predecessor, but first item
        {
            m_pIP4Addresses = p_pIP4Address->m_pNext;
            delete p_pIP4Address;
            bResult = true;
        }
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address (const)
*/
const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) const
{

    return (stcIP4Address*)(((const stcAnswer*)this)->findIP4Address(p_IPAddress));
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress)
{

    stcIP4Address*  pIP4Address = m_pIP4Addresses;
    while (pIP4Address)
    {
        if (pIP4Address->m_IPAddress == p_IPAddress)
        {
            break;
        }
        pIP4Address = pIP4Address->m_pNext;
    }
    return pIP4Address;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount
*/
uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount(void) const
{

    uint32_t    u32Count = 0;

    stcIP4Address*  pIP4Address = m_pIP4Addresses;
    while (pIP4Address)
    {
        ++u32Count;
        pIP4Address = pIP4Address->m_pNext;
    }
    return u32Count;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index)
{

    return (stcIP4Address*)(((const stcAnswer*)this)->IP4AddressAtIndex(p_u32Index));
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex (const)
*/
const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) const
{

    const stcIP4Address*    pIP4Address = 0;

    if (((uint32_t)(-1) != p_u32Index) &&
            (m_pIP4Addresses))
    {

        uint32_t    u32Index;
        for (pIP4Address = m_pIP4Addresses, u32Index = 0; ((pIP4Address) && (u32Index < p_u32Index)); pIP4Address = pIP4Address->m_pNext, ++u32Index);
    }
    return pIP4Address;
}
#endif

#ifdef MDNS_IP6_SUPPORT
/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses(void)
{

    while (m_pIP6Addresses)
    {
        stcIP6Address*  pNext = m_pIP6Addresses->m_pNext;
        delete m_pIP6Addresses;
        m_pIP6Addresses = pNext;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address)
{

    bool bResult = false;

    if (p_pIP6Address)
    {
        p_pIP6Address->m_pNext = m_pIP6Addresses;
        m_pIP6Addresses = p_pIP6Address;
        bResult = true;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address
*/
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address)
{

    bool    bResult = false;

    if (p_pIP6Address)
    {
        stcIP6Address*  pPred = m_pIP6Addresses;
        while ((pPred) &&
                (pPred->m_pNext != p_pIP6Address))
        {
            pPred = pPred->m_pNext;
        }
        if (pPred)
        {
            pPred->m_pNext = p_pIP6Address->m_pNext;
            delete p_pIP6Address;
            bResult = true;
        }
        else if (m_pIP6Addresses == p_pIP6Address)     // No predecessor, but first item
        {
            m_pIP6Addresses = p_pIP6Address->m_pNext;
            delete p_pIP6Address;
            bResult = true;
        }
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IP6Address& p_IPAddress)
{

    return (stcIP6Address*)(((const stcAnswer*)this)->findIP6Address(p_IPAddress));
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address (const)
*/
const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IPAddress& p_IPAddress) const
{

    const stcIP6Address*    pIP6Address = m_pIP6Addresses;
    while (pIP6Address)
    {
        if (p_IP6Address->m_IPAddress == p_IPAddress)
        {
            break;
        }
        pIP6Address = pIP6Address->m_pNext;
    }
    return pIP6Address;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount
*/
uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount(void) const
{

    uint32_t    u32Count = 0;

    stcIP6Address*  pIP6Address = m_pIP6Addresses;
    while (pIP6Address)
    {
        ++u32Count;
        pIP6Address = pIP6Address->m_pNext;
    }
    return u32Count;
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex (const)
*/
const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) const
{

    return (stcIP6Address*)(((const stcAnswer*)this)->IP6AddressAtIndex(p_u32Index));
}

/*
    MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index)
{

    stcIP6Address*    pIP6Address = 0;

    if (((uint32_t)(-1) != p_u32Index) &&
            (m_pIP6Addresses))
    {

        uint32_t    u32Index;
        for (pIP6Address = m_pIP6Addresses, u32Index = 0; ((pIP6Address) && (u32Index < p_u32Index)); pIP6Address = pIP6Address->m_pNext, ++u32Index);
    }
    return pIP6Address;
}
#endif


/**
    MDNSResponder::stcMDNSServiceQuery

    A service query object.
    A static query is flagged via 'm_bLegacyQuery'; while the function 'queryService'
    is waiting for answers, the internal flag 'm_bAwaitingAnswers' is set. When the
    timeout is reached, the flag is removed. These two flags are only used for static
    service queries.
    All answers to the service query are stored in 'm_pAnswers' list.
    Individual answers may be addressed by index (in the list of answers).
    Every time a answer component is added (or changes) in a dynamic service query,
    the callback 'm_fnCallback' is called.
    The answer list may be searched by service and host domain.

    Service query object may be connected to a linked list.
*/

/*
    MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery constructor
*/
MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery(void)
    :   m_pNext(0),
        m_fnCallback(0),
        m_bLegacyQuery(false),
        m_u8SentCount(0),
        m_ResendTimeout(esp8266::polledTimeout::oneShotMs::neverExpires),
        m_bAwaitingAnswers(true),
        m_pAnswers(0)
{

    clear();
}

/*
    MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery destructor
*/
MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNSServiceQuery::clear
*/
bool MDNSResponder::stcMDNSServiceQuery::clear(void)
{

    m_fnCallback = 0;
    m_bLegacyQuery = false;
    m_u8SentCount = 0;
    m_ResendTimeout.resetToNeverExpires();
    m_bAwaitingAnswers = true;
    while (m_pAnswers)
    {
        stcAnswer*  pNext = m_pAnswers->m_pNext;
        delete m_pAnswers;
        m_pAnswers = pNext;
    }
    return true;
}

/*
    MDNSResponder::stcMDNSServiceQuery::answerCount
*/
uint32_t MDNSResponder::stcMDNSServiceQuery::answerCount(void) const
{

    uint32_t    u32Count = 0;

    stcAnswer*  pAnswer = m_pAnswers;
    while (pAnswer)
    {
        ++u32Count;
        pAnswer = pAnswer->m_pNext;
    }
    return u32Count;
}

/*
    MDNSResponder::stcMDNSServiceQuery::answerAtIndex
*/
const MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) const
{

    const stcAnswer*    pAnswer = 0;

    if (((uint32_t)(-1) != p_u32Index) &&
            (m_pAnswers))
    {

        uint32_t    u32Index;
        for (pAnswer = m_pAnswers, u32Index = 0; ((pAnswer) && (u32Index < p_u32Index)); pAnswer = pAnswer->m_pNext, ++u32Index);
    }
    return pAnswer;
}

/*
    MDNSResponder::stcMDNSServiceQuery::answerAtIndex
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index)
{

    return (stcAnswer*)(((const stcMDNSServiceQuery*)this)->answerAtIndex(p_u32Index));
}

/*
    MDNSResponder::stcMDNSServiceQuery::indexOfAnswer
*/
uint32_t MDNSResponder::stcMDNSServiceQuery::indexOfAnswer(const MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) const
{

    uint32_t    u32Index = 0;

    for (const stcAnswer* pAnswer = m_pAnswers; pAnswer; pAnswer = pAnswer->m_pNext, ++u32Index)
    {
        if (pAnswer == p_pAnswer)
        {
            return u32Index;
        }
    }
    return ((uint32_t)(-1));
}

/*
    MDNSResponder::stcMDNSServiceQuery::addAnswer
*/
bool MDNSResponder::stcMDNSServiceQuery::addAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer)
{

    bool    bResult = false;

    if (p_pAnswer)
    {
        p_pAnswer->m_pNext = m_pAnswers;
        m_pAnswers = p_pAnswer;
        bResult = true;
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceQuery::removeAnswer
*/
bool MDNSResponder::stcMDNSServiceQuery::removeAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer)
{

    bool    bResult = false;

    if (p_pAnswer)
    {
        stcAnswer*  pPred = m_pAnswers;
        while ((pPred) &&
                (pPred->m_pNext != p_pAnswer))
        {
            pPred = pPred->m_pNext;
        }
        if (pPred)
        {
            pPred->m_pNext = p_pAnswer->m_pNext;
            delete p_pAnswer;
            bResult = true;
        }
        else if (m_pAnswers == p_pAnswer)   // No predecessor, but first item
        {
            m_pAnswers = p_pAnswer->m_pNext;
            delete p_pAnswer;
            bResult = true;
        }
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain(const MDNSResponder::stcMDNS_RRDomain& p_ServiceDomain)
{

    stcAnswer*  pAnswer = m_pAnswers;
    while (pAnswer)
    {
        if (pAnswer->m_ServiceDomain == p_ServiceDomain)
        {
            break;
        }
        pAnswer = pAnswer->m_pNext;
    }
    return pAnswer;
}

/*
    MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain
*/
MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain(const MDNSResponder::stcMDNS_RRDomain& p_HostDomain)
{

    stcAnswer*  pAnswer = m_pAnswers;
    while (pAnswer)
    {
        if (pAnswer->m_HostDomain == p_HostDomain)
        {
            break;
        }
        pAnswer = pAnswer->m_pNext;
    }
    return pAnswer;
}


/**
    MDNSResponder::stcMDNSSendParameter

    A 'collection' of properties and flags for one MDNS query or response.
    Mainly managed by the 'Control' functions.
    The current offset in the UPD output buffer is tracked to be able to do
    a simple host or service domain compression.

*/

/**
    MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem

    A cached host or service domain, incl. the offset in the UDP output buffer.

*/

/*
    MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem constructor
*/
MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem(const void* p_pHostnameOrService,
        bool p_bAdditionalData,
        uint32_t p_u16Offset)
    :   m_pNext(0),
        m_pHostnameOrService(p_pHostnameOrService),
        m_bAdditionalData(p_bAdditionalData),
        m_u16Offset(p_u16Offset)
{

}

/**
    MDNSResponder::stcMDNSSendParameter
*/

/*
    MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter constructor
*/
MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter(void)
    :   m_pQuestions(0),
        m_pDomainCacheItems(0)
{

    clear();
}

/*
    MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter destructor
*/
MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter(void)
{

    clear();
}

/*
    MDNSResponder::stcMDNSSendParameter::clear
*/
bool MDNSResponder::stcMDNSSendParameter::clear(void)
{

    m_u16ID = 0;
    m_u8HostReplyMask = 0;
    m_u16Offset = 0;

    m_bLegacyQuery = false;
    m_bResponse = false;
    m_bAuthorative = false;
    m_bUnicast = false;
    m_bUnannounce = false;

    m_bCacheFlush = true;

    while (m_pQuestions)
    {
        stcMDNS_RRQuestion* pNext = m_pQuestions->m_pNext;
        delete m_pQuestions;
        m_pQuestions = pNext;
    }

    return clearCachedNames();;
}
/*
    MDNSResponder::stcMDNSSendParameter::clear cached names
*/
bool MDNSResponder::stcMDNSSendParameter::clearCachedNames(void)
{

    m_u16Offset = 0;

    while (m_pDomainCacheItems)
    {
        stcDomainCacheItem* pNext = m_pDomainCacheItems->m_pNext;
        delete m_pDomainCacheItems;
        m_pDomainCacheItems = pNext;
    }
    m_pDomainCacheItems = nullptr;

    return true;
}

/*
    MDNSResponder::stcMDNSSendParameter::shiftOffset
*/
bool MDNSResponder::stcMDNSSendParameter::shiftOffset(uint16_t p_u16Shift)
{

    m_u16Offset += p_u16Shift;
    return true;
}

/*
    MDNSResponder::stcMDNSSendParameter::addDomainCacheItem
*/
bool MDNSResponder::stcMDNSSendParameter::addDomainCacheItem(const void* p_pHostnameOrService,
        bool p_bAdditionalData,
        uint16_t p_u16Offset)
{

    bool    bResult = false;

    stcDomainCacheItem* pNewItem = 0;
    if ((p_pHostnameOrService) &&
            (p_u16Offset) &&
            ((pNewItem = new stcDomainCacheItem(p_pHostnameOrService, p_bAdditionalData, p_u16Offset))))
    {

        pNewItem->m_pNext = m_pDomainCacheItems;
        bResult = ((m_pDomainCacheItems = pNewItem));
    }
    return bResult;
}

/*
    MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset
*/
uint16_t MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset(const void* p_pHostnameOrService,
        bool p_bAdditionalData) const
{

    const stcDomainCacheItem*   pCacheItem = m_pDomainCacheItems;

    for (; pCacheItem; pCacheItem = pCacheItem->m_pNext)
    {
        if ((pCacheItem->m_pHostnameOrService == p_pHostnameOrService) &&
                (pCacheItem->m_bAdditionalData == p_bAdditionalData))   // Found cache item
        {
            break;
        }
    }
    return (pCacheItem ? pCacheItem->m_u16Offset : 0);
}

}   // namespace MDNSImplementation

} // namespace esp8266



