1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-09-06 19:08:12 +03:00
Files
esp8266/libraries/ESP8266mDNS/src/LEAmDNS2Host_Transfer.cpp

2452 lines
110 KiB
C++

/*
LEAmDNS2Host_Transfer.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 <coredecls.h> // for can_yield()
#include "ESP8266mDNS.h"
#include "LEAmDNS2Host.h"
#include "LEAmDNS2_Priv.h"
namespace esp8266
{
namespace experimental
{
/*
SENDING
*/
/*
MDNSResponder::_sendMessage
Unicast responses are prepared and sent directly to the querier.
Multicast responses or queries are transferred to _sendMessage_Multicast
Any reply flags in installed services are removed at the end!
*/
bool clsLEAMDNSHost::_sendMessage(clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
bool bResult = true;
for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next)
if (netif_is_up(pNetIf))
{
bResult = bResult && _sendMessage(pNetIf, p_rSendParameter);
}
// Finally clear service reply masks
for (clsService* pService : m_Services)
{
pService->m_u32ReplyMask = 0;
}
return bResult;
}
bool clsLEAMDNSHost::_sendMessage(netif* pNetIf, clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
bool bResult = false;
uint8_t u8AvailableProtocols = 0;
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage: if=" NETIFID_STR "\n"), _DH(), NETIFID_VAL(pNetIf)));
#ifdef MDNS_IPV4_SUPPORT
// Only send out IPv4 messages, if we've got an IPv4 address
if (_getResponderIPAddress(pNetIf, enuIPProtocolType::V4).isSet())
{
u8AvailableProtocols |= static_cast<uint8_t>(enuIPProtocolType::V4);
}
DEBUG_EX_INFO(else
{
DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage: No IPv4 address available!\n"), _DH());
});
#endif
#ifdef MDNS2_IPV6_SUPPORT
// Only send out IPv6 messages, if we've got an IPv6 address
if (_getResponderIPAddress(pNetIf, enuIPProtocolType::V6).isSet())
{
u8AvailableProtocols |= static_cast<uint8_t>(enuIPProtocolType::V6);
}
DEBUG_EX_INFO(else
{
DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage: No IPv6 address available!\n"), _DH());
});
#endif
if (clsBackbone::sm_pBackbone->setDelayUDPProcessing(true))
{
// Avoid 're-entry-like problems because delay() is called!
if (clsSendParameter::enuResponseType::None != p_rSendParameter.m_Response)
{
IPAddress ipRemote = ((clsSendParameter::enuResponseType::Response == p_rSendParameter.m_Response)
? m_pUDPContext->getRemoteAddress()
: IPAddress());
if (p_rSendParameter.m_bUnicast)
{
// Unicast response -> Send to querier
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage: Will send unicast to '%s'.\n"), _DH(), ipRemote.toString().c_str()););
DEBUG_EX_ERR(if (!ipRemote.isSet()) DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage: MISSING remote address for unicast response!\n"), _DH()););
bResult = ((ipRemote.isSet()) &&
(_prepareMessage(pNetIf, p_rSendParameter)) &&
(m_pUDPContext->sendTimeout(ipRemote, m_pUDPContext->getRemotePort(), clsConsts::u32SendTimeoutMs)) /*&&
(Serial.println("Did send UC"), true)*/);
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage (V4): FAILED!\n"), _DH()););
}
else
{
// Multicast response -> Send via the same network interface, that received the query
#ifdef MDNS_IPV4_SUPPORT
if (((!ipRemote.isSet()) || // NO remote IP
(ipRemote.isV4())) && // OR IPv4
(u8AvailableProtocols & static_cast<uint8_t>(enuIPProtocolType::V4))) // AND IPv4 protocol available
{
bResult = _sendMessage_Multicast(pNetIf, p_rSendParameter, static_cast<uint8_t>(enuIPProtocolType::V4));
}
#endif
#ifdef MDNS2_IPV6_SUPPORT
if (((!ipRemote.isSet()) || // NO remote IP
(ipRemote.isV6())) && // OR IPv6
(u8AvailableProtocols & static_cast<uint8_t>(enuIPProtocolType::V6))) // AND IPv6 protocol available
{
bResult = _sendMessage_Multicast(pNetIf, p_rSendParameter, static_cast<uint8_t>(enuIPProtocolType::V6));
}
#endif
}
}
else
{
// Multicast query -> Send by all available protocols
bResult = ((u8AvailableProtocols) &&
(_sendMessage_Multicast(pNetIf, p_rSendParameter, u8AvailableProtocols)));
}
clsBackbone::sm_pBackbone->setDelayUDPProcessing(false);
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_sendMessage_Multicast
Fills the UDP output buffer (via _prepareMessage) and sends the buffer
via the selected WiFi protocols
*/
bool clsLEAMDNSHost::_sendMessage_Multicast(netif* pNetIf, clsLEAMDNSHost::clsSendParameter& p_rSendParameter,
uint8_t p_IPProtocolTypes)
{
bool bIPv4Result = true;
bool bIPv6Result = true;
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage_Multicast: if=" NETIFID_STR "\n"), _DH(), NETIFID_VAL(pNetIf)));
#ifdef MDNS_IPV4_SUPPORT
if (p_IPProtocolTypes & static_cast<uint8_t>(enuIPProtocolType::V4))
{
IPAddress ip4MulticastAddress(DNS_MQUERY_IPV4_GROUP_INIT);
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage_Multicast IPv4: Will send to '%s'.\n"), _DH(), ip4MulticastAddress.toString().c_str()););
DEBUG_EX_INFO(if (!_getResponderIPAddress(pNetIf, enuIPProtocolType::V4)) DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage_Multicast IPv4: NO IPv4 address!.\n"), _DH()););
bIPv4Result = ((_prepareMessage(pNetIf, p_rSendParameter)) &&
(m_pUDPContext->setMulticastInterface(pNetIf), true) &&
(m_pUDPContext->sendTimeout(ip4MulticastAddress, DNS_MQUERY_PORT, clsConsts::u32SendTimeoutMs)) &&
(m_pUDPContext->setMulticastInterface(0), true) /*&&
(Serial.println("Did send MC V4"), true)*/);
DEBUG_EX_ERR(if (!bIPv4Result) DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage_Multicast (V4): FAILED!\n"), _DH()););
}
#endif
#ifdef MDNS2_IPV6_SUPPORT
if (p_IPProtocolTypes & static_cast<uint8_t>(enuIPProtocolType::V6))
{
IPAddress ip6MulticastAddress(DNS_MQUERY_IPV6_GROUP_INIT);
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage_Multicast IPv6: Will send to '%s'.\n"), _DH(), ip6MulticastAddress.toString().c_str()););
DEBUG_EX_INFO(if (!_getResponderIPAddress(pNetIf, enuIPProtocolType::V6)) DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage_Multicast IPv6: NO IPv6 address!.\n"), _DH()););
DEBUG_EX_ERR(
bool bPrepareMessage = false;
bool bUDPContextSend = false;
);
bIPv6Result = ((DEBUG_EX_ERR(bPrepareMessage =)_prepareMessage(pNetIf, p_rSendParameter)) &&
(m_pUDPContext->setMulticastInterface(pNetIf), true) &&
(DEBUG_EX_ERR(bUDPContextSend =)m_pUDPContext->sendTimeout(ip6MulticastAddress, DNS_MQUERY_PORT, clsConsts::u32SendTimeoutMs)) &&
(m_pUDPContext->setMulticastInterface(0), true) /*&&
(Serial.println("Did send MC V6"), true)*/);
DEBUG_EX_ERR(if (!bIPv6Result) DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage_Multicast (IPv6): FAILED! (%s, %s, %s)\n"), _DH(), (_getResponderIPAddress(pNetIf, enuIPProtocolType::V6).isSet() ? "1" : "0"), (bPrepareMessage ? "1" : "0"), (bUDPContextSend ? "1" : "0")););
}
#endif
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage_Multicast: %s!\n\n"), _DH(), ((bIPv4Result && bIPv6Result) ? "Succeeded" : "FAILED")););
DEBUG_EX_ERR(if (!(bIPv4Result && bIPv6Result)) DEBUG_OUTPUT.printf_P(PSTR("%s _sendMessage_Multicast: FAILED!\n"), _DH()););
return (bIPv4Result && bIPv6Result);
}
/*
MDNSResponder::_prepareMessage
The MDNS message is composed in a two-step process.
In the first loop 'only' the header informations (mainly number of answers) are collected,
while in the second loop, the header and all queries and answers are written to the UDP
output buffer.
*/
bool clsLEAMDNSHost::_prepareMessage(netif* pNetIf, clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage\n")););
bool bResult = true;
// Prepare output buffer for potential reuse
p_rSendParameter.flushTempContent();
// Prepare header; count answers
clsMsgHeader msgHeader(p_rSendParameter.m_u16ID,
(static_cast<clsSendParameter::enuResponseType>(clsSendParameter::enuResponseType::None) != p_rSendParameter.m_Response),
0,
p_rSendParameter.m_bAuthorative);
// If this is a response, the answers are anwers,
// else this is a query or probe and the answers go into auth section
uint16_t& ru16Answers = ((clsSendParameter::enuResponseType::None != p_rSendParameter.m_Response)
? msgHeader.m_u16ANCount // Usual answers
: msgHeader.m_u16NSCount); // Authorative answers
/**
enuSequence
*/
using typeSequence = uint8_t;
enum class enuSequence : typeSequence
{
Count = 0,
Send = 1
};
// Two step sequence: 'Count' and 'Send'
for (typeSequence sequence = static_cast<typeSequence>(enuSequence::Count); ((bResult) && (sequence <= static_cast<typeSequence>(enuSequence::Send))); ++sequence)
{
/*
DEBUG_EX_INFO(
if (static_cast<typeSequence>(enuSequence::Send) == sequence)
DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"),
_DH(),
(unsigned)msgHeader.m_u16ID,
(unsigned)msgHeader.m_1bQR, (unsigned)msgHeader.m_4bOpcode, (unsigned)msgHeader.m_1bAA, (unsigned)msgHeader.m_1bTC, (unsigned)msgHeader.m_1bRD,
(unsigned)msgHeader.m_1bRA, (unsigned)msgHeader.m_4bRCode,
(unsigned)msgHeader.m_u16QDCount,
(unsigned)msgHeader.m_u16ANCount,
(unsigned)msgHeader.m_u16NSCount,
(unsigned)msgHeader.m_u16ARCount);
);
*/
// Count/send
// Header
bResult = ((static_cast<typeSequence>(enuSequence::Count) == sequence)
? true
: _writeMDNSMsgHeader(msgHeader, p_rSendParameter));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSMsgHeader FAILED!\n"), _DH()););
// Questions
for (clsRRQuestion::list::iterator it = p_rSendParameter.m_RRQuestions.begin(); ((bResult) && (it != p_rSendParameter.m_RRQuestions.end())); it++)
{
clsRRQuestion* pQuestion = *it;
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++msgHeader.m_u16QDCount
: (bResult = _writeMDNSQuestion(*pQuestion, p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSQuestion FAILED!\n"), _DH()););
}
// Answers and authorative answers
// NSEC host (part 1)
uint32_t u32NSECContent = 0;
#ifdef MDNS_IPV4_SUPPORT
// A
if ((bResult) &&
(p_rSendParameter.m_u32HostReplyMask & static_cast<uint32_t>(enuContentFlag::A)) &&
(_getResponderIPAddress(pNetIf, enuIPProtocolType::V4).isSet()))
{
u32NSECContent |= static_cast<uint32_t>(enuContentFlag::A);
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16Answers
: (bResult = _writeMDNSAnswer_A(_getResponderIPAddress(pNetIf, enuIPProtocolType::V4), p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_A(A) FAILED!\n"), _DH()););
}
// PTR_IPv4
if ((bResult) &&
(p_rSendParameter.m_u32HostReplyMask & static_cast<uint32_t>(enuContentFlag::PTR_IPv4)) &&
(_getResponderIPAddress(pNetIf, enuIPProtocolType::V4).isSet()))
{
u32NSECContent |= static_cast<uint32_t>(enuContentFlag::PTR_IPv4);
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16Answers
: (bResult = _writeMDNSAnswer_PTR_IPv4(_getResponderIPAddress(pNetIf, enuIPProtocolType::V4), p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_PTR_IPv4 FAILED!\n"), _DH()););
}
#endif
#ifdef MDNS2_IPV6_SUPPORT
// AAAA
if ((bResult) &&
(p_rSendParameter.m_u32HostReplyMask & static_cast<uint32_t>(enuContentFlag::AAAA)) &&
(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V6)).isSet()))
{
u32NSECContent |= static_cast<uint32_t>(enuContentFlag::AAAA);
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16Answers
: (bResult = _writeMDNSAnswer_AAAA(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V6)), p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_AAAA(A) FAILED!\n"), _DH()););
}
// PTR_IPv6
if ((bResult) &&
(p_rSendParameter.m_u32HostReplyMask & static_cast<uint32_t>(enuContentFlag::PTR_IPv6)) &&
(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V6)).isSet()))
{
u32NSECContent |= static_cast<uint32_t>(enuContentFlag::PTR_IPv6);
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16Answers
: (bResult = _writeMDNSAnswer_PTR_IPv6(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V6)), p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_PTR_IPv6 FAILED!\n"), _DH()););
}
#endif
for (clsService::list::iterator it = m_Services.begin(); ((bResult) && (it != m_Services.end())); it++)
{
clsService* pService = *it;
// PTR_TYPE
if ((bResult) &&
(pService->m_u32ReplyMask & static_cast<uint32_t>(enuContentFlag::PTR_TYPE)))
{
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16Answers
: (bResult = _writeMDNSAnswer_PTR_TYPE(*pService, p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_PTR_TYPE FAILED!\n"), _DH()););
}
// PTR_NAME
if ((bResult) &&
(pService->m_u32ReplyMask & static_cast<uint32_t>(enuContentFlag::PTR_NAME)))
{
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16Answers
: (bResult = _writeMDNSAnswer_PTR_NAME(*pService, p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_PTR_NAME FAILED!\n"), _DH()););
}
// SRV
if ((bResult) &&
(pService->m_u32ReplyMask & static_cast<uint32_t>(enuContentFlag::SRV)))
{
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16Answers
: (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_SRV(A) FAILED!\n"), _DH()););
}
// TXT
if ((bResult) &&
(pService->m_u32ReplyMask & static_cast<uint32_t>(enuContentFlag::TXT)))
{
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16Answers
: (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_TXT(A) FAILED!\n"), _DH()););
}
} // for services
// Additional answers
uint16_t& ru16AdditionalAnswers = msgHeader.m_u16ARCount;
#ifdef MDNS_IPV4_SUPPORT
bool bNeedsAdditionalAnswerA = false;
#endif
#ifdef MDNS2_IPV6_SUPPORT
bool bNeedsAdditionalAnswerAAAA = false;
#endif
for (clsService::list::iterator it = m_Services.begin(); ((bResult) && (it != m_Services.end())); it++)
{
clsService* pService = *it;
if ((bResult) &&
(pService->m_u32ReplyMask & static_cast<uint32_t>(enuContentFlag::PTR_NAME)) && // If PTR_NAME is requested, AND
(!(pService->m_u32ReplyMask & static_cast<uint32_t>(enuContentFlag::SRV)))) // NOT SRV -> add SRV as additional answer
{
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16AdditionalAnswers
: (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_SRV(B) FAILED!\n"), _DH()););
}
/* AppleTV doesn't add TXT
if ((bResult) &&
(pService->m_u32ReplyMask & ContentFlag_PTR_NAME) && // If PTR_NAME is requested, AND
(!(pService->m_u32ReplyMask & ContentFlag_TXT))) { // NOT TXT -> add TXT as additional answer
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16AdditionalAnswers
: (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_TXT(B) FAILED!\n")););
}
*/
if ((pService->m_u32ReplyMask & (static_cast<uint32_t>(enuContentFlag::PTR_NAME) | static_cast<uint32_t>(enuContentFlag::SRV))) || // If service instance name or SRV OR
(p_rSendParameter.m_u32HostReplyMask & (static_cast<uint32_t>(enuContentFlag::A) | static_cast<uint32_t>(enuContentFlag::AAAA)))) // any host IP address is requested
{
#ifdef MDNS_IPV4_SUPPORT
if ((bResult) &&
(!(p_rSendParameter.m_u32HostReplyMask & static_cast<uint32_t>(enuContentFlag::A)))) // Add IPv4 address
{
bNeedsAdditionalAnswerA = true;
}
#endif
#ifdef MDNS2_IPV6_SUPPORT
if ((bResult) &&
(!(p_rSendParameter.m_u32HostReplyMask & static_cast<uint32_t>(enuContentFlag::AAAA)))) // Add IPv6 address
{
bNeedsAdditionalAnswerAAAA = true;
}
#endif
}
// NSEC record for service
if ((bResult) &&
(pService->m_u32ReplyMask) &&
((clsSendParameter::enuResponseType::None != p_rSendParameter.m_Response)))
{
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16AdditionalAnswers
: (bResult = _writeMDNSAnswer_NSEC(*pService, (static_cast<uint32_t>(enuContentFlag::TXT) | static_cast<uint32_t>(enuContentFlag::SRV)), p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_NSEC(Service) FAILED!\n"), _DH()););
}
} // for services
#ifdef MDNS_IPV4_SUPPORT
// Answer A needed?
if ((bResult) &&
(bNeedsAdditionalAnswerA) &&
(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V4)).isSet()))
{
// Additional A
u32NSECContent |= static_cast<uint32_t>(enuContentFlag::A);
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16AdditionalAnswers
: (bResult = _writeMDNSAnswer_A(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V4)), p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_A(B) FAILED!\n"), _DH()););
}
#endif
#ifdef MDNS2_IPV6_SUPPORT
// Answer AAAA needed?
if ((bResult) &&
(bNeedsAdditionalAnswerAAAA) &&
(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V6)).isSet()))
{
// Additional AAAA
u32NSECContent |= static_cast<uint32_t>(enuContentFlag::AAAA);
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? ++ru16AdditionalAnswers
: (bResult = _writeMDNSAnswer_AAAA(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V6)), p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_AAAA(B) FAILED!\n"), _DH()););
}
#endif
// NSEC host (part 2)
if ((bResult) &&
((clsSendParameter::enuResponseType::None != p_rSendParameter.m_Response)) &&
(u32NSECContent))
{
// NSEC PTR IPv4/IPv6 are separate answers; make sure, that this is counted for
#ifdef MDNS_IPV4_SUPPORT
uint32_t u32NSECContent_PTR_IPv4 = (u32NSECContent & static_cast<uint32_t>(enuContentFlag::PTR_IPv4));
u32NSECContent &= ~static_cast<uint32_t>(enuContentFlag::PTR_IPv4);
#endif
#ifdef MDNS2_IPV6_SUPPORT
uint32_t u32NSECContent_PTR_IPv6 = (u32NSECContent & static_cast<uint32_t>(enuContentFlag::PTR_IPv6));
u32NSECContent &= ~static_cast<uint32_t>(enuContentFlag::PTR_IPv6);
#endif
((static_cast<typeSequence>(enuSequence::Count) == sequence)
? (ru16AdditionalAnswers += ((u32NSECContent ? 1 : 0)
#ifdef MDNS_IPV4_SUPPORT
+ (u32NSECContent_PTR_IPv4 ? 1 : 0)
#endif
#ifdef MDNS2_IPV6_SUPPORT
+ (u32NSECContent_PTR_IPv6 ? 1 : 0)
#endif
))
: (bResult = (((!u32NSECContent) ||
// Write host domain NSEC answer
(_writeMDNSAnswer_NSEC(u32NSECContent, p_rSendParameter)))
#ifdef MDNS_IPV4_SUPPORT
// Write separate answer for host PTR IPv4
&& ((!u32NSECContent_PTR_IPv4) ||
((!_getResponderIPAddress(pNetIf, (enuIPProtocolType::V4)).isSet()) ||
(_writeMDNSAnswer_NSEC_PTR_IPv4(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V4)), p_rSendParameter))))
#endif
#ifdef MDNS2_IPV6_SUPPORT
// Write separate answer for host PTR IPv6
&& ((!u32NSECContent_PTR_IPv6) ||
((!_getResponderIPAddress(pNetIf, (enuIPProtocolType::V6)).isSet()) ||
(_writeMDNSAnswer_NSEC_PTR_IPv6(_getResponderIPAddress(pNetIf, (enuIPProtocolType::V6)), p_rSendParameter))))
#endif
)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: _writeMDNSAnswer_NSEC(Host) FAILED!\n"), _DH()););
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: Loop %i FAILED!\n"), _DH(), sequence););
} // for sequence
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _prepareMDNSMessage: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_addQueryRecord
Adds a query for the given domain and query type.
*/
bool clsLEAMDNSHost::_addQueryRecord(clsLEAMDNSHost::clsSendParameter& p_rSendParameter,
const clsLEAMDNSHost::clsRRDomain& p_QueryDomain,
uint16_t p_u16RecordType)
{
bool bResult = false;
clsRRQuestion* pNewRRQuestion = new clsRRQuestion;
if ((bResult = (0 != pNewRRQuestion)))
{
// Link to list of questions
p_rSendParameter.m_RRQuestions.push_back(pNewRRQuestion);
pNewRRQuestion->m_Header.m_Domain = p_QueryDomain;
pNewRRQuestion->m_Header.m_Attributes.m_u16Type = p_u16RecordType;
// It seems, that some mDNS implementations don't support 'unicast response' questions...
pNewRRQuestion->m_Header.m_Attributes.m_u16Class = (/*0x8000 |*/ DNS_RRCLASS_IN); // /*Unicast &*/ INternet
}
return bResult;
}
/*
MDNSResponder::_sendQuery
Creates and sends a query for the given domain and query type.
*/
bool clsLEAMDNSHost::_sendQuery(const clsLEAMDNSHost::clsQuery& p_Query,
clsLEAMDNSHost::clsQuery::clsAnswer::list* p_pKnownAnswers /*= 0*/)
{
bool bResult = false;
clsSendParameter sendParameter;
switch (p_Query.m_QueryType)
{
case clsQuery::enuQueryType::Host:
#ifdef MDNS_IPV4_SUPPORT
bResult = _addQueryRecord(sendParameter, p_Query.m_Domain, DNS_RRTYPE_A);
#endif
#ifdef MDNS2_IPV6_SUPPORT
bResult = _addQueryRecord(sendParameter, p_Query.m_Domain, DNS_RRTYPE_AAAA);
#endif
break;
case clsQuery::enuQueryType::Service:
bResult = _addQueryRecord(sendParameter, p_Query.m_Domain, DNS_RRTYPE_PTR);
break;
case clsQuery::enuQueryType::None:
default:
break;
}
// TODO: Add known answers to query
(void)p_pKnownAnswers;
bResult = ((bResult) &&
(_sendMessage(sendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _sendQuery: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_sendQuery
Creates and sends a query for the given domain and record type.
*/
bool clsLEAMDNSHost::_sendQuery(const clsLEAMDNSHost::clsRRDomain& p_QueryDomain,
uint16_t p_u16RecordType,
clsLEAMDNSHost::clsQuery::clsAnswer::list* p_pKnownAnswers /*= 0*/)
{
bool bResult = false;
clsSendParameter sendParameter;
bResult = ((_addQueryRecord(sendParameter, p_QueryDomain, p_u16RecordType)) &&
(_sendMessage(sendParameter)));
// TODO: Add known answer records
(void) p_pKnownAnswers;
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _sendQuery: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_getResponderIPAddress
*/
IPAddress clsLEAMDNSHost::_getResponderIPAddress(netif* pNetIf, enuIPProtocolType p_IPProtocolType) const
{
IPAddress ipResponder;
#ifdef MDNS_IPV4_SUPPORT
if (enuIPProtocolType::V4 == p_IPProtocolType)
{
ipResponder = netif_ip_addr4(pNetIf);
}
#endif
#ifdef MDNS2_IPV6_SUPPORT
if (enuIPProtocolType::V6 == p_IPProtocolType)
{
bool bCheckLinkLocal = true;
for (int i = 0; ((!ipResponder.isSet()) && (i < 2)); ++i) // Two loops: First with link-local check, second without
{
for (int idx = 0; idx < LWIP_IPV6_NUM_ADDRESSES; ++idx)
{
//DEBUG_EX_INFO(if ip6_addr_isvalid(netif_ip6_addr_state(&pNetIf, idx)) DEBUG_OUTPUT.printf_P(PSTR("%s _getResponderIPAddress: Checking IPv6 address %s (LL: %s)\n"), _DH(), IPAddress(netif_ip_addr6(pNetIf, idx)).toString().c_str(), (bCheckLinkLocal ? "YES" : "NO")););
if ((ip6_addr_isvalid(netif_ip6_addr_state(pNetIf, idx))) &&
(((!bCheckLinkLocal) ||
(ip6_addr_islinklocal(netif_ip6_addr(pNetIf, idx))))))
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _getResponderIPAddress: Selected IPv6 address %s (LL: %s)\n"), _DH(), IPAddress(netif_ip_addr6(pNetIf, idx)).toString().c_str(), (bCheckLinkLocal ? "YES" : "NO")););
ipResponder = netif_ip_addr6(pNetIf, idx);
break;
}
}
bCheckLinkLocal = false;
}
}
#endif
return ipResponder;
}
/**
HELPERS
*/
/**
RESOURCE RECORDS
*/
/*
MDNSResponder::_readRRQuestion
Reads a question (eg. MyESP._http._tcp.local ANY IN) from the UPD input buffer.
*/
bool clsLEAMDNSHost::_readRRQuestion(clsLEAMDNSHost::clsRRQuestion& p_rRRQuestion)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRQuestion\n")););
bool bResult = false;
if ((bResult = _readRRHeader(p_rRRQuestion.m_Header)))
{
// Extract unicast flag from class field
p_rRRQuestion.m_bUnicast = (p_rRRQuestion.m_Header.m_Attributes.m_u16Class & 0x8000);
//p_rRRQuestion.m_Header.m_Attributes.m_u16Class &= (~0x8000);
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _readRRQuestion "), _DH());
_printRRDomain(p_rRRQuestion.m_Header.m_Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:%s Class:%s\n"), _RRType2Name(p_rRRQuestion.m_Header.m_Attributes.m_u16Type), _RRClass2String(p_rRRQuestion.m_Header.m_Attributes.m_u16Class, true));
);
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRQuestion: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_readRRAnswer
Reads an answer (eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local)
from the UDP input buffer.
After reading the domain and type info, the further processing of the answer
is transferred the answer specific reading functions.
Unknown answer types are processed by the generic answer reader (to remove them
from the input buffer).
*/
bool clsLEAMDNSHost::_readRRAnswer(clsLEAMDNSHost::clsRRAnswer*& p_rpRRAnswer)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswer\n")););
bool bResult = false;
clsRRHeader header;
uint32_t u32TTL;
uint16_t u16RDLength;
if ((_readRRHeader(header)) &&
(_udpRead32(u32TTL)) &&
(_udpRead16(u16RDLength)))
{
/* DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswer: Reading 0x%04X answer (class:0x%04X, TTL:%u, RDLength:%u) for "), header.m_Attributes.m_u16Type, header.m_Attributes.m_u16Class, u32TTL, u16RDLength);
_printRRDomain(header.m_Domain);
DEBUG_OUTPUT.printf_P(PSTR("\n"));
);*/
switch (header.m_Attributes.m_u16Type /*& (~0x8000)*/) // Topmost bit might carry 'cache flush' flag
{
#ifdef MDNS_IPV4_SUPPORT
case DNS_RRTYPE_A:
p_rpRRAnswer = new clsRRAnswerA(header, u32TTL);
bResult = _readRRAnswerA(*(clsRRAnswerA*&)p_rpRRAnswer, u16RDLength);
break;
#endif
case DNS_RRTYPE_PTR:
p_rpRRAnswer = new clsRRAnswerPTR(header, u32TTL);
bResult = _readRRAnswerPTR(*(clsRRAnswerPTR*&)p_rpRRAnswer, u16RDLength);
break;
case DNS_RRTYPE_TXT:
p_rpRRAnswer = new clsRRAnswerTXT(header, u32TTL);
bResult = _readRRAnswerTXT(*(clsRRAnswerTXT*&)p_rpRRAnswer, u16RDLength);
break;
#ifdef MDNS2_IPV6_SUPPORT
case DNS_RRTYPE_AAAA:
p_rpRRAnswer = new clsRRAnswerAAAA(header, u32TTL);
bResult = _readRRAnswerAAAA(*(clsRRAnswerAAAA*&)p_rpRRAnswer, u16RDLength);
break;
#endif
case DNS_RRTYPE_SRV:
p_rpRRAnswer = new clsRRAnswerSRV(header, u32TTL);
bResult = _readRRAnswerSRV(*(clsRRAnswerSRV*&)p_rpRRAnswer, u16RDLength);
break;
default:
p_rpRRAnswer = new clsRRAnswerGeneric(header, u32TTL);
bResult = _readRRAnswerGeneric(*(clsRRAnswerGeneric*&)p_rpRRAnswer, u16RDLength);
break;
}
DEBUG_EX_INFO_IF((bResult) && (p_rpRRAnswer),
{
DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswer: "), _DH());
_printRRDomain(p_rpRRAnswer->m_Header.m_Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:%s Class:0x%04X TTL:%u, RDLength:%u "),
_RRType2Name(p_rpRRAnswer->m_Header.m_Attributes.m_u16Type),
(p_rpRRAnswer->m_Header.m_Attributes.m_u16Class | (p_rpRRAnswer->m_bCacheFlush ? 0x8000 : 0)),
p_rpRRAnswer->m_u32TTL,
u16RDLength);
switch (header.m_Attributes.m_u16Type /*& (~0x8000)*/) // Topmost bit might carry 'cache flush' flag
{
#ifdef MDNS_IPV4_SUPPORT
case DNS_RRTYPE_A:
DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((clsRRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str());
break;
#endif
case DNS_RRTYPE_PTR:
DEBUG_OUTPUT.printf_P(PSTR("PTR "));
_printRRDomain(((clsRRAnswerPTR*&)p_rpRRAnswer)->m_PTRDomain);
break;
case DNS_RRTYPE_TXT:
{
size_t stTxtLength = ((clsRRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_strLength();
char* pTxts = new char[stTxtLength];
if (pTxts)
{
((clsRRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_str(pTxts);
DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts);
delete[] pTxts;
}
break;
}
#ifdef MDNS2_IPV6_SUPPORT
case DNS_RRTYPE_AAAA:
DEBUG_OUTPUT.printf_P(PSTR("AAAA IP:%s"), ((clsRRAnswerAAAA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str());
break;
#endif
case DNS_RRTYPE_SRV:
DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), ((clsRRAnswerSRV*&)p_rpRRAnswer)->m_u16Port);
_printRRDomain(((clsRRAnswerSRV*&)p_rpRRAnswer)->m_SRVDomain);
break;
/* case DNS_RRTYPE_NSEC:
DEBUG_OUTPUT.printf_P(PSTR("NSEC "));
_printRRDomain(((stcRRAnswerNSEC*&)p_rpRRAnswer)->m_NSECDomain);
for (uint32_t u=0; u<(((stcRRAnswerNSEC*&)p_rpRRAnswer)->m_pNSECBitmap->m_u16BitmapLength * 8); ++u) {
uint8_t byte = ((stcRRAnswerNSEC*&)p_rpRRAnswer)->m_pNSECBitmap->m_pu8BitmapData[u / 8];
uint8_t flag = 1 << (7 - (u % 8)); // (7 - (0..7)) = 7..0
if (byte & flag) {
DEBUG_OUTPUT.printf_P(PSTR(" %s"), _RRType2Name(u));
}
}
break;*/
default:
DEBUG_OUTPUT.printf_P(PSTR("generic "));
break;
}
DEBUG_OUTPUT.printf_P(PSTR("\n"));
}); // DEBUG_EX_INFO
DEBUG_EX_INFO_IF(!((bResult) && (p_rpRRAnswer)),
{
DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswer: FAILED to read specific answer of type 0x%04X!\n"), _DH(), p_rpRRAnswer->m_Header.m_Attributes.m_u16Type);
});
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswer: FAILED!\n"), _DH()););
return bResult;
}
#ifdef MDNS_IPV4_SUPPORT
/*
MDNSResponder::_readRRAnswerA
*/
bool clsLEAMDNSHost::_readRRAnswerA(clsLEAMDNSHost::clsRRAnswerA& p_rRRAnswerA,
uint16_t p_u16RDLength)
{
uint32_t u32IPv4Address;
bool bResult = ((clsConsts::u16IPv4Size == p_u16RDLength) &&
(_udpReadBuffer((unsigned char*)&u32IPv4Address, clsConsts::u16IPv4Size)) &&
((p_rRRAnswerA.m_IPAddress = IPAddress(u32IPv4Address))));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerA: FAILED!\n"), _DH()););
return bResult;
}
#endif
/*
MDNSResponder::_readRRAnswerPTR
*/
bool clsLEAMDNSHost::_readRRAnswerPTR(clsLEAMDNSHost::clsRRAnswerPTR& p_rRRAnswerPTR,
uint16_t p_u16RDLength)
{
bool bResult = ((p_u16RDLength) &&
(_readRRDomain(p_rRRAnswerPTR.m_PTRDomain)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerPTR: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_readRRAnswerTXT
Read TXT items from a buffer like 4c#=15ff=20
*/
bool clsLEAMDNSHost::_readRRAnswerTXT(clsLEAMDNSHost::clsRRAnswerTXT& p_rRRAnswerTXT,
uint16_t p_u16RDLength)
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerTXT: RDLength:%u\n"), _DH(), p_u16RDLength););
bool bResult = true;
p_rRRAnswerTXT.clear();
if (p_u16RDLength)
{
bResult = false;
unsigned char* pucBuffer = new unsigned char[p_u16RDLength];
if (pucBuffer)
{
if (_udpReadBuffer(pucBuffer, p_u16RDLength))
{
bResult = true;
const unsigned char* pucCursor = pucBuffer;
while ((pucCursor < (pucBuffer + p_u16RDLength)) &&
(bResult))
{
bResult = false;
clsServiceTxt* pTxt = 0;
unsigned char ucLength = *pucCursor++; // Length of the next txt item
if (ucLength)
{
DEBUG_EX_INFO(
char sacBuffer[64];
*sacBuffer = 0;
uint8_t u8MaxLength = ((ucLength > (sizeof(sacBuffer) - 1)) ? (sizeof(sacBuffer) - 1) : ucLength);
os_strncpy(sacBuffer, (const char*)pucCursor, u8MaxLength + 1);
sacBuffer[u8MaxLength] = 0;
DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerTXT: Item(%u): %s\n"), _DH(), ucLength, sacBuffer);
);
unsigned char* pucEqualSign = (unsigned char*)os_strchr((const char*)pucCursor, '='); // Position of the '=' sign
unsigned char ucKeyLength;
if ((pucEqualSign) &&
((ucKeyLength = (pucEqualSign - pucCursor))))
{
unsigned char ucValueLength = (ucLength - (pucEqualSign - pucCursor + 1));
bResult = (((pTxt = new clsServiceTxt)) &&
(pTxt->setKey((const char*)pucCursor, ucKeyLength)) &&
(pTxt->setValue((const char*)(pucEqualSign + 1), ucValueLength)));
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerTXT: INVALID TXT format (No '=')!\n"), _DH()););
}
pucCursor += ucLength;
}
else // no/zero length TXT
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerTXT: INFO! TXT answer contains no items.\n"), _DH()););
bResult = true;
}
if ((bResult) &&
(pTxt))
{
// Everythings fine so far
// Link TXT item to answer TXTs
p_rRRAnswerTXT.m_Txts.add(pTxt);
}
else
{
// At least no TXT (migth be OK, if length was 0) OR an error
if (!bResult)
{
DEBUG_EX_ERR(
DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerTXT: FAILED to read TXT item!\n"), _DH());
DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n"));
_udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength);
DEBUG_OUTPUT.printf_P(PSTR("\n"));
);
}
if (pTxt)
{
delete pTxt;
pTxt = 0;
}
p_rRRAnswerTXT.clear();
}
} // while
DEBUG_EX_ERR(
if (!bResult) // Some failure
{
DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n"));
_udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength);
DEBUG_OUTPUT.printf_P(PSTR("\n"));
}
);
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerTXT: FAILED to read TXT content!\n"), _DH()););
}
// Clean up
delete[] pucBuffer;
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerTXT: FAILED to alloc buffer for TXT content!\n"), _DH()););
}
}
else
{
DEBUG_EX_ERR(
DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerTXT: WARNING! No content in TXT answer from "), _DH());
_printRRDomain(p_rRRAnswerTXT.m_Header.m_Domain);
DEBUG_OUTPUT.printf_P(PSTR("\n"));
);
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerTXT: FAILED!\n"), _DH()););
return bResult;
}
#ifdef MDNS2_IPV6_SUPPORT
bool clsLEAMDNSHost::_readRRAnswerAAAA(clsLEAMDNSHost::clsRRAnswerAAAA& p_rRRAnswerAAAA,
uint16_t p_u16RDLength)
{
bool bResult = false;
uint32_t au32IPv6Address[4]; // 16 bytes
if ((bResult = ((clsConsts::u16IPv6Size == p_u16RDLength) &&
(_udpReadBuffer((uint8_t*)&au32IPv6Address[0], clsConsts::u16IPv6Size)))))
{
// ?? IPADDR6_INIT_HOST ??
ip_addr_t addr = IPADDR6_INIT(au32IPv6Address[0], au32IPv6Address[1], au32IPv6Address[2], au32IPv6Address[3]);
p_rRRAnswerAAAA.m_IPAddress = IPAddress(addr);
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerAAAA: FAILED!\n"), _DH()););
return bResult;
}
#endif
/*
MDNSResponder::_readRRAnswerSRV
*/
bool clsLEAMDNSHost::_readRRAnswerSRV(clsLEAMDNSHost::clsRRAnswerSRV& p_rRRAnswerSRV,
uint16_t p_u16RDLength)
{
bool bResult = (((3 * sizeof(uint16_t)) < p_u16RDLength) &&
(_udpRead16(p_rRRAnswerSRV.m_u16Priority)) &&
(_udpRead16(p_rRRAnswerSRV.m_u16Weight)) &&
(_udpRead16(p_rRRAnswerSRV.m_u16Port)) &&
(_readRRDomain(p_rRRAnswerSRV.m_SRVDomain)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerSRV: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_readRRAnswerGeneric
*/
bool clsLEAMDNSHost::_readRRAnswerGeneric(clsLEAMDNSHost::clsRRAnswerGeneric& p_rRRAnswerGeneric,
uint16_t p_u16RDLength)
{
bool bResult = (0 == p_u16RDLength);
p_rRRAnswerGeneric.clear();
if (((p_rRRAnswerGeneric.m_u16RDLength = p_u16RDLength)) &&
((p_rRRAnswerGeneric.m_pu8RDData = new unsigned char[p_rRRAnswerGeneric.m_u16RDLength])))
{
bResult = _udpReadBuffer(p_rRRAnswerGeneric.m_pu8RDData, p_rRRAnswerGeneric.m_u16RDLength);
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAnswerGeneric: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_readRRHeader
*/
bool clsLEAMDNSHost::_readRRHeader(clsLEAMDNSHost::clsRRHeader& p_rRRHeader)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRHeader\n")););
bool bResult = ((_readRRDomain(p_rRRHeader.m_Domain)) &&
(_readRRAttributes(p_rRRHeader.m_Attributes)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRHeader: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_readRRDomain
Reads a (maybe multilevel compressed) domain from the UDP input buffer.
*/
bool clsLEAMDNSHost::_readRRDomain(clsLEAMDNSHost::clsRRDomain& p_rRRDomain)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain\n")););
bool bResult = ((p_rRRDomain.clear()) &&
(_readRRDomain_Loop(p_rRRDomain, 0)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_readRRDomain_Loop
Reads a domain from the UDP input buffer. For every compression level, the functions
calls itself recursively. To avoid endless recursion because of malformed MDNS records,
the maximum recursion depth is set by clsConsts::u8DomainMaxRedirections.
*/
bool clsLEAMDNSHost::_readRRDomain_Loop(clsLEAMDNSHost::clsRRDomain& p_rRRDomain,
uint8_t p_u8Depth)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(%u)\n"), _DH(), p_u8Depth););
bool bResult = false;
if (clsConsts::u8DomainMaxRedirections >= p_u8Depth)
{
bResult = true;
uint8_t u8Len = 0;
do
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(%u): Offset:%u p0:%02x\n"), _DH(), p_u8Depth, m_pUDPContext->tell(), m_pUDPContext->peek()););
_udpRead8(u8Len);
if (u8Len & clsConsts::u8DomainCompressMark)
{
// Compressed label(s)
uint16_t u16Offset = ((u8Len & ~clsConsts::u8DomainCompressMark) << 8); // Implicit BE to LE conversion!
_udpRead8(u8Len);
u16Offset |= u8Len;
if (m_pUDPContext->isValidOffset(u16Offset))
{
size_t stCurrentPosition = m_pUDPContext->tell(); // Prepare return from recursion
//DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(%u): Redirecting from %u to %u!\n"), _DH(), p_u8Depth, stCurrentPosition, u16Offset););
m_pUDPContext->seek(u16Offset);
if (_readRRDomain_Loop(p_rRRDomain, p_u8Depth + 1)) // Do recursion
{
//DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(%u): Succeeded to read redirected label! Returning to %u\n"), _DH(), p_u8Depth, stCurrentPosition););
m_pUDPContext->seek(stCurrentPosition); // Restore after recursion
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(%u): FAILED to read redirected label!\n"), _DH(), p_u8Depth););
bResult = false;
}
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(%u): INVALID offset in redirection!\n"), _DH(), p_u8Depth););
bResult = false;
}
break;
}
else
{
// Normal (uncompressed) label (maybe '\0' only)
if (clsConsts::stDomainMaxLength > (p_rRRDomain.m_u16NameLength + u8Len))
{
// Add length byte
p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength] = u8Len;
++(p_rRRDomain.m_u16NameLength);
if (u8Len) // Add name
{
if ((bResult = _udpReadBuffer((unsigned char*) & (p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength]), u8Len)))
{
/* DEBUG_EX_INFO(
p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength + u8Len] = 0; // Closing '\0' for printing
DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(%u): Domain label (%u): %s\n"), _DH(), p_u8Depth, (unsigned)(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength - 1]), &(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength]));
);*/
p_rRRDomain.m_u16NameLength += u8Len;
}
}
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(2) offset:%u p0:%x\n"), _DH(), m_pUDPContext->tell(), m_pUDPContext->peek()););
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(%u): ERROR! Domain name too long (%u + %u)!\n"), _DH(), p_u8Depth, p_rRRDomain.m_u16NameLength, u8Len););
bResult = false;
break;
}
}
} while ((bResult) &&
(0 != u8Len));
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRDomain_Loop(%u): ERROR! Too many redirections!\n"), _DH(), p_u8Depth););
}
return bResult;
}
/*
MDNSResponder::_readRRAttributes
*/
bool clsLEAMDNSHost::_readRRAttributes(clsLEAMDNSHost::clsRRAttributes& p_rRRAttributes)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAttributes\n")););
bool bResult = ((_udpRead16(p_rRRAttributes.m_u16Type)) &&
(_udpRead16(p_rRRAttributes.m_u16Class)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readRRAttributes: FAILED!\n"), _DH()););
return bResult;
}
/*
DOMAIN NAMES
*/
/*
MDNSResponder::_buildDomainForHost
Builds a MDNS host domain (eg. esp8266.local) for the given hostname.
*/
bool clsLEAMDNSHost::_buildDomainForHost(const char* p_pcHostName,
clsLEAMDNSHost::clsRRDomain& p_rHostDomain) const
{
p_rHostDomain.clear();
bool bResult = ((p_pcHostName) &&
(*p_pcHostName) &&
(p_rHostDomain.addLabel(p_pcHostName)) &&
(p_rHostDomain.addLabel(clsConsts::pcLocal)) &&
(p_rHostDomain.addLabel(0)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _buildDomainForHost: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_buildDomainForDNSSD
Builds the '_services._dns-sd._udp.local' domain.
Used while detecting generic service enum question (DNS-SD) and answering these questions.
*/
bool clsLEAMDNSHost::_buildDomainForDNSSD(clsLEAMDNSHost::clsRRDomain& p_rDNSSDDomain) const
{
p_rDNSSDDomain.clear();
bool bResult = ((p_rDNSSDDomain.addLabel(clsConsts::pcServices, true)) &&
(p_rDNSSDDomain.addLabel(clsConsts::pcDNSSD, true)) &&
(p_rDNSSDDomain.addLabel(clsConsts::pcUDP, true)) &&
(p_rDNSSDDomain.addLabel(clsConsts::pcLocal)) &&
(p_rDNSSDDomain.addLabel(0)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _buildDomainForDNSSD: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_buildDomainForService
Builds the domain for the given service (eg. _http._tcp.local or
MyESP._http._tcp.local (if p_bIncludeName is set)).
*/
bool clsLEAMDNSHost::_buildDomainForService(const clsLEAMDNSHost::clsService& p_Service,
bool p_bIncludeName,
clsLEAMDNSHost::clsRRDomain& p_rServiceDomain) const
{
p_rServiceDomain.clear();
bool bResult = (((!p_bIncludeName) ||
(p_rServiceDomain.addLabel(p_Service.instanceName()))) &&
(p_rServiceDomain.addLabel(p_Service.type(), ('_' != *p_Service.type()))) &&
(p_rServiceDomain.addLabel(p_Service.protocol(), ('_' != *p_Service.protocol()))) &&
(p_rServiceDomain.addLabel(clsConsts::pcLocal)) &&
(p_rServiceDomain.addLabel(0)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _buildDomainForService: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_buildDomainForService
Builds the domain for the given service properties (eg. _http._tcp.local).
The usual prepended '_' are added, if missing in the input strings.
*/
bool clsLEAMDNSHost::_buildDomainForService(const char* p_pcServiceType,
const char* p_pcProtocol,
clsLEAMDNSHost::clsRRDomain& p_rServiceDomain) const
{
p_rServiceDomain.clear();
bool bResult = ((p_pcServiceType) &&
(p_pcProtocol) &&
(p_rServiceDomain.addLabel(p_pcServiceType, ('_' != *p_pcServiceType))) &&
(p_rServiceDomain.addLabel(p_pcProtocol, ('_' != *p_pcProtocol))) &&
(p_rServiceDomain.addLabel(clsConsts::pcLocal)) &&
(p_rServiceDomain.addLabel(0)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _buildDomainForService: FAILED for (%s.%s)!\n"), _DH(), (p_pcServiceType ? : "-"), (p_pcProtocol ? : "-")););
return bResult;
}
#ifdef MDNS_IPV4_SUPPORT
/*
MDNSResponder::_buildDomainForReverseIPv4
The IPv4 address is stringized by printing the four address bytes into a char buffer in reverse order
and adding 'in-addr.arpa' (eg. 012.789.456.123.in-addr.arpa).
Used while detecting reverse IPv4 questions and answering these
*/
bool clsLEAMDNSHost::_buildDomainForReverseIPv4(IPAddress p_IPv4Address,
clsLEAMDNSHost::clsRRDomain& p_rReverseIPv4Domain) const
{
bool bResult = ((p_IPv4Address.isSet()) &&
(p_IPv4Address.isV4()));
p_rReverseIPv4Domain.clear();
char acBuffer[32];
for (int i = clsConsts::u16IPv4Size; ((bResult) && (i >= 1)); --i)
{
itoa(p_IPv4Address[i - 1], acBuffer, 10);
bResult = p_rReverseIPv4Domain.addLabel(acBuffer);
}
bResult = ((bResult) &&
(p_rReverseIPv4Domain.addLabel(clsConsts::pcReverseIPv4Domain)) &&
(p_rReverseIPv4Domain.addLabel(clsConsts::pcReverseTopDomain)) &&
(p_rReverseIPv4Domain.addLabel(0)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _buildDomainForReverseIPv4: FAILED!\n"), _DH()););
return bResult;
}
#endif
#ifdef MDNS2_IPV6_SUPPORT
/*
MDNSResponder::_buildDomainForReverseIPv6
The IPv6 address is stringized by printing the 16 address bytes (32 nibbles) into a char buffer in reverse order
and adding 'ip6.arpa' (eg. 3.B.6.E.A.1.B.B.A.B.F.7.F.8.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.8.E.F.ip6.arpa).
Used while detecting reverse IPv6 questions and answering these
*/
bool clsLEAMDNSHost::_buildDomainForReverseIPv6(IPAddress p_IPv6Address,
clsLEAMDNSHost::clsRRDomain& p_rReverseIPv6Domain) const
{
bool bResult = ((p_IPv6Address.isSet()) &&
(p_IPv6Address.isV6()));
p_rReverseIPv6Domain.clear();
const uint16_t* pRaw = p_IPv6Address.raw6();
for (int8_t i8 = (clsConsts::u16IPv6Size / 2); ((bResult) && (i8 > 0)); --i8) // 8..1
{
uint16_t u16Part = ntohs(pRaw[i8 - 1] & 0xFFFF);
char acBuffer[2];
for (uint8_t u8 = 0; ((bResult) && (u8 < 4)); ++u8) // 0..3
{
itoa((u16Part & 0xF), acBuffer, 16);
bResult = p_rReverseIPv6Domain.addLabel(acBuffer);
u16Part >>= 4;
}
}
bResult = ((bResult) &&
(p_rReverseIPv6Domain.addLabel(clsConsts::pcReverseIPv6Domain)) && // .ip6.arpa
(p_rReverseIPv6Domain.addLabel(clsConsts::pcReverseTopDomain)) && // .local
(p_rReverseIPv6Domain.addLabel(0)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _buildDomainForReverseIPv6: FAILED!\n"), _DH()););
return bResult;
}
#endif
/*
UDP
*/
/*
MDNSResponder::_udpReadBuffer
*/
bool clsLEAMDNSHost::_udpReadBuffer(unsigned char* p_pBuffer,
size_t p_stLength)
{
bool bResult = ((m_pUDPContext->getSize() >= p_stLength) &&
(p_pBuffer) &&
(p_stLength) &&
((p_stLength == m_pUDPContext->read((char*)p_pBuffer, p_stLength))));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _udpReadBuffer: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_udpRead8
*/
bool clsLEAMDNSHost::_udpRead8(uint8_t& p_ru8Value)
{
return _udpReadBuffer((unsigned char*)&p_ru8Value, sizeof(p_ru8Value));
}
/*
MDNSResponder::_udpRead16
*/
bool clsLEAMDNSHost::_udpRead16(uint16_t& p_ru16Value)
{
bool bResult = false;
if (_udpReadBuffer((unsigned char*)&p_ru16Value, sizeof(p_ru16Value)))
{
p_ru16Value = lwip_ntohs(p_ru16Value);
bResult = true;
}
return bResult;
}
/*
MDNSResponder::_udpRead32
*/
bool clsLEAMDNSHost::_udpRead32(uint32_t& p_ru32Value)
{
bool bResult = false;
if (_udpReadBuffer((unsigned char*)&p_ru32Value, sizeof(p_ru32Value)))
{
p_ru32Value = lwip_ntohl(p_ru32Value);
bResult = true;
}
return bResult;
}
/*
MDNSResponder::_udpAppendBuffer
*/
bool clsLEAMDNSHost::_udpAppendBuffer(const unsigned char* p_pcBuffer,
size_t p_stLength)
{
bool bResult = ((p_pcBuffer) &&
(p_stLength) &&
(p_stLength == m_pUDPContext->append((const char*)p_pcBuffer, p_stLength)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _udpAppendBuffer: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_udpAppend8
*/
bool clsLEAMDNSHost::_udpAppend8(uint8_t p_u8Value)
{
return (_udpAppendBuffer((unsigned char*)&p_u8Value, sizeof(p_u8Value)));
}
/*
MDNSResponder::_udpAppend16
*/
bool clsLEAMDNSHost::_udpAppend16(uint16_t p_u16Value)
{
p_u16Value = lwip_htons(p_u16Value);
return (_udpAppendBuffer((unsigned char*)&p_u16Value, sizeof(p_u16Value)));
}
/*
MDNSResponder::_udpAppend32
*/
bool clsLEAMDNSHost::_udpAppend32(uint32_t p_u32Value)
{
p_u32Value = lwip_htonl(p_u32Value);
return (_udpAppendBuffer((unsigned char*)&p_u32Value, sizeof(p_u32Value)));
}
#ifdef DEBUG_ESP_PORT
/*
MDNSResponder::_udpDump
*/
bool clsLEAMDNSHost::_udpDump(bool p_bMovePointer /*= false*/)
{
const uint8_t cu8BytesPerLine = 16;
uint32_t u32StartPosition = m_pUDPContext->tell();
DEBUG_OUTPUT.println("UDP Context Dump:");
uint32_t u32Counter = 0;
uint8_t u8Byte = 0;
while (_udpRead8(u8Byte))
{
DEBUG_OUTPUT.printf_P(PSTR("%02x %s"), u8Byte, ((++u32Counter % cu8BytesPerLine) ? "" : "\n"));
}
DEBUG_OUTPUT.printf_P(PSTR("%sDone: %u bytes\n"), (((u32Counter) && (u32Counter % cu8BytesPerLine)) ? "\n" : ""), u32Counter);
if (!p_bMovePointer) // Restore
{
m_pUDPContext->seek(u32StartPosition);
}
return true;
}
/*
MDNSResponder::_udpDump
*/
bool clsLEAMDNSHost::_udpDump(unsigned p_uOffset,
unsigned p_uLength)
{
if (m_pUDPContext->isValidOffset(p_uOffset))
{
unsigned uCurrentPosition = m_pUDPContext->tell(); // Remember start position
m_pUDPContext->seek(p_uOffset);
uint8_t u8Byte;
for (unsigned u = 0; ((u < p_uLength) && (_udpRead8(u8Byte))); ++u)
{
DEBUG_OUTPUT.printf_P(PSTR("%02x "), u8Byte);
}
// Return to start position
m_pUDPContext->seek(uCurrentPosition);
}
return true;
}
#endif // DEBUG_ESP_PORT
/**
READ/WRITE MDNS STRUCTS
*/
/*
MDNSResponder::_readMDNSMsgHeader
Read a MDNS header from the UDP input buffer.
| 8 | 8 | 8 | 8 |
00| Identifier | Flags & Codes |
01| Question count | Answer count |
02| NS answer count | Ad answer count |
All 16-bit and 32-bit elements need to be translated from network coding to host coding (done in _udpRead16 and _udpRead32)
In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they
need some mapping here
*/
bool clsLEAMDNSHost::_readMDNSMsgHeader(clsLEAMDNSHost::clsMsgHeader& p_rMsgHeader)
{
bool bResult = false;
uint8_t u8B1;
uint8_t u8B2;
if ((_udpRead16(p_rMsgHeader.m_u16ID)) &&
(_udpRead8(u8B1)) &&
(_udpRead8(u8B2)) &&
(_udpRead16(p_rMsgHeader.m_u16QDCount)) &&
(_udpRead16(p_rMsgHeader.m_u16ANCount)) &&
(_udpRead16(p_rMsgHeader.m_u16NSCount)) &&
(_udpRead16(p_rMsgHeader.m_u16ARCount)))
{
p_rMsgHeader.m_1bQR = (u8B1 & 0x80); // Query/Response flag
p_rMsgHeader.m_4bOpcode = (u8B1 & 0x78); // Operation code (0: Standard query, others ignored)
p_rMsgHeader.m_1bAA = (u8B1 & 0x04); // Authorative answer
p_rMsgHeader.m_1bTC = (u8B1 & 0x02); // Truncation flag
p_rMsgHeader.m_1bRD = (u8B1 & 0x01); // Recursion desired
p_rMsgHeader.m_1bRA = (u8B2 & 0x80); // Recursion available
p_rMsgHeader.m_3bZ = (u8B2 & 0x70); // Zero
p_rMsgHeader.m_4bRCode = (u8B2 & 0x0F); // Response code
/* DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _readMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"),
_DH(),
(unsigned)p_rMsgHeader.m_u16ID,
(unsigned)p_rMsgHeader.m_1bQR, (unsigned)p_rMsgHeader.m_4bOpcode, (unsigned)p_rMsgHeader.m_1bAA, (unsigned)p_rMsgHeader.m_1bTC, (unsigned)p_rMsgHeader.m_1bRD,
(unsigned)p_rMsgHeader.m_1bRA, (unsigned)p_rMsgHeader.m_4bRCode,
(unsigned)p_rMsgHeader.m_u16QDCount,
(unsigned)p_rMsgHeader.m_u16ANCount,
(unsigned)p_rMsgHeader.m_u16NSCount,
(unsigned)p_rMsgHeader.m_u16ARCount););*/
bResult = true;
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _readMDNSMsgHeader: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_write8
*/
bool clsLEAMDNSHost::_write8(uint8_t p_u8Value,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
return ((_udpAppend8(p_u8Value)) &&
(p_rSendParameter.shiftOffset(sizeof(p_u8Value))));
}
/*
MDNSResponder::_write16
*/
bool clsLEAMDNSHost::_write16(uint16_t p_u16Value,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
return ((_udpAppend16(p_u16Value)) &&
(p_rSendParameter.shiftOffset(sizeof(p_u16Value))));
}
/*
MDNSResponder::_write32
*/
bool clsLEAMDNSHost::_write32(uint32_t p_u32Value,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
return ((_udpAppend32(p_u32Value)) &&
(p_rSendParameter.shiftOffset(sizeof(p_u32Value))));
}
/*
MDNSResponder::_writeMDNSMsgHeader
Write MDNS header to the UDP output buffer.
All 16-bit and 32-bit elements need to be translated from host coding to network coding (done in _udpAppend16 and _udpAppend32)
In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they
need some mapping here
*/
bool clsLEAMDNSHost::_writeMDNSMsgHeader(const clsLEAMDNSHost::clsMsgHeader& p_MsgHeader,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
/* DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"),
_DH(),
(unsigned)p_MsgHeader.m_u16ID,
(unsigned)p_MsgHeader.m_1bQR, (unsigned)p_MsgHeader.m_4bOpcode, (unsigned)p_MsgHeader.m_1bAA, (unsigned)p_MsgHeader.m_1bTC, (unsigned)p_MsgHeader.m_1bRD,
(unsigned)p_MsgHeader.m_1bRA, (unsigned)p_MsgHeader.m_4bRCode,
(unsigned)p_MsgHeader.m_u16QDCount,
(unsigned)p_MsgHeader.m_u16ANCount,
(unsigned)p_MsgHeader.m_u16NSCount,
(unsigned)p_MsgHeader.m_u16ARCount););*/
uint8_t u8B1((p_MsgHeader.m_1bQR << 7) | (p_MsgHeader.m_4bOpcode << 3) | (p_MsgHeader.m_1bAA << 2) | (p_MsgHeader.m_1bTC << 1) | (p_MsgHeader.m_1bRD));
uint8_t u8B2((p_MsgHeader.m_1bRA << 7) | (p_MsgHeader.m_3bZ << 4) | (p_MsgHeader.m_4bRCode));
bool bResult = ((_write16(p_MsgHeader.m_u16ID, p_rSendParameter)) &&
(_write8(u8B1, p_rSendParameter)) &&
(_write8(u8B2, p_rSendParameter)) &&
(_write16(p_MsgHeader.m_u16QDCount, p_rSendParameter)) &&
(_write16(p_MsgHeader.m_u16ANCount, p_rSendParameter)) &&
(_write16(p_MsgHeader.m_u16NSCount, p_rSendParameter)) &&
(_write16(p_MsgHeader.m_u16ARCount, p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSMsgHeader: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeRRAttributes
*/
bool clsLEAMDNSHost::_writeMDNSRRAttributes(const clsLEAMDNSHost::clsRRAttributes& p_Attributes,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
bool bResult = ((_write16(p_Attributes.m_u16Type, p_rSendParameter)) &&
(_write16(p_Attributes.m_u16Class, p_rSendParameter)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSRRAttributes: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeMDNSRRDomain
*/
bool clsLEAMDNSHost::_writeMDNSRRDomain(const clsLEAMDNSHost::clsRRDomain& p_Domain,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
bool bResult = ((_udpAppendBuffer((const unsigned char*)p_Domain.m_acName, p_Domain.m_u16NameLength)) &&
(p_rSendParameter.shiftOffset(p_Domain.m_u16NameLength)));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSRRDomain: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeMDNSHostDomain
Write a host domain to the UDP output buffer.
If the domain record is part of the answer, the records length is
prepended (p_bPrependRDLength is set).
A very simple form of name compression is applied here:
If the domain is written to the UDP output buffer, the write offset is stored
together with a domain id (the pointer) in a p_rSendParameter substructure (cache).
If the same domain (pointer) should be written to the UDP output later again,
the old offset is retrieved from the cache, marked as a compressed domain offset
and written to the output buffer.
*/
bool clsLEAMDNSHost::_writeMDNSHostDomain(const char* p_pcHostName,
bool p_bPrependRDLength,
uint16_t p_u16AdditionalLength,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
// The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV'
uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)p_pcHostName, false);
clsRRDomain hostDomain;
bool bResult = (u16CachedDomainOffset
// Found cached domain -> mark as compressed domain
? ((clsConsts::u8DomainCompressMark > ((u16CachedDomainOffset >> 8) & ~clsConsts::u8DomainCompressMark)) && // Valid offset
((!p_bPrependRDLength) ||
(_write16((2 + p_u16AdditionalLength), p_rSendParameter))) && // Length of 'Cxxx'
(_write8(((u16CachedDomainOffset >> 8) | clsConsts::u8DomainCompressMark), p_rSendParameter)) && // Compression mark (and offset)
(_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter)))
// No cached domain -> add this domain to cache and write full domain name
: ((_buildDomainForHost(p_pcHostName, hostDomain)) && // eg. esp8266.local
((!p_bPrependRDLength) ||
(_write16((hostDomain.m_u16NameLength + p_u16AdditionalLength), p_rSendParameter))) && // RDLength (if needed)
(p_rSendParameter.addDomainCacheItem((const void*)p_pcHostName, false, p_rSendParameter.m_u16Offset)) &&
(_writeMDNSRRDomain(hostDomain, p_rSendParameter))));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSHostDomain: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeMDNSServiceDomain
Write a service domain to the UDP output buffer.
If the domain record is part of the answer, the records length is
prepended (p_bPrependRDLength is set).
A very simple form of name compression is applied here: see '_writeMDNSHostDomain'
The cache differentiates of course between service domains which includes
the instance name (p_bIncludeName is set) and thoose who don't.
*/
bool clsLEAMDNSHost::_writeMDNSServiceDomain(const clsLEAMDNSHost::clsService& p_Service,
bool p_bIncludeName,
bool p_bPrependRDLength,
uint16_t p_u16AdditionalLength,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
// The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV'
uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)&p_Service, p_bIncludeName);
clsRRDomain serviceDomain;
bool bResult = (u16CachedDomainOffset
// Found cached domain -> mark as compressed domain
? ((clsConsts::u8DomainCompressMark > ((u16CachedDomainOffset >> 8) & ~clsConsts::u8DomainCompressMark)) && // Valid offset
((!p_bPrependRDLength) ||
(_write16((2 + p_u16AdditionalLength), p_rSendParameter))) && // Lenght of 'Cxxx'
(_write8(((u16CachedDomainOffset >> 8) | clsConsts::u8DomainCompressMark), p_rSendParameter)) && // Compression mark (and offset)
(_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter)))
// No cached domain -> add this domain to cache and write full domain name
: ((_buildDomainForService(p_Service, p_bIncludeName, serviceDomain)) && // eg. MyESP._http._tcp.local
((!p_bPrependRDLength) ||
(_write16((serviceDomain.m_u16NameLength + p_u16AdditionalLength), p_rSendParameter))) && // RDLength (if needed)
(p_rSendParameter.addDomainCacheItem((const void*)&p_Service, p_bIncludeName, p_rSendParameter.m_u16Offset)) &&
(_writeMDNSRRDomain(serviceDomain, p_rSendParameter))));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSServiceDomain: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeMDNSQuestion
Write a MDNS question to the UDP output buffer
QNAME (host/service domain, eg. esp8266.local)
QTYPE (16bit, eg. ANY)
QCLASS (16bit, eg. IN)
*/
bool clsLEAMDNSHost::_writeMDNSQuestion(clsLEAMDNSHost::clsRRQuestion& p_Question,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSQuestion\n")););
bool bResult = ((_writeMDNSRRDomain(p_Question.m_Header.m_Domain, p_rSendParameter)) &&
(_writeMDNSRRAttributes(p_Question.m_Header.m_Attributes, p_rSendParameter)));
DEBUG_EX_INFO(if (bResult)
{
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSQuestion "), _DH());
_printRRDomain(p_Question.m_Header.m_Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:%s Class:0x%04X\n"),
_RRType2Name(p_Question.m_Header.m_Attributes.m_u16Type),
p_Question.m_Header.m_Attributes.m_u16Class);
});
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSQuestion: FAILED!\n"), _DH()););
return bResult;
}
#ifdef MDNS_IPV4_SUPPORT
/*
MDNSResponder::_writeMDNSAnswer_A
Write a MDNS A answer to the UDP output buffer.
NAME (var, host/service domain, eg. esp8266.local
TYPE (16bit, eg. A)
CLASS (16bit, eg. IN)
TTL (32bit, eg. 120)
RDLENGTH (16bit, eg 4)
RDATA (var, eg. 123.456.789.012)
eg. esp8266.local A 0x8001 120 4 123.456.789.012
Ref: http://www.zytrax.com/books/dns/ch8/a.html
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_A(IPAddress p_IPAddress,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_A (%s)%s\n"), p_IPAddress.toString().c_str(), (p_rSendParameter.m_bCacheFlush ? "" : " nF")););
clsRRAttributes attributes(DNS_RRTYPE_A,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
const unsigned char aucIPAddress[clsConsts::u16IPv4Size] = { p_IPAddress[0], p_IPAddress[1], p_IPAddress[2], p_IPAddress[3] };
bool bResult = ((p_IPAddress.isV4()) &&
(_writeMDNSHostDomain(m_pcHostName, false, 0, p_rSendParameter)) &&
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)), p_rSendParameter)) && // TTL
(_write16(clsConsts::u16IPv4Size, p_rSendParameter)) && // RDLength
(_udpAppendBuffer(aucIPAddress, clsConsts::u16IPv4Size)) && // RData
(p_rSendParameter.shiftOffset(clsConsts::u16IPv4Size)));
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_A %s.local Type:%s Class:0x%04X TTL:%u %s\n"),
_DH(),
m_pcHostName,
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)),
p_IPAddress.toString().c_str());
);
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_A: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeMDNSAnswer_PTR_IPv4
Write a MDNS reverse IPv4 PTR answer to the UDP output buffer.
See: '_writeMDNSAnswer_A'
eg. 012.789.456.123.in-addr.arpa PTR 0x8001 120 15 esp8266.local
Used while answering reverse IPv4 questions
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_PTR_IPv4(IPAddress p_IPAddress,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_IPv4 (%s)%s\n"), p_IPAddress.toString().c_str(), (p_rSendParameter.m_bCacheFlush ? "" : " nF")););
clsRRDomain reverseIPv4Domain;
clsRRAttributes attributes(DNS_RRTYPE_PTR,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
clsRRDomain hostDomain;
bool bResult = ((p_IPAddress.isV4()) &&
(_buildDomainForReverseIPv4(p_IPAddress, reverseIPv4Domain)) && // 012.789.456.123.in-addr.arpa
(_writeMDNSRRDomain(reverseIPv4Domain, p_rSendParameter)) &&
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)), p_rSendParameter)) && // TTL
(_writeMDNSHostDomain(m_pcHostName, true, 0, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local)
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_IPv4 "), _DH());
_printRRDomain(reverseIPv4Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:%s Class:0x%04X TTL:%u %s.local\n"),
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)),
m_pcHostName);
);
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_IPv4: FAILED!\n"), _DH()););
return bResult;
}
#endif
/*
MDNSResponder::_writeMDNSAnswer_PTR_TYPE
Write a MDNS PTR answer to the UDP output buffer.
See: '_writeMDNSAnswer_A'
PTR all-services -> service type
eg. _services._dns-sd._udp.local PTR 0x8001 5400 xx _http._tcp.local
http://www.zytrax.com/books/dns/ch8/ptr.html
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_PTR_TYPE(clsLEAMDNSHost::clsService& p_rService,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_TYPE\n")););
clsRRDomain dnssdDomain;
clsRRDomain serviceDomain;
clsRRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush for shared records! only INternet
bool bResult = ((_buildDomainForDNSSD(dnssdDomain)) && // _services._dns-sd._udp.local
(_writeMDNSRRDomain(dnssdDomain, p_rSendParameter)) &&
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32ServiceTTL)), p_rSendParameter)) && // TTL
(_writeMDNSServiceDomain(p_rService, false, true, 0, p_rSendParameter))); // RDLength & RData (service domain, eg. _http._tcp.local)
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_TYPE "), _DH());
_printRRDomain(dnssdDomain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:%s Class:0x%04X TTL:%u _%s._%s.local\n"),
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32ServiceTTL)),
p_rService.m_pcType,
p_rService.m_pcProtocol);
);
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_TYPE: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeMDNSAnswer_PTR_NAME
Write a MDNS PTR answer to the UDP output buffer.
See: '_writeMDNSAnswer_A'
PTR service type -> service name
eg. _http.tcp.local PTR 0x8001 120 xx myESP._http._tcp.local
http://www.zytrax.com/books/dns/ch8/ptr.html
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_PTR_NAME(clsLEAMDNSHost::clsService& p_rService,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_NAME\n"), _DH()););
clsRRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush for shared records! only INternet
bool bResult = ((_writeMDNSServiceDomain(p_rService, false, false, 0, p_rSendParameter)) && // _http._tcp.local
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32ServiceTTL)), p_rSendParameter)) && // TTL
(_writeMDNSServiceDomain(p_rService, true, true, 0, p_rSendParameter))); // RDLength & RData (service domain, eg. MyESP._http._tcp.local)
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_NAME _%s._%s.local Type:%s Class:0x%04X TTL:%u %s._%s._%s.local\n"),
_DH(),
p_rService.m_pcType,
p_rService.m_pcProtocol,
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32ServiceTTL)),
p_rService.m_pcInstanceName,
p_rService.m_pcType,
p_rService.m_pcProtocol);
);
DEBUG_EX_ERR(if (!bResult)DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_NAME: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeMDNSAnswer_TXT
Write a MDNS TXT answer to the UDP output buffer.
See: '_writeMDNSAnswer_A'
The TXT items in the RDATA block are 'length byte encoded': [len]vardata
eg. myESP._http._tcp.local TXT 0x8001 120 4 c#=1
http://www.zytrax.com/books/dns/ch8/txt.html
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_TXT(clsLEAMDNSHost::clsService& p_rService,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_TXT%s\n"), (p_rSendParameter.m_bCacheFlush ? "" : " nF"), _DH()););
bool bResult = false;
clsRRAttributes attributes(DNS_RRTYPE_TXT,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
if ((_collectServiceTxts(p_rService)) &&
(_writeMDNSServiceDomain(p_rService, true, false, 0, p_rSendParameter)) && // MyESP._http._tcp.local
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery // TTL
? clsConsts::u32LegacyTTL
: clsConsts::u32ServiceTTL)), p_rSendParameter)) &&
(_write16((p_rService.m_Txts.count() // RDLength
? p_rService.m_Txts.length() // default case
: 1), p_rSendParameter))) // If no TXT records exist, a single 0 byte is sent
{
bResult = true;
// RData Txts
if (p_rService.m_Txts.count())
{
for (const clsServiceTxt* pTxt : p_rService.m_Txts.m_Txts)
{
unsigned char ucLengthByte = pTxt->length();
if (!((bResult = ((_udpAppendBuffer((unsigned char*)&ucLengthByte, sizeof(ucLengthByte))) && // Length
(p_rSendParameter.shiftOffset(sizeof(ucLengthByte))) &&
((size_t)os_strlen(pTxt->m_pcKey) == m_pUDPContext->append(pTxt->m_pcKey, os_strlen(pTxt->m_pcKey))) && // Key
(p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcKey))) &&
(1 == m_pUDPContext->append("=", 1)) && // =
(p_rSendParameter.shiftOffset(1)) &&
((!pTxt->m_pcValue) ||
(((size_t)os_strlen(pTxt->m_pcValue) == m_pUDPContext->append(pTxt->m_pcValue, os_strlen(pTxt->m_pcValue))) && // Value
(p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcValue)))))))))
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_TXT: FAILED to write %sTxt %s=%s!\n"), _DH(), (pTxt->m_bTemp ? "temp. " : ""), (pTxt->m_pcKey ? : "?"), (pTxt->m_pcValue ? : "?")););
break;
}
}
}
else
{
// RFC 6763 Ch.6: Every DNS-SD service MUST have a TXT record in addition to its SRV record, ...
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_TXT: Adding EMPTY TXT record!\n"), _DH()););
unsigned char ucLengthByte = 0;
bResult = ((_udpAppendBuffer((unsigned char*)&ucLengthByte, sizeof(ucLengthByte))) && // Length
(p_rSendParameter.shiftOffset(sizeof(ucLengthByte))));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_TXT: FAILED to write EMPTY TXT record!\n"), _DH()););
}
}
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_TXT %s._%s._%s.local Type:%s Class:0x%04X TTL:%u \n"),
_DH(),
p_rService.m_pcInstanceName,
p_rService.m_pcType,
p_rService.m_pcProtocol,
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32ServiceTTL)));
);
_releaseTempServiceTxts(p_rService);
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_TXT: FAILED!\n"), _DH()););
return bResult;
}
#ifdef MDNS2_IPV6_SUPPORT
/*
MDNSResponder::_writeMDNSAnswer_AAAA
Write a MDNS AAAA answer to the UDP output buffer.
See: '_writeMDNSAnswer_AAAA'
eg. esp8266.local AAAA 0x8001 120 16 xxxx::xx
http://www.zytrax.com/books/dns/ch8/aaaa.html
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_AAAA(IPAddress p_IPAddress,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_AAAA (%s)%s\n"), p_IPAddress.toString().c_str(), (p_rSendParameter.m_bCacheFlush ? "" : " nF")););
clsRRAttributes attributes(DNS_RRTYPE_AAAA,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
bool bResult = ((p_IPAddress.isV6()) &&
(_writeMDNSHostDomain(m_pcHostName, false, 0, p_rSendParameter)) && // esp8266.local
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)), p_rSendParameter)) && // TTL
(_write16(clsConsts::u16IPv6Size, p_rSendParameter)) && // RDLength
(_udpAppendBuffer((uint8_t*)p_IPAddress.raw6(), clsConsts::u16IPv6Size)) && // RData
(p_rSendParameter.shiftOffset(clsConsts::u16IPv6Size)));
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_AAAA %s.local Type:%s Class:0x%04X TTL:%u %s\n"),
_DH(),
m_pcHostName,
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)),
p_IPAddress.toString().c_str());
);
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_AAAA: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeMDNSAnswer_PTR_IPv6
Write a MDNS reverse IPv6 PTR answer to the UDP output buffer.
See: '_writeMDNSAnswer_AAAA'
eg. xxxx::xx.ip6.arpa PTR 0x8001 120 15 esp8266.local
Used while answering reverse IPv6 questions
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_PTR_IPv6(IPAddress p_IPAddress,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_IPv6%s\n"), (p_rSendParameter.m_bCacheFlush ? "" : " nF")););
clsRRDomain reverseIPv6Domain;
clsRRAttributes attributes(DNS_RRTYPE_PTR,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
bool bResult = ((p_IPAddress.isV6()) &&
(_buildDomainForReverseIPv6(p_IPAddress, reverseIPv6Domain)) && // xxxx::xx.ip6.arpa
(_writeMDNSRRDomain(reverseIPv6Domain, p_rSendParameter)) &&
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)), p_rSendParameter)) && // TTL
(_writeMDNSHostDomain(m_pcHostName, true, 0, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local)
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_IPv6 "), _DH());
_printRRDomain(reverseIPv6Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:%s Class:0x%04X TTL:%u %s.local\n"),
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)),
m_pcHostName);
);
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_PTR_IPv6: FAILED!\n"), _DH()););
return bResult;
}
#endif
/*
MDNSResponder::_writeMDNSAnswer_SRV
eg. MyESP._http.tcp.local SRV 0x8001 120 0 0 60068 esp8266.local
http://www.zytrax.com/books/dns/ch8/srv.html ???? Include instance name ????
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_SRV(clsLEAMDNSHost::clsService& p_rService,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_SRV%s\n"), (p_rSendParameter.m_bCacheFlush ? "" : " nF")););
uint16_t u16CachedDomainOffset = (p_rSendParameter.m_bLegacyDNSQuery
? 0
: p_rSendParameter.findCachedDomainOffset((const void*)m_pcHostName, false));
clsRRAttributes attributes(DNS_RRTYPE_SRV,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
clsRRDomain hostDomain;
bool bResult = ((_writeMDNSServiceDomain(p_rService, true, false, 0, p_rSendParameter)) && // MyESP._http._tcp.local
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL/*Consts::u32ServiceTTL*/)), p_rSendParameter)) && // TTL
(!u16CachedDomainOffset
// No cache for domain name (or no compression allowed)
? ((_buildDomainForHost(m_pcHostName, hostDomain)) &&
(_write16((sizeof(uint16_t /*Prio*/) + // RDLength
sizeof(uint16_t /*Weight*/) +
sizeof(uint16_t /*Port*/) +
hostDomain.m_u16NameLength), p_rSendParameter)) && // Domain length
(_write16(clsConsts::u16SRVPriority, p_rSendParameter)) && // Priority
(_write16(clsConsts::u16SRVWeight, p_rSendParameter)) && // Weight
(_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port
(p_rSendParameter.addDomainCacheItem((const void*)m_pcHostName, false, p_rSendParameter.m_u16Offset)) &&
(_writeMDNSRRDomain(hostDomain, p_rSendParameter))) // Host, eg. esp8266.local
// Cache available for domain
: ((clsConsts::u8DomainCompressMark > ((u16CachedDomainOffset >> 8) & ~clsConsts::u8DomainCompressMark)) && // Valid offset
(_write16((sizeof(uint16_t /*Prio*/) + // RDLength
sizeof(uint16_t /*Weight*/) +
sizeof(uint16_t /*Port*/) +
2), p_rSendParameter)) && // Length of 'C0xx'
(_write16(clsConsts::u16SRVPriority, p_rSendParameter)) && // Priority
(_write16(clsConsts::u16SRVWeight, p_rSendParameter)) && // Weight
(_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port
(_write8(((u16CachedDomainOffset >> 8) | clsConsts::u8DomainCompressMark), p_rSendParameter)) && // Compression mark (and offset)
(_write8((uint8_t)u16CachedDomainOffset, p_rSendParameter))))); // Offset
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_SRV %s._%s._%s.local Type:%s Class:0x%04X TTL:%u %u %u %u %s.local\n"),
_DH(),
p_rService.m_pcInstanceName,
p_rService.m_pcType,
p_rService.m_pcProtocol,
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)),
clsConsts::u16SRVPriority,
clsConsts::u16SRVWeight,
p_rService.m_u16Port,
m_pcHostName);
);
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_SRV: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_createNSECBitmap
*/
clsLEAMDNSHost::clsNSECBitmap* clsLEAMDNSHost::_createNSECBitmap(uint32_t p_u32NSECContent)
{
// Currently 6 bytes (6*8 -> 0..47) are long enough, and only this is implemented
clsNSECBitmap* pNSECBitmap = new clsNSECBitmap;
if (pNSECBitmap)
{
#ifdef MDNS_IPV4_SUPPORT
if (p_u32NSECContent & static_cast<uint32_t>(enuContentFlag::A))
{
pNSECBitmap->setBit(DNS_RRTYPE_A); // 01/0x01
}
#endif
if ((p_u32NSECContent & static_cast<uint32_t>(enuContentFlag::PTR_IPv4)) ||
(p_u32NSECContent & static_cast<uint32_t>(enuContentFlag::PTR_IPv6)))
{
pNSECBitmap->setBit(DNS_RRTYPE_PTR); // 12/0x0C
}
#ifdef MDNS2_IPV6_SUPPORT
if (p_u32NSECContent & static_cast<uint32_t>(enuContentFlag::AAAA))
{
pNSECBitmap->setBit(DNS_RRTYPE_AAAA); // 28/0x1C
}
#endif
if (p_u32NSECContent & static_cast<uint32_t>(enuContentFlag::TXT))
{
pNSECBitmap->setBit(DNS_RRTYPE_TXT); // 16/0x10
}
if (p_u32NSECContent & static_cast<uint32_t>(enuContentFlag::SRV))
{
pNSECBitmap->setBit(DNS_RRTYPE_SRV); // 33/0x21
}
if (p_u32NSECContent & static_cast<uint32_t>(enuContentFlag::NSEC))
{
pNSECBitmap->setBit(clsConsts::u8DNS_RRTYPE_NSEC); // 47/0x2F
}
}
return pNSECBitmap;
}
/*
MDNSResponder::_writeMDNSNSECBitmap
*/
bool clsLEAMDNSHost::_writeMDNSNSECBitmap(const clsLEAMDNSHost::clsNSECBitmap& p_NSECBitmap,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
/* DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("_writeMDNSNSECBitmap: "));
for (uint16_t u=0; u<p_NSECBitmap.m_u16BitmapLength; ++u) {
DEBUG_OUTPUT.printf_P(PSTR("0x%02X "), p_NSECBitmap.m_pu8BitmapData[u]);
}
DEBUG_OUTPUT.printf_P(PSTR("\n"));
);*/
bool bResult = ((_write16(p_NSECBitmap.length(), p_rSendParameter)) &&
((_udpAppendBuffer(p_NSECBitmap.m_au8BitmapData, p_NSECBitmap.length())) &&
(p_rSendParameter.shiftOffset(p_NSECBitmap.length()))));
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSNSECBitmap: FAILED!\n"), _DH()););
return bResult;
}
/*
MDNSResponder::_writeMDNSAnswer_NSEC(host)
eg. esp8266.local NSEC 0x8001 120 XX esp8266.local xyz
http://www.zytrax.com/books/dns/ch8/nsec.html
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_NSEC(uint32_t p_u32NSECContent,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC (host: %s)%s\n"), _replyFlags2String(p_u32NSECContent), (p_rSendParameter.m_bCacheFlush ? "" : " nF")););
clsRRAttributes attributes(clsConsts::u8DNS_RRTYPE_NSEC,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
clsNSECBitmap* pNSECBitmap = _createNSECBitmap(p_u32NSECContent);
bool bResult = ((pNSECBitmap) && // NSEC bitmap created
(_writeMDNSHostDomain(m_pcHostName, false, 0, p_rSendParameter)) && // esp8266.local
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)), p_rSendParameter)) && // TTL
(_writeMDNSHostDomain(m_pcHostName, true, (2 + pNSECBitmap->length()), p_rSendParameter)) && // XX esp8266.local
(_writeMDNSNSECBitmap(*pNSECBitmap, p_rSendParameter))); // NSEC bitmap
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC %s.local Type:%s Class:0x%04X TTL:%u %s %s\n"),
_DH(),
m_pcHostName,
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)),
m_pcHostName,
_NSECBitmap2String(pNSECBitmap));
);
if (pNSECBitmap)
{
delete pNSECBitmap;
pNSECBitmap = 0;
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC (host): FAILED!\n"), _DH()););
return bResult;
}
#ifdef MDNS_IPV4_SUPPORT
/*
MDNSResponder::_writeMDNSAnswer_NSEC_PTR_IPv4(host)
eg. 012.789.456.123.in-addr.arpa NSEC 0x8001 120 XX 012.789.456.123.in-addr.arpa xyz
http://www.zytrax.com/books/dns/ch8/nsec.html
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_NSEC_PTR_IPv4(IPAddress p_IPAddress,
clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC_PTR_IPv4\n")););
clsRRAttributes attributes(clsConsts::u8DNS_RRTYPE_NSEC,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
clsNSECBitmap* pNSECBitmap = _createNSECBitmap(static_cast<uint32_t>(enuContentFlag::PTR_IPv4));
clsRRDomain reverseIPv4Domain;
bool bResult = ((p_IPAddress.isV4()) &&
(pNSECBitmap) && // NSEC bitmap created
(_buildDomainForReverseIPv4(p_IPAddress, reverseIPv4Domain)) && // 012.789.456.123.in-addr.arpa
(_writeMDNSRRDomain(reverseIPv4Domain, p_rSendParameter)) && // 012.789.456.123.in-addr.arpa
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)), p_rSendParameter)) && // TTL
(_write16((reverseIPv4Domain.m_u16NameLength + (2 + pNSECBitmap->length())), p_rSendParameter)) &&
(_writeMDNSRRDomain(reverseIPv4Domain, p_rSendParameter)) && // 012.789.456.123.in-addr.arpa
(_writeMDNSNSECBitmap(*pNSECBitmap, p_rSendParameter))); // NSEC bitmap
DEBUG_EX_INFO(if (bResult)
{
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC_PTR_IPv4 "), _DH());
_printRRDomain(reverseIPv4Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:%s Class:0x%04X TTL:%u "),
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32ServiceTTL)));
_printRRDomain(reverseIPv4Domain);
DEBUG_OUTPUT.printf_P(PSTR(" %s\n"), _NSECBitmap2String(pNSECBitmap));
});
if (pNSECBitmap)
{
delete pNSECBitmap;
pNSECBitmap = 0;
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC_PTR_IPv4 (host): FAILED!\n"), _DH()););
return bResult;
}
#endif
#ifdef MDNS2_IPV6_SUPPORT
/*
MDNSResponder::_writeMDNSAnswer_NSEC_PTR_IPv6(host)
eg. 9.0.0.0.0.0.0.0.0.0.0.0.0.7.8.5.6.3.4.1.2.ip6.arpa NSEC 0x8001 120 XX 9.0.0.0.0.0.0.0.0.0.0.0.0.7.8.5.6.3.4.1.2.ip6.arpa xyz
http://www.zytrax.com/books/dns/ch8/nsec.html
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_NSEC_PTR_IPv6(IPAddress p_IPAddress,
clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC_PTR_IPv6\n")););
clsRRAttributes attributes(clsConsts::u8DNS_RRTYPE_NSEC,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
clsNSECBitmap* pNSECBitmap = _createNSECBitmap(static_cast<uint32_t>(enuContentFlag::PTR_IPv6));
clsRRDomain reverseIPv6Domain;
bool bResult = ((p_IPAddress.isV6()) &&
(pNSECBitmap) && // NSEC bitmap created
(_buildDomainForReverseIPv6(p_IPAddress, reverseIPv6Domain)) && // 9.0.0.0.0.0.0.0.0.0.0.0.0.7.8.5.6.3.4.1.2.ip6.arpa
(_writeMDNSRRDomain(reverseIPv6Domain, p_rSendParameter)) && // 9.0.0.0.0.0.0.0.0.0.0.0.0.7.8.5.6.3.4.1.2.ip6.arpa
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32HostTTL)), p_rSendParameter)) && // TTL
(_write16((reverseIPv6Domain.m_u16NameLength + (2 + pNSECBitmap->length())), p_rSendParameter)) &&
(_writeMDNSRRDomain(reverseIPv6Domain, p_rSendParameter)) && // 9.0.0.0.0.0.0.0.0.0.0.0.0.7.8.5.6.3.4.1.2.ip6.arpa
(_writeMDNSNSECBitmap(*pNSECBitmap, p_rSendParameter))); // NSEC bitmap
DEBUG_EX_INFO(if (bResult)
{
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC_PTR_IPv6 "), _DH());
_printRRDomain(reverseIPv6Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:%s Class:0x%04X TTL:%u "),
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32ServiceTTL)));
_printRRDomain(reverseIPv6Domain);
DEBUG_OUTPUT.printf_P(PSTR(" %s\n"), _NSECBitmap2String(pNSECBitmap));
});
if (pNSECBitmap)
{
delete pNSECBitmap;
pNSECBitmap = 0;
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC_PTR_IPv6 (host): FAILED!\n"), _DH()););
return bResult;
}
#endif
/*
MDNSResponder::_writeMDNSAnswer_NSEC(service)
eg. MyESP._http.tcp.local NSEC 0x8001 4500 XX MyESP._http.tcp.local xyz
http://www.zytrax.com/books/dns/ch8/nsec.html
*/
bool clsLEAMDNSHost::_writeMDNSAnswer_NSEC(clsLEAMDNSHost::clsService& p_rService,
uint32_t p_u32NSECContent,
clsLEAMDNSHost::clsSendParameter& p_rSendParameter)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC (service: %s)\n"), _DH(), _replyFlags2String(p_u32NSECContent)););
clsRRAttributes attributes(clsConsts::u8DNS_RRTYPE_NSEC,
((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet
clsNSECBitmap* pNSECBitmap = _createNSECBitmap(p_u32NSECContent);
bool bResult = ((pNSECBitmap) && // NSEC bitmap created
(_writeMDNSServiceDomain(p_rService, true, false, 0, p_rSendParameter)) && // MyESP._http._tcp.local
(_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS
(_write32((p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32ServiceTTL)), p_rSendParameter)) && // TTL
(_writeMDNSServiceDomain(p_rService, true, true, (2 + pNSECBitmap->length()), p_rSendParameter)) && // XX MyESP._http._tcp.local
(_writeMDNSNSECBitmap(*pNSECBitmap, p_rSendParameter))); // NSEC bitmap
DEBUG_EX_INFO(if (bResult)
DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC %s._%s._%s.local Type:%s Class:0x%04X TTL:%u %s\n"),
_DH(),
p_rService.m_pcInstanceName,
p_rService.m_pcType,
p_rService.m_pcProtocol,
_RRType2Name(attributes.m_u16Type),
attributes.m_u16Class,
(p_rSendParameter.m_bUnannounce
? 0
: (p_rSendParameter.m_bLegacyDNSQuery ? clsConsts::u32LegacyTTL : clsConsts::u32ServiceTTL)),
_NSECBitmap2String(pNSECBitmap));
);
if (pNSECBitmap)
{
delete pNSECBitmap;
pNSECBitmap = 0;
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _writeMDNSAnswer_NSEC (service): FAILED!\n"), _DH()););
return bResult;
}
} // namespace MDNSImplementation
} // namespace esp8266