1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-07-23 08:45:22 +03:00
Files
esp8266/libraries/ESP8266mDNS/src/LEAmDNS2Host_Control.cpp
Labor-Et-Ars a3281fe2f3 LEA mDNS v2 (#7540)
* LEAmDNSv2
2020-09-25 11:12:39 +02:00

2220 lines
112 KiB
C++

/*
LEAmDNS2Host_Control.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 "LEAmDNS2Host.h"
#include "LEAmDNS2_Priv.h"
namespace esp8266
{
namespace experimental
{
/*
RECEIVING
*/
/*
clsLEAmDNS2_Host::_parseMessage
*/
bool clsLEAMDNSHost::_parseMessage()
{
DEBUG_EX_INFO(
unsigned long ulStartTime = millis();
unsigned uStartMemory = ESP.getFreeHeap();
DEBUG_OUTPUT.printf_P(PSTR("%s _parseMessage (Time: %lu ms, heap: %u bytes, from %s, to %s)\n"), _DH(), ulStartTime, uStartMemory,
m_pUDPContext->getRemoteAddress().toString().c_str(),
m_pUDPContext->getDestAddress().toString().c_str());
);
//DEBUG_EX_INFO(_udpDump(););
netif* pNetIf = m_pUDPContext->getInputNetif();
bool bResult = false;
clsMsgHeader header;
if (_readMDNSMsgHeader(header))
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseMessage: 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)header.m_u16ID,
(unsigned)header.m_1bQR, (unsigned)header.m_4bOpcode, (unsigned)header.m_1bAA, (unsigned)header.m_1bTC, (unsigned)header.m_1bRD,
(unsigned)header.m_1bRA, (unsigned)header.m_4bRCode,
(unsigned)header.m_u16QDCount,
(unsigned)header.m_u16ANCount,
(unsigned)header.m_u16NSCount,
(unsigned)header.m_u16ARCount));
if (0 == header.m_4bOpcode)
{
// A standard query
if (header.m_1bQR)
{
// Received a response -> answers to a query
//DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseMessage: Reading answers: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), _DH(), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount););
bResult = _parseResponse(pNetIf, header);
}
else
{
// Received a query (Questions)
//DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseMessage: Reading query: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), _DH(), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount););
bResult = _parseQuery(m_pUDPContext->getInputNetif(), header);
}
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseMessage: Received UNEXPECTED opcode:%u. Ignoring message!\n"), _DH(), header.m_4bOpcode););
m_pUDPContext->flush();
}
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseMessage: FAILED to read header\n"), _DH()););
m_pUDPContext->flush();
}
DEBUG_EX_INFO(
unsigned uFreeHeap = ESP.getFreeHeap();
DEBUG_OUTPUT.printf_P(PSTR("%s _parseMessage: Done (%s after %lu ms, ate %i bytes, remaining %u)\n\n"), _DH(), (bResult ? "Succeeded" : "FAILED"), (millis() - ulStartTime), (uStartMemory - uFreeHeap), uFreeHeap);
);
return bResult;
}
/*
clsLEAmDNS2_Host::_parseQuery
Queries are of interest in two cases:
1. allow for tiebreaking while probing in the case of a race condition between two instances probing for
the same name at the same time
2. provide answers to questions for our host domain or any presented service
When reading the questions, a set of (planned) responses is created, eg. a reverse PTR question for the host domain
gets an A (IP address) response, a PTR question for the _services._dns-sd domain gets a PTR (type) response for any
registered service, ...
As any mDNS responder should be able to handle 'legacy' queries (from DNS clients), this case is handled here also.
Legacy queries have got only one (unicast) question and are directed to the local DNS port (not the multicast port).
*/
bool clsLEAMDNSHost::_parseQuery(netif* pNetIf,
const clsLEAMDNSHost::clsMsgHeader& p_MsgHeader)
{
bool bResult = true;
clsSendParameter sendParameter;
uint32_t u32HostOrServiceReplies = 0;
bool bHostOrServiceTiebreakNeeded = false;
for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd)
{
clsRRQuestion questionRR;
if ((bResult = _readRRQuestion(questionRR)))
{
// Define host replies, BUT only answer queries after probing is done
u32HostOrServiceReplies =
sendParameter.m_u32HostReplyMask |= ((probeStatus())
? _replyMaskForHost(pNetIf, questionRR.m_Header, 0)
: 0);
DEBUG_EX_INFO(if (u32HostOrServiceReplies) DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Host reply needed %s\n"), _DH(), _replyFlags2String(u32HostOrServiceReplies)););
// Check tiebreak need for host domain
if (clsProbeInformation_Base::clsProbeInformation_Base::enuProbingStatus::InProgress == m_ProbeInformation.m_ProbingStatus)
{
bool bFullNameMatch = false;
if ((_replyMaskForHost(pNetIf, questionRR.m_Header, &bFullNameMatch)) &&
(bFullNameMatch))
{
// We're in 'probing' state and someone is asking for our host domain: this might be
// a race-condition: Two hosts with the same domain names try simutanously to probe their domains
// See: RFC 6762, 8.2 (Tiebraking)
// However, we're using a max. reduced approach for tiebreaking here: The higher IP-address wins!
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Possible race-condition for host domain detected while probing.\n"), _DH()););
bHostOrServiceTiebreakNeeded =
m_ProbeInformation.m_bTiebreakNeeded = true;
}
}
// Define service replies
for (clsService::list::iterator it = m_Services.begin(); ((bResult) && (it != m_Services.end())); it++)
{
clsService* pService = *it;
// Define service replies, BUT only answer queries after probing is done
uint32_t u32ReplyMaskForQuestion = ((pService->probeStatus())
? _replyMaskForService(questionRR.m_Header, *pService, 0)
: 0);
u32HostOrServiceReplies |= (pService->m_u32ReplyMask |= u32ReplyMaskForQuestion);
DEBUG_EX_INFO(if (u32ReplyMaskForQuestion) DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Service reply needed: %s\n"), _DH(pService), _replyFlags2String(u32ReplyMaskForQuestion)););
// Check tiebreak need for service domain
if (clsProbeInformation_Base::clsProbeInformation_Base::enuProbingStatus::InProgress == pService->m_ProbeInformation.m_ProbingStatus)
{
bool bFullNameMatch = false;
if ((_replyMaskForService(questionRR.m_Header, *pService, &bFullNameMatch)) &&
(bFullNameMatch))
{
// We're in 'probing' state and someone is asking for this service domain: this might be
// a race-condition: Two services with the same domain names try simutanously to probe their domains
// See: RFC 6762, 8.2 (Tiebraking)
// However, we're using a max. reduced approach for tiebreaking here: The 'higher' SRV host wins!
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Possible race-condition for service domain detected while probing.\n"), _DH(pService)););
bHostOrServiceTiebreakNeeded =
pService->m_ProbeInformation.m_bTiebreakNeeded = true;
}
}
}
// Handle unicast and legacy specialities
// If only one question asks for unicast reply, the whole reply packet is send unicast
if (((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) || // Unicast (maybe legacy) query OR
(questionRR.m_bUnicast)) && // Expressivly unicast query
(!sendParameter.m_bUnicast))
{
sendParameter.m_bUnicast = true;
//sendParameter.m_bCacheFlush = false;
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Unicast response asked for %s!\n"), _DH(), m_pUDPContext->getRemoteAddress().toString().c_str()););
//Serial.printf_P(PSTR("%s _parseQuery: Ignored Unicast response asked for by %s!\n"), _DH(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());
if ((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) && // Unicast (maybe legacy) query AND
(1 == p_MsgHeader.m_u16QDCount) && // Only one question AND
((sendParameter.m_u32HostReplyMask) || // Host replies OR
(u32HostOrServiceReplies))) // Host or service replies available
{
// Local host check
// We're a match for this legacy query, BUT
// make sure, that the query comes from a local host
if ((m_pUDPContext) &&
#ifdef MDNS_IPV4_SUPPORT
(m_pUDPContext->getRemoteAddress().isV4()) &&
(ip4_addr_netcmp(ip_2_ip4((const ip_addr_t*)m_pUDPContext->getRemoteAddress()),
ip_2_ip4(&m_pUDPContext->getInputNetif()->ip_addr),
ip_2_ip4(&m_pUDPContext->getInputNetif()->netmask)))
#else
(true)
#endif
&&
#ifdef MDNS_IPV6_SUPPORT
(m_pUDPContext->getRemoteAddress().isV6()) &&
(ip6_addr_islinklocal(ip_2_ip6((const ip_addr_t*)m_pUDPContext->getRemoteAddress())))
#else
(true)
#endif
)
{
DEBUG_EX_RX(DEBUG_OUTPUT.println("\n\n\nUNICAST QUERY\n\n"));
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Legacy DNS query from local host %s!\n"), _DH(), m_pUDPContext->getRemoteAddress().toString().c_str()););
sendParameter.m_u16ID = p_MsgHeader.m_u16ID;
sendParameter.m_bLegacyDNSQuery = true;
sendParameter.m_bCacheFlush = false;
clsRRQuestion* pNewRRQuestion = new clsRRQuestion;
if (pNewRRQuestion)
{
pNewRRQuestion->m_Header.m_Domain = questionRR.m_Header.m_Domain;
pNewRRQuestion->m_Header.m_Attributes.m_u16Type = questionRR.m_Header.m_Attributes.m_u16Type;
pNewRRQuestion->m_Header.m_Attributes.m_u16Class = questionRR.m_Header.m_Attributes.m_u16Class;
sendParameter.m_RRQuestions.push_back(pNewRRQuestion);
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: FAILED to add legacy DNS question!\n"), _DH()););
}
}
else
{
DEBUG_EX_RX(DEBUG_OUTPUT.printf("\n\n\nINVALID UNICAST QUERY from %s\n\n\n", m_pUDPContext->getRemoteAddress().toString().c_str()));
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Legacy DNS query from NON-LOCAL host at %s!\n"), _DH(), m_pUDPContext->getRemoteAddress().toString().c_str()););
bResult = false;
}
}
}
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: FAILED to read question!\n"), _DH()););
}
} // for questions
//DEBUG_EX_INFO(if (u8HostOrServiceReplies) DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Reply needed: %u (%s: %s->%s)\n"), _DH(), u8HostOrServiceReplies, clsTimeSyncer::timestr(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), IPAddress(m_pUDPContext->getDestAddress()).toString().c_str()););
//bHostOrServiceTiebreakNeeded = false;
// Handle known answers
uint32_t u32Answers = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount);
if (((u32HostOrServiceReplies) ||
(bHostOrServiceTiebreakNeeded)) &&
(u32Answers))
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Reading known answers(%u):\n"), _DH(), u32Answers););
for (uint32_t an = 0; ((bResult) && (an < u32Answers)); ++an)
{
clsRRAnswer* pKnownRRAnswer = 0;
if (((bResult = _readRRAnswer(pKnownRRAnswer))) &&
(pKnownRRAnswer))
{
if ((DNS_RRTYPE_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Type) && // No ANY type answer
(DNS_RRCLASS_ANY != (pKnownRRAnswer->m_Header.m_Attributes.m_u16Class & (~0x8000)))) // No ANY class answer
{
/* - RFC6762 7.1 Suppression only for 'Shared Records' -
// Find match between planned answer (sendParameter.m_u8HostReplyMask) and this 'known answer'
uint32_t u32HostMatchMask = (sendParameter.m_u32HostReplyMask & _replyMaskForHost(pNetIf, pKnownRRAnswer->m_Header));
if ((u32HostMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND
((Consts::u32HostTTL / 2) <= pKnownRRAnswer->m_u32TTL)) // The TTL of the known answer is longer than half of the new host TTL (120s)
{
// Compare contents
if (enuAnswerType::PTR == pKnownRRAnswer->answerType())
{
stcRRDomain hostDomain;
if ((_buildDomainForHost(m_pcHostName, hostDomain)) &&
(((stcRRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain == hostDomain))
{
// Host domain match
#ifdef MDNS_IPV4_SUPPORT
if (u32HostMatchMask & static_cast<uint32_t>(enuContentFlag::PTR_IPv4))
{
// IPv4 PTR was asked for, but is already known -> skipping
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: IPv4 PTR already known (TTL:%u)... skipping!\n"), _DH(), pKnownRRAnswer->m_u32TTL););
sendParameter.m_u32HostReplyMask &= ~static_cast<uint32_t>(enuContentFlag::PTR_IPv4);
}
#endif
#ifdef MDNS_IPV6_SUPPORT
if (u32HostMatchMask & static_cast<uint32_t>(enuContentFlag::PTR_IPv6))
{
// IPv6 PTR was asked for, but is already known -> skipping
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: IPv6 PTR already known (TTL:%u)... skipping!\n"), _DH(), pKnownRRAnswer->m_u32TTL););
sendParameter.m_u32HostReplyMask &= ~static_cast<uint32_t>(enuContentFlag::PTR_IPv6);
}
#endif
}
}
else if (u32HostMatchMask & static_cast<uint32_t>(enuContentFlag::A))
{
// IPv4 address was asked for
#ifdef MDNS_IPV4_SUPPORT
if ((enuAnswerType::A == pKnownRRAnswer->answerType()) &&
(((stcRRAnswerA*)pKnownRRAnswer)->m_IPAddress == _getResponderIPAddress(pNetIf, enuIPProtocolType::V4)))
{
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: IPv4 address already known (TTL:%u)... skipping!\n"), _DH(), pKnownRRAnswer->m_u32TTL););
sendParameter.m_u32HostReplyMask &= ~static_cast<uint32_t>(enuContentFlag::A);
} // else: RData NOT IPv4 length !!
#endif
}
else if (u32HostMatchMask & static_cast<uint32_t>(enuContentFlag::AAAA))
{
// IPv6 address was asked for
#ifdef MDNS_IPV6_SUPPORT
if ((enuAnswerType::AAAA == pKnownRRAnswer->answerType()) &&
(((stcRRAnswerAAAA*)pKnownRRAnswer)->m_IPAddress == _getResponderIPAddress(pNetIf, enuIPProtocolType::V6)))
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: IPv6 address already known... skipping!\n"), _DH()););
sendParameter.m_u32HostReplyMask &= ~static_cast<uint32_t>(enuContentFlag::AAAA);
} // else: RData NOT IPv6 length !!
#endif
}
} // Host match and TTL
*/
//
// Check host tiebreak possibility
if (m_ProbeInformation.m_bTiebreakNeeded)
{
clsRRDomain hostDomain;
if ((_buildDomainForHost(m_pcHostName, hostDomain)) &&
(pKnownRRAnswer->m_Header.m_Domain == hostDomain))
{
// Host domain match
#ifdef MDNS_IPV4_SUPPORT
if (enuAnswerType::A == pKnownRRAnswer->answerType())
{
// CHECK
IPAddress localIPAddress(_getResponderIPAddress(pNetIf, enuIPProtocolType::V4));
if (((clsRRAnswerA*)pKnownRRAnswer)->m_IPAddress == localIPAddress)
{
// SAME IP address -> We've received an old message from ourselfs (same IP)
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Tiebreak (IPv4) WON (was an old message)!\n"), _DH()););
m_ProbeInformation.m_bTiebreakNeeded = false;
}
else
{
if ((uint32_t)(((clsRRAnswerA*)pKnownRRAnswer)->m_IPAddress) > (uint32_t)localIPAddress) // The OTHER IP is 'higher' -> LOST
{
// LOST tiebreak
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Tiebreak (IPv4) LOST (lower IPv4)!\n"), _DH()););
_cancelProbingForHost();
m_ProbeInformation.m_bTiebreakNeeded = false;
}
else
{
// WON tiebreak
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Tiebreak (IPv4) WON (higher IPv4)!\n"), _DH()););
m_ProbeInformation.m_bTiebreakNeeded = false;
}
}
}
#endif
#ifdef MDNS_IPV6_SUPPORT
if (enuAnswerType::AAAA == pKnownRRAnswer->answerType())
{
IPAddress localIPAddress(_getResponderIPAddress(pNetIf, enuIPProtocolType::V6));
if (((clsRRAnswerAAAA*)pKnownRRAnswer)->m_IPAddress == localIPAddress)
{
// SAME IP address -> We've received an old message from ourselfs (same IP)
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Tiebreak (IPv6) WON (was an old message)!\n"), _DH()););
m_ProbeInformation.m_bTiebreakNeeded = false;
}
else
{
// memcmp delivers >0 (positive) if the first non-matching byte in A is higher than in B
if (0 < memcmp((((clsRRAnswerAAAA*)pKnownRRAnswer)->m_IPAddress).raw6(), localIPAddress.raw6(), clsConsts::u16IPv6Size)) // The OTHER IP is 'higher' -> LOST
{
// LOST tiebreak
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Tiebreak (IPv6) LOST (lower IPv6)!\n"), _DH()););
_cancelProbingForHost();
m_ProbeInformation.m_bTiebreakNeeded = false;
}
else
{
// WON tiebreak
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Tiebreak (IPv6) WON (higher IPv6)!\n"), _DH()););
m_ProbeInformation.m_bTiebreakNeeded = false;
}
}
}
#endif
}
} // Host tiebreak possibility
// Check service answers
for (clsService::list::iterator it = m_Services.begin(); ((bResult) && (it != m_Services.end())); it++)
{
clsService* pService = *it;
uint32_t u32ServiceMatchMask = (pService->m_u32ReplyMask & _replyMaskForService(pKnownRRAnswer->m_Header, *pService));
if ((u32ServiceMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND
((clsConsts::u32ServiceTTL / 2) <= pKnownRRAnswer->m_u32TTL)) // The TTL of the known answer is longer than half of the new service TTL (4500s)
{
if (enuAnswerType::PTR == pKnownRRAnswer->answerType())
{
clsRRDomain serviceDomain;
if ((u32ServiceMatchMask & static_cast<uint32_t>(enuContentFlag::PTR_TYPE)) &&
(_buildDomainForService(*pService, false, serviceDomain)) &&
(serviceDomain == ((clsRRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain))
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Service type PTR already known (TTL:%u)... skipping!\n"), _DH(pService), pKnownRRAnswer->m_u32TTL););
pService->m_u32ReplyMask &= ~static_cast<uint32_t>(enuContentFlag::PTR_TYPE);
}
if ((u32ServiceMatchMask & static_cast<uint32_t>(enuContentFlag::PTR_NAME)) &&
(_buildDomainForService(*pService, true, serviceDomain)) &&
(serviceDomain == ((clsRRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain))
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Service name PTR already known (TTL:%u)... skipping!\n"), _DH(pService), pKnownRRAnswer->m_u32TTL););
pService->m_u32ReplyMask &= ~static_cast<uint32_t>(enuContentFlag::PTR_NAME);
}
}
/* - RFC6762 7.1 Suppression only for 'Shared Records'
else if (u32ServiceMatchMask & static_cast<uint32_t>(enuContentFlag::SRV))
{
DEBUG_EX_ERR(if (enuAnswerType::SRV != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: ERROR! INVALID answer type (SRV)!\n"), _DH(pService)););
stcRRDomain hostDomain;
if ((_buildDomainForHost(m_pcHostName, hostDomain)) &&
(hostDomain == ((stcRRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) // Host domain match
{
if ((Consts::u16SRVPriority == ((stcRRAnswerSRV*)pKnownRRAnswer)->m_u16Priority) &&
(Consts::u16SRVWeight == ((stcRRAnswerSRV*)pKnownRRAnswer)->m_u16Weight) &&
(pService->m_u16Port == ((stcRRAnswerSRV*)pKnownRRAnswer)->m_u16Port))
{
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Service SRV answer already known (TTL:%u)... skipping!\n"), _DH(pService), pKnownRRAnswer->m_u32TTL););
pService->m_u32ReplyMask &= ~static_cast<uint32_t>(enuContentFlag::SRV);
} // else: Small differences -> send update message
}
}*/
/* - RFC6762 7.1 Suppression only for 'Shared Records'
else if (u32ServiceMatchMask & static_cast<uint32_t>(enuContentFlag::TXT))
{
DEBUG_EX_ERR(if (enuAnswerType::TXT != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: ERROR! INVALID answer type (TXT)!\n"), _DH(pService)););
_collectServiceTxts(*pService);
if (pService->m_Txts == ((stcRRAnswerTXT*)pKnownRRAnswer)->m_Txts)
{
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Service TXT answer already known (TTL:%u)... skipping!\n"), _DH(pService), pKnownRRAnswer->m_u32TTL););
pService->m_u32ReplyMask &= ~static_cast<uint32_t>(enuContentFlag::TXT);
}
_releaseTempServiceTxts(*pService);
}*/
} // Service match and enough TTL
//
// Check service tiebreak possibility
if (pService->m_ProbeInformation.m_bTiebreakNeeded)
{
clsRRDomain serviceDomain;
if ((_buildDomainForService(*pService, true, serviceDomain)) &&
(pKnownRRAnswer->m_Header.m_Domain == serviceDomain))
{
// Service domain match
if (enuAnswerType::SRV == pKnownRRAnswer->answerType())
{
clsRRDomain hostDomain;
if ((_buildDomainForHost(m_pcHostName, hostDomain)) &&
(hostDomain == ((clsRRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) // Host domain match
{
// We've received an old message from ourselfs (same SRV)
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Tiebreak (SRV) won (was an old message)!\n"), _DH(pService)););
pService->m_ProbeInformation.m_bTiebreakNeeded = false;
}
else
{
if (((clsRRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain > hostDomain) // The OTHER domain is 'higher' -> LOST
{
// LOST tiebreak
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Tiebreak (SRV) LOST (lower)!\n"), _DH(pService)););
_cancelProbingForService(*pService);
pService->m_ProbeInformation.m_bTiebreakNeeded = false;
}
else
{
// WON tiebreak
DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Tiebreak (SRV) won (higher)!\n"), _DH(pService)););
pService->m_ProbeInformation.m_bTiebreakNeeded = false;
}
}
}
}
} // service tiebreak possibility
} // for services
} // ANY answers
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: FAILED to read known answer!\n"), _DH()););
}
if (pKnownRRAnswer)
{
delete pKnownRRAnswer;
pKnownRRAnswer = 0;
}
} // for answers
}
else
{
DEBUG_EX_INFO(if (u32Answers) DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Skipped %u known answers!\n"), _DH(), u32Answers););
m_pUDPContext->flush();
}
if (bResult)
{
// Check, if a reply is needed
uint32_t u32ReplyNeeded = sendParameter.m_u32HostReplyMask;
for (const clsService* pService : m_Services)
{
u32ReplyNeeded |= pService->m_u32ReplyMask;
}
if (u32ReplyNeeded)
{
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Sending %s answer(%s)...\n"), _DH(), (sendParameter.m_bUnicast ? "UC" : "MC"), _replyFlags2String(u32ReplyNeeded)););
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Sending %s answer(%s)...\n"), _DH(), (sendParameter.m_bUnicast ? "UC" : "MC"), _replyFlags2String(u32ReplyNeeded)););
sendParameter.m_Response = clsSendParameter::enuResponseType::Response;
sendParameter.m_bAuthorative = true;
bResult = _sendMessage(pNetIf, sendParameter);
}
DEBUG_EX_INFO(else
{
DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: No reply needed\n"), _DH());
});
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: Something FAILED!\n"), _DH()););
m_pUDPContext->flush();
}
//
// Check and reset tiebreak-states
if (m_ProbeInformation.m_bTiebreakNeeded)
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: UNSOLVED tiebreak-need for host domain!\n"), _DH()););
m_ProbeInformation.m_bTiebreakNeeded = false;
}
for (clsService* pService : m_Services)
{
if (pService->m_ProbeInformation.m_bTiebreakNeeded)
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: UNSOLVED tiebreak-need for service domain '%s')\n"), _DH(), _service2String(pService)););
pService->m_ProbeInformation.m_bTiebreakNeeded = false;
}
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _parseQuery: FAILED!\n"), _DH()););
return bResult;
}
/*
clsLEAmDNS2_Host::_parseResponse
Responses are of interest in two cases:
1. find domain name conflicts while probing
2. get answers to service queries
In both cases any included questions are ignored
1. If any answer has a domain name similar to one of the domain names we're planning to use (and are probing for),
then we've got a 'probing conflict'. The conflict has to be solved on our side of the conflict (eg. by
setting a new hostname and restart probing). The callback 'm_fnProbeResultCallback' is called with
'p_bProbeResult=false' in this case.
2. Service queries like '_http._tcp.local' will (if available) produce PTR, SRV, TXT and A/AAAA answers.
All stored answers are pivoted by the service instance name (from the PTR record). Other answer parts,
like host domain or IP address are than attached to this element.
Any answer part carries a TTL, this is also stored (incl. the reception time); if the TTL is '0' the
answer (part) is withdrawn by the sender and should be removed from any cache. RFC 6762, 10.1 proposes to
set the caches TTL-value to 1 second in such a case and to delete the item only, if no update has
has taken place in this second.
Answer parts may arrive in 'unsorted' order, so they are grouped into three levels:
Level 1: PRT - names the service instance (and is used as pivot), voids all other parts if is withdrawn or outdates
Level 2: SRV - links the instance name to a host domain and port, voids A/AAAA parts if is withdrawn or outdates
TXT - links the instance name to services TXTs
Level 3: A/AAAA - links the host domain to an IP address
*/
bool clsLEAMDNSHost::_parseResponse(netif* pNetIf, const clsLEAMDNSHost::clsMsgHeader& p_MsgHeader)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse\n")););
//DEBUG_EX_INFO(_udpDump(););
bool bResult = false;
// A response should be the result of a query or a probe
if ((_hasQueriesWaitingForAnswers()) || // Waiting for query answers OR
(_hasProbesWaitingForAnswers())) // Probe responses
{
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse: Received a response\n"), _DH());
//_udpDump();
);
bResult = true;
//
// Ignore questions here
clsRRQuestion dummyRRQ;
for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd)
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse: Received a response containing a question... ignoring!\n"), _DH()););
bResult = _readRRQuestion(dummyRRQ);
} // for queries
//
// Read and collect answers
clsRRAnswer* pCollectedRRAnswers = 0;
uint32_t u32NumberOfAnswerRRs = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount);
for (uint32_t an = 0; ((bResult) && (an < u32NumberOfAnswerRRs)); ++an)
{
clsRRAnswer* pRRAnswer = 0;
if (((bResult = _readRRAnswer(pRRAnswer))) &&
(pRRAnswer))
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse: ADDING answer!\n")););
pRRAnswer->m_pNext = pCollectedRRAnswers;
pCollectedRRAnswers = pRRAnswer;
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse: FAILED to read answer!\n"), _DH()););
if (pRRAnswer)
{
delete pRRAnswer;
pRRAnswer = 0;
}
bResult = false;
}
} // for answers
//
// Process answers
if (bResult)
{
bResult = ((!pCollectedRRAnswers) ||
(_processAnswers(pNetIf, pCollectedRRAnswers)));
}
else // Some failure while reading answers
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse: FAILED to read answers!\n"), _DH()););
m_pUDPContext->flush();
}
// Delete collected answers
while (pCollectedRRAnswers)
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse: DELETING answer!\n"), _DH()););
clsRRAnswer* pNextAnswer = pCollectedRRAnswers->m_pNext;
delete pCollectedRRAnswers;
pCollectedRRAnswers = pNextAnswer;
}
}
else // Received an unexpected response -> ignore
{
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse: Received an unexpected response... ignoring!\n"), _DH());
/*
DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse: Received an unexpected response... ignoring!\nDUMP:\n"), _DH());
bool bDumpResult = true;
for (uint16_t qd=0; ((bDumpResult) && (qd<p_MsgHeader.m_u16QDCount)); ++qd) {
stcRRQuestion questionRR;
bDumpResult = _readRRQuestion(questionRR);
} // for questions
// Handle known answers
uint32_t u32Answers = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount);
for (uint32_t an=0; ((bDumpResult) && (an<u32Answers)); ++an) {
stcRRAnswer* pRRAnswer = 0;
bDumpResult = _readRRAnswer(pRRAnswer);
if (pRRAnswer) {
delete pRRAnswer;
pRRAnswer = 0;
}
}
*/
);
m_pUDPContext->flush();
bResult = true;
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _parseResponse: FAILED!\n"), _DH()););
return bResult;
}
/*
clsLEAmDNS2_Host::_processAnswers
Host:
A (0x01): eg. esp8266.local A OP TTL 123.456.789.012
AAAA (01Cx): eg. esp8266.local AAAA OP TTL 1234:5678::90
PTR (0x0C, IPv4): eg. 012.789.456.123.in-addr.arpa PTR OP TTL esp8266.local
PTR (0x0C, IPv6): eg. 90.0.0.0.0.0.0.0.0.0.0.0.78.56.34.12.ip6.arpa PTR OP TTL esp8266.local
Service:
PTR (0x0C, srv name): eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local
PTR (0x0C, srv type): eg. _services._dns-sd._udp.local PTR OP TTL _http._tcp.local
SRV (0x21): eg. MyESP._http._tcp.local SRV OP TTL PRIORITY WEIGHT PORT esp8266.local
TXT (0x10): eg. MyESP._http._tcp.local TXT OP TTL c#=1
*/
bool clsLEAMDNSHost::_processAnswers(netif* pNetIf, const clsLEAMDNSHost::clsRRAnswer* p_pAnswers)
{
bool bResult = false;
if (p_pAnswers)
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _processAnswers: Processing answers...\n"), _DH()););
bResult = true;
// Answers may arrive in an unexpected order. So we loop our answers as long, as we
// can connect new information to service queries
bool bFoundNewKeyAnswer;
do
{
bFoundNewKeyAnswer = false;
const clsRRAnswer* pRRAnswer = p_pAnswers;
while ((pRRAnswer) &&
(bResult))
{
// 1. level answer (PTR)
if (enuAnswerType::PTR == pRRAnswer->answerType())
{
// eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local
bResult = _processPTRAnswer((clsRRAnswerPTR*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new SRV or TXT answers to be linked to queries
}
// 2. level answers
// SRV -> host domain and port
else if (enuAnswerType::SRV == pRRAnswer->answerType())
{
// eg. MyESP._http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local
bResult = _processSRVAnswer((clsRRAnswerSRV*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new A/AAAA answers to be linked to queries
}
// TXT -> Txts
else if (enuAnswerType::TXT == pRRAnswer->answerType())
{
// eg. MyESP_http._tcp.local TXT xxxx xx c#=1
bResult = _processTXTAnswer((clsRRAnswerTXT*)pRRAnswer);
}
// 3. level answers
#ifdef MDNS_IPV4_SUPPORT
// A -> IPv4Address
else if (enuAnswerType::A == pRRAnswer->answerType())
{
// eg. esp8266.local A xxxx xx 192.168.2.120
bResult = _processAAnswer((clsRRAnswerA*)pRRAnswer);
}
#endif
#ifdef MDNS_IPV6_SUPPORT
// AAAA -> IPv6Address
else if (enuAnswerType::AAAA == pRRAnswer->answerType())
{
// eg. esp8266.local AAAA xxxx xx 09cf::0c
bResult = _processAAAAAnswer((clsRRAnswerAAAA*)pRRAnswer);
}
#endif
// Finally check for probing conflicts
// Host domain
if ((clsProbeInformation_Base::enuProbingStatus::InProgress == m_ProbeInformation.m_ProbingStatus) &&
((enuAnswerType::A == pRRAnswer->answerType()) ||
(enuAnswerType::AAAA == pRRAnswer->answerType())))
{
clsRRDomain hostDomain;
if ((_buildDomainForHost(m_pcHostName, hostDomain)) &&
(pRRAnswer->m_Header.m_Domain == hostDomain))
{
bool bPossibleEcho = false;
#ifdef MDNS_IPV4_SUPPORT
if ((enuAnswerType::A == pRRAnswer->answerType()) &&
(((clsRRAnswerA*)pRRAnswer)->m_IPAddress == _getResponderIPAddress(pNetIf, enuIPProtocolType::V4)))
{
bPossibleEcho = true;
}
#endif
#ifdef MDNS_IPV6_SUPPORT
if ((enuAnswerType::AAAA == pRRAnswer->answerType()) &&
(((clsRRAnswerAAAA*)pRRAnswer)->m_IPAddress == _getResponderIPAddress(pNetIf, enuIPProtocolType::V6)))
{
bPossibleEcho = true;
}
#endif
if (!bPossibleEcho)
{
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _processAnswers: Probing CONFLICT found with '%s.local'\n"), _DH(), m_pcHostName););
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _processAnswers: Probing CONFLICT found with '%s.local'\n"), _DH(), m_pcHostName););
_cancelProbingForHost();
}
else
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _processAnswers: Ignoring CONFLICT found with '%s.local' as echo!\n"), _DH(), m_pcHostName););
}
}
}
// Service domains
for (clsService* pService : m_Services)
{
if ((clsProbeInformation_Base::enuProbingStatus::InProgress == pService->m_ProbeInformation.m_ProbingStatus) &&
((enuAnswerType::TXT == pRRAnswer->answerType()) ||
(enuAnswerType::SRV == pRRAnswer->answerType())))
{
clsRRDomain serviceDomain;
if ((_buildDomainForService(*pService, true, serviceDomain)) &&
(pRRAnswer->m_Header.m_Domain == serviceDomain))
{
// TODO: Echo management needed?
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _processAnswers: Probing CONFLICT found with '%s'\n"), _DH(), _service2String(pService)););
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _processAnswers: Probing CONFLICT found with '%s'\n"), _DH(), _service2String(pService)););
_cancelProbingForService(*pService);
}
}
}
pRRAnswer = pRRAnswer->m_pNext; // Next collected answer
} // while (answers)
} while ((bFoundNewKeyAnswer) &&
(bResult));
} // else: No answers provided
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _processAnswers: FAILED!\n"), _DH()););
return bResult;
}
/*
clsLEAmDNS2_Host::_processPTRAnswer (level 1)
*/
bool clsLEAMDNSHost::_processPTRAnswer(const clsLEAMDNSHost::clsRRAnswerPTR* p_pPTRAnswer,
bool& p_rbFoundNewKeyAnswer)
{
bool bResult = false;
if ((bResult = (0 != p_pPTRAnswer)))
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _processPTRAnswer: Processing PTR answers...\n"), _DH()););
// eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local
// Check pending service queries for eg. '_http._tcp'
clsQuery* pQuery = _findNextQueryByDomain(p_pPTRAnswer->m_Header.m_Domain, clsQuery::enuQueryType::Service, 0);
while (pQuery)
{
if (pQuery->m_bAwaitingAnswers)
{
// Find answer for service domain (eg. MyESP._http._tcp.local)
clsQuery::clsAnswer* pSQAnswer = pQuery->findAnswerForServiceDomain(p_pPTRAnswer->m_PTRDomain);
if (pSQAnswer)
{
// existing answer
if (p_pPTRAnswer->m_u32TTL)
{
// Received update message
pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); // Update TTL tag
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processPTRAnswer: Updated TTL(%lu) for "), _DH(), p_pPTRAnswer->m_u32TTL);
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR("\n"));
);
}
else
{
// received goodbye-message
pSQAnswer->m_TTLServiceDomain.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processPTRAnswer: 'Goodbye' received for "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR("\n"));
);
}
}
else if ((p_pPTRAnswer->m_u32TTL) && // Not just a goodbye-message
((pSQAnswer = new clsQuery::clsAnswer))) // Not yet included -> add answer
{
pSQAnswer->m_ServiceDomain = p_pPTRAnswer->m_PTRDomain;
pSQAnswer->m_QueryAnswerFlags |= static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::ServiceDomain);
pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL);
//pSQAnswer->releaseServiceDomain();
bResult = pQuery->addAnswer(pSQAnswer);
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processPTRAnswer: Added service domain to answer: "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.println();
);
p_rbFoundNewKeyAnswer = true;
_executeQueryCallback(*pQuery, *pSQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::ServiceDomain), true);
}
}
pQuery = _findNextQueryByDomain(p_pPTRAnswer->m_Header.m_Domain, clsQuery::enuQueryType::Service, pQuery);
}
} // else: No p_pPTRAnswer
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _processPTRAnswer: FAILED!\n"), _DH()););
return bResult;
}
/*
clsLEAmDNS2_Host::_processSRVAnswer (level 2)
*/
bool clsLEAMDNSHost::_processSRVAnswer(const clsLEAMDNSHost::clsRRAnswerSRV* p_pSRVAnswer,
bool& p_rbFoundNewKeyAnswer)
{
bool bResult = false;
if ((bResult = (0 != p_pSRVAnswer)))
{
// eg. MyESP._http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local
for (clsQuery::list::iterator it = m_Queries.begin(); ((bResult) && (it != m_Queries.end())); it++)
{
clsQuery* pQuery = *it;
if (pQuery->m_bAwaitingAnswers)
{
clsQuery::clsAnswer* pSQAnswer = pQuery->findAnswerForServiceDomain(p_pSRVAnswer->m_Header.m_Domain);
if (pSQAnswer)
{
// Answer for this service domain (eg. MyESP._http._tcp.local) available
if (p_pSRVAnswer->m_u32TTL)
{
// First or update message (TTL != 0)
pSQAnswer->m_TTLHostDomainAndPort.set(p_pSRVAnswer->m_u32TTL); // Update TTL tag
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processSRVAnswer: Updated TTL(%lu) for "), _DH(), p_pSRVAnswer->m_u32TTL);
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n"));
);
// Host domain & Port
if ((pSQAnswer->m_HostDomain != p_pSRVAnswer->m_SRVDomain) ||
(pSQAnswer->m_u16Port != p_pSRVAnswer->m_u16Port))
{
pSQAnswer->m_HostDomain = p_pSRVAnswer->m_SRVDomain;
//pSQAnswer->releaseHostDomain();
pSQAnswer->m_u16Port = p_pSRVAnswer->m_u16Port;
pSQAnswer->m_QueryAnswerFlags |= (static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::HostDomain) | static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::Port));
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processSVRAnswer: Added host domain and port to "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(": "));
_printRRDomain(pSQAnswer->m_HostDomain);
DEBUG_OUTPUT.printf_P(PSTR(": %u\n"), pSQAnswer->m_u16Port);
);
p_rbFoundNewKeyAnswer = true;
_executeQueryCallback(*pQuery, *pSQAnswer, (static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::HostDomain) | static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::Port)), true);
}
}
else
{
// Goodby message
pSQAnswer->m_TTLHostDomainAndPort.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processSRVAnswer: 'Goodbye' received for "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n"));
);
}
}
} // m_bAwaitingAnswers
} // for(queries)
} // else: No p_pSRVAnswer
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _processSRVAnswer: FAILED!\n"), _DH()););
return bResult;
}
/*
clsLEAmDNS2_Host::_processTXTAnswer (level 2)
*/
bool clsLEAMDNSHost::_processTXTAnswer(const clsLEAMDNSHost::clsRRAnswerTXT* p_pTXTAnswer)
{
bool bResult = false;
if ((bResult = (0 != p_pTXTAnswer)))
{
// eg. MyESP._http._tcp.local TXT xxxx xx c#=1
for (clsQuery::list::iterator it = m_Queries.begin(); ((bResult) && (it != m_Queries.end())); it++)
{
clsQuery* pQuery = *it;
if (pQuery->m_bAwaitingAnswers)
{
clsQuery::clsAnswer* pSQAnswer = pQuery->findAnswerForServiceDomain(p_pTXTAnswer->m_Header.m_Domain);
if (pSQAnswer)
{
// Answer for this service domain (eg. MyESP._http._tcp.local) available
if (p_pTXTAnswer->m_u32TTL)
{
// First or update message
pSQAnswer->m_TTLTxts.set(p_pTXTAnswer->m_u32TTL); // Update TTL tag
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processTXTAnswer: Updated TTL(%lu) for "), _DH(), p_pTXTAnswer->m_u32TTL);
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n"));
);
if (!pSQAnswer->m_Txts.compare(p_pTXTAnswer->m_Txts))
{
pSQAnswer->m_Txts = p_pTXTAnswer->m_Txts;
pSQAnswer->m_QueryAnswerFlags |= static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::Txts);
//pSQAnswer->releaseTxts();
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processTXTAnswer: Added TXT to "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.println();
);
_executeQueryCallback(*pQuery, *pSQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::Txts), true);
}
}
else
{
// Goodby message
pSQAnswer->m_TTLTxts.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processTXTAnswer: 'Goodbye' received for "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n"));
);
}
}
} // m_bAwaitingAnswers
} // for(queries)
} // else: No p_pTXTAnswer
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _processTXTAnswer: FAILED!\n"), _DH()););
return bResult;
}
#ifdef MDNS_IPV4_SUPPORT
/*
clsLEAmDNS2_Host::_processAAnswer (level 3)
*/
bool clsLEAMDNSHost::_processAAnswer(const clsLEAMDNSHost::clsRRAnswerA* p_pAAnswer)
{
bool bResult = false;
if ((bResult = (0 != p_pAAnswer)))
{
// eg. esp8266.local A xxxx xx 192.168.2.120
for (clsQuery::list::iterator it = m_Queries.begin(); ((bResult) && (it != m_Queries.end())); it++)
{
clsQuery* pQuery = *it;
if (pQuery->m_bAwaitingAnswers)
{
// Look for answers to host queries
if ((p_pAAnswer->m_u32TTL) && // NOT just a goodbye message
(clsQuery::enuQueryType::Host == pQuery->m_QueryType) && // AND a host query
(pQuery->m_Domain == p_pAAnswer->m_Header.m_Domain)) // AND a matching host domain
{
clsQuery::clsAnswer* pSQAnswer = pQuery->findAnswerForHostDomain(p_pAAnswer->m_Header.m_Domain);
if ((!pSQAnswer) &&
((pSQAnswer = new clsQuery::clsAnswer)))
{
// Add not yet included answer
pSQAnswer->m_HostDomain = p_pAAnswer->m_Header.m_Domain;
//pSQAnswer->releaseHostDomain();
bResult = pQuery->addAnswer(pSQAnswer);
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processAAnswer: Added host query answer for "), _DH());
_printRRDomain(pQuery->m_Domain);
DEBUG_OUTPUT.println();
);
pSQAnswer->m_QueryAnswerFlags |= static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::HostDomain);
_executeQueryCallback(*pQuery, *pSQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::HostDomain), true);
}
}
// Look for answers to service queries
clsQuery::clsAnswer* pSQAnswer = pQuery->findAnswerForHostDomain(p_pAAnswer->m_Header.m_Domain);
if (pSQAnswer)
{
// Answer for this host domain (eg. esp8266.local) available
clsQuery::clsAnswer::clsIPAddressWithTTL* pIPAddress = pSQAnswer->findIPv4Address(p_pAAnswer->m_IPAddress);
if (pIPAddress)
{
// Already known IPv4 address
if (p_pAAnswer->m_u32TTL)
{
// Valid TTL -> Update answers TTL
pIPAddress->m_TTL.set(p_pAAnswer->m_u32TTL);
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processAAnswer: Updated TTL(%lu) for "), _DH(), p_pAAnswer->m_u32TTL);
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" IPv4 address (%s)\n"), pIPAddress->m_IPAddress.toString().c_str());
);
}
else
{
// 'Goodbye' message for known IPv4 address
pIPAddress->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processAAnswer: 'Goodbye' received for "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" IPv4 address (%s)\n"), pIPAddress->m_IPAddress.toString().c_str());
);
}
}
else
{
// Until now unknown IPv4 address -> Add (if the message isn't just a 'Goodbye' note)
if (p_pAAnswer->m_u32TTL)
{
// NOT just a 'Goodbye' message
pIPAddress = new clsQuery::clsAnswer::clsIPAddressWithTTL(p_pAAnswer->m_IPAddress, p_pAAnswer->m_u32TTL);
if ((pIPAddress) &&
(pSQAnswer->addIPv4Address(pIPAddress)))
{
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processAAnswer: Added IPv4 address to "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(": %s\n"), pIPAddress->m_IPAddress.toString().c_str());
);
pSQAnswer->m_QueryAnswerFlags |= static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::IPv4Address);
_executeQueryCallback(*pQuery, *pSQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::IPv4Address), true);
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _processAAnswer: FAILED to add IPv4 address (%s)!\n"), _DH(), p_pAAnswer->m_IPAddress.toString().c_str()););
}
}
}
}
} // m_bAwaitingAnswers
} // for(queries)
} // else: No p_pAAnswer
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _processAAnswer: FAILED!\n"), _DH()););
return bResult;
}
#endif
#ifdef MDNS_IPV6_SUPPORT
/*
clsLEAmDNS2_Host::_processAAAAAnswer (level 3)
*/
bool clsLEAMDNSHost::_processAAAAAnswer(const clsLEAMDNSHost::clsRRAnswerAAAA* p_pAAAAAnswer)
{
bool bResult = false;
if ((bResult = (0 != p_pAAAAAnswer)))
{
// eg. esp8266.local AAAA xxxx xx 0bf3::0c
for (clsQuery::list::iterator it = m_Queries.begin(); ((bResult) && (it != m_Queries.end())); it++)
{
clsQuery* pQuery = *it;
if (pQuery->m_bAwaitingAnswers)
{
// Look for answers to host queries
if ((p_pAAAAAnswer->m_u32TTL) && // NOT just a goodbye message
(clsQuery::enuQueryType::Host == pQuery->m_QueryType) && // AND a host query
(pQuery->m_Domain == p_pAAAAAnswer->m_Header.m_Domain)) // AND a matching host domain
{
clsQuery::clsAnswer* pSQAnswer = pQuery->findAnswerForHostDomain(p_pAAAAAnswer->m_Header.m_Domain);
if ((!pSQAnswer) &&
((pSQAnswer = new clsQuery::clsAnswer)))
{
// Add not yet included answer
pSQAnswer->m_HostDomain = p_pAAAAAnswer->m_Header.m_Domain;
//pSQAnswer->releaseHostDomain();
bResult = pQuery->addAnswer(pSQAnswer);
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processAAAAAnswer: Added host query answer for "), _DH());
_printRRDomain(pQuery->m_Domain);
DEBUG_OUTPUT.println();
);
pSQAnswer->m_QueryAnswerFlags |= static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::HostDomain);
_executeQueryCallback(*pQuery, *pSQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::HostDomain), true);
}
}
// Look for answers to service queries
clsQuery::clsAnswer* pSQAnswer = pQuery->findAnswerForHostDomain(p_pAAAAAnswer->m_Header.m_Domain);
if (pSQAnswer) // Answer for this host domain (eg. esp8266.local) available
{
clsQuery::clsAnswer::clsIPAddressWithTTL* pIPAddress = pSQAnswer->findIPv6Address(p_pAAAAAnswer->m_IPAddress);
if (pIPAddress)
{
// Already known IPv6 address
if (p_pAAAAAnswer->m_u32TTL)
{
// Valid TTL -> Update answers TTL
pIPAddress->m_TTL.set(p_pAAAAAnswer->m_u32TTL);
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processAAAAAnswer: Updated TTL(%lu) for "), _DH(), p_pAAAAAnswer->m_u32TTL);
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" IPv6 address (%s)\n"), pIPAddress->m_IPAddress.toString().c_str());
);
}
else
{
// 'Goodbye' message for known IPv6 address
pIPAddress->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processAAAAAnswer: 'Goodbye' received for "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" IPv6 address (%s)\n"), pIPAddress->m_IPAddress.toString().c_str());
);
}
}
else
{
// Until now unknown IPv6 address -> Add (if the message isn't just a 'Goodbye' note)
if (p_pAAAAAnswer->m_u32TTL)
{
// NOT just a 'Goodbye' message
pIPAddress = new clsQuery::clsAnswer::clsIPAddressWithTTL(p_pAAAAAnswer->m_IPAddress, p_pAAAAAnswer->m_u32TTL);
if ((pIPAddress) &&
(pSQAnswer->addIPv6Address(pIPAddress)))
{
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _processAAAAAnswer: Added IPv6 address to "), _DH());
_printRRDomain(pSQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(": %s\n"), pIPAddress->m_IPAddress.toString().c_str());
);
pSQAnswer->m_QueryAnswerFlags |= static_cast<uint32_t>(clsQuery::clsAnswer::enuQueryAnswerType::IPv6Address);
_executeQueryCallback(*pQuery, *pSQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::IPv6Address), true);
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _processAAAAAnswer: FAILED to add IPv6 address (%s)!\n"), _DH(), p_pAAAAAnswer->m_IPAddress.toString().c_str()););
}
}
}
}
} // m_bAwaitingAnswers
} // for(queries)
} // else: No p_pAAAAAnswer
return bResult;
}
#endif
/*
PROBING
*/
/*
clsLEAmDNS2_Host::_updateProbeStatus
Manages the (outgoing) probing process.
- If probing has not been started yet (ProbingStatus_NotStarted), the initial delay (see RFC 6762) is determined and
the process is started
- After timeout (of initial or subsequential delay) a probe message is send out for three times. If the message has
already been sent out three times, the probing has been successful and is finished.
Conflict management is handled in '_parseResponse ff.'
Tiebraking is handled in 'parseQuery ff.'
*/
bool clsLEAMDNSHost::_updateProbeStatus()
{
bool bResult = true;
//
// Probe host domain
if ((clsProbeInformation_Base::enuProbingStatus::ReadyToStart == m_ProbeInformation.m_ProbingStatus))// && // Ready to get started AND
/*
((
#ifdef MDNS_IPV4_SUPPORT
_getResponderIPAddress(pNetIf, enuIPProtocolType::V4).isSet() // AND has IPv4 address
#else
true
#endif
) || (
#ifdef MDNS_IPV6_SUPPORT
_getResponderIPAddress(pNetIf, enuIPProtocolType::V6).isSet() // OR has IPv6 address
#else
true
#endif
))) // Has IP address
*/
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Starting host probing...\n"), _DH()););
// First probe delay SHOULD be random 0-250 ms
m_ProbeInformation.m_Timeout.reset(rand() % clsConsts::u32ProbeDelay);
m_ProbeInformation.m_ProbingStatus = clsProbeInformation_Base::enuProbingStatus::InProgress;
}
else if ((clsProbeInformation_Base::enuProbingStatus::InProgress == m_ProbeInformation.m_ProbingStatus) && // Probing AND
(m_ProbeInformation.m_Timeout.expired())) // Time for next probe
{
if (clsConsts::u32ProbeCount > m_ProbeInformation.m_u32SentCount)
{
// Send next probe
if ((bResult = _sendHostProbe()))
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Did sent host probe for '%s.local'\n\n"), _DH(), (m_pcHostName ? : "")););
m_ProbeInformation.m_Timeout.reset(clsConsts::u32ProbeDelay);
++m_ProbeInformation.m_u32SentCount;
}
}
else
{
// Probing finished
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("\n%s _updateProbeStatus: Done host probing for '%s.local'.\n\n\n"), _DH(), (m_pcHostName ? : "")););
m_ProbeInformation.m_ProbingStatus = clsProbeInformation_Base::enuProbingStatus::ReadyToAnnounce;
m_ProbeInformation.m_Timeout.reset(esp8266::polledTimeout::oneShot::neverExpires);
_callHostProbeResultCallback(true);
// Prepare to announce host
m_ProbeInformation.m_u32SentCount = 0;
m_ProbeInformation.m_Timeout.reset(clsConsts::u32AnnounceDelay);
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Prepared host announcing.\n\n"), _DH()););
}
} // else: Probing already finished OR waiting for next time slot
else if ((clsProbeInformation_Base::enuProbingStatus::ReadyToAnnounce == m_ProbeInformation.m_ProbingStatus) &&
(m_ProbeInformation.m_Timeout.expired()))
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: ReadyToAnnounce => Announce (without services), now\n"), _DH()););
if ((bResult = _announce(true, false)))
{
// Don't announce services here
++m_ProbeInformation.m_u32SentCount; // 1..
if (clsConsts::u32AnnounceCount > m_ProbeInformation.m_u32SentCount)
{
m_ProbeInformation.m_Timeout.reset(clsConsts::u32AnnounceDelay * pow(2, (m_ProbeInformation.m_u32SentCount - 1))); // 2^(0..) -> 1, 2, 4, ...
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Announcing host '%s.local' (%lu).\n\n"), _DH(), (m_pcHostName ? : ""), m_ProbeInformation.m_u32SentCount););
}
else
{
m_ProbeInformation.m_ProbingStatus = clsProbeInformation_Base::enuProbingStatus::DoneFinally;
m_ProbeInformation.m_Timeout.reset(esp8266::polledTimeout::oneShot::neverExpires);
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Done host announcing for '%s.local'.\n"), _DH(), (m_pcHostName ? : "")););
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Done host announcing for '%s.local'.\n\n"), _DH(), (m_pcHostName ? : "")););
//DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Done host announcing for '%s.local'.\n"), _DH(), (m_pcHostName ? : ""));
}
}
}
//
// Probe services
for (clsService::list::iterator it = m_Services.begin(); ((bResult) && (it != m_Services.end())); it++)
{
clsService* pService = *it;
if (clsProbeInformation_Base::enuProbingStatus::ReadyToStart == pService->m_ProbeInformation.m_ProbingStatus)
{
// Ready to get started
pService->m_ProbeInformation.m_Timeout.reset(clsConsts::u32ProbeDelay); // More or equal than first probe for host domain
pService->m_ProbeInformation.m_ProbingStatus = clsProbeInformation_Base::enuProbingStatus::InProgress;
}
else if ((clsProbeInformation_Base::enuProbingStatus::InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing AND
(pService->m_ProbeInformation.m_Timeout.expired())) // Time for next probe
{
if (clsConsts::u32ProbeCount > pService->m_ProbeInformation.m_u32SentCount)
{
// Send next probe
if ((bResult = _sendServiceProbe(*pService)))
{
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Did sent service probe for '%s' (%u)\n\n"), _DH(), _service2String(pService), (pService->m_ProbeInformation.m_u32SentCount + 1)););
pService->m_ProbeInformation.m_Timeout.reset(clsConsts::u32ProbeDelay);
++pService->m_ProbeInformation.m_u32SentCount;
}
}
else
{
// Probing finished
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("\n%s _updateProbeStatus: Done service probing '%s'\n\n\n"), _DH(), _service2String(pService)););
pService->m_ProbeInformation.m_ProbingStatus = clsProbeInformation_Base::enuProbingStatus::ReadyToAnnounce;
pService->m_ProbeInformation.m_Timeout.reset(esp8266::polledTimeout::oneShot::neverExpires);
_callServiceProbeResultCallback(*pService, true);
// Prepare to announce service
pService->m_ProbeInformation.m_u32SentCount = 0;
pService->m_ProbeInformation.m_Timeout.reset(clsConsts::u32AnnounceDelay);
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Prepared service announcing.\n\n"), _DH()););
}
}
else if ((clsProbeInformation_Base::enuProbingStatus::ReadyToAnnounce == pService->m_ProbeInformation.m_ProbingStatus) &&
(pService->m_ProbeInformation.m_Timeout.expired()))
{
// Probing already finished OR waiting for next time slot
if ((bResult = _announceService(*pService)))
{
// Announce service
++pService->m_ProbeInformation.m_u32SentCount; // 1..
if (clsConsts::u32AnnounceCount > pService->m_ProbeInformation.m_u32SentCount)
{
pService->m_ProbeInformation.m_Timeout.reset(clsConsts::u32AnnounceDelay * pow(2, (pService->m_ProbeInformation.m_u32SentCount - 1))); // 2^(0..) -> 1, 2, 4, ...
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Announcing service '%s' (%lu)\n\n"), _DH(), _service2String(pService), pService->m_ProbeInformation.m_u32SentCount););
}
else
{
pService->m_ProbeInformation.m_ProbingStatus = clsProbeInformation_Base::enuProbingStatus::DoneFinally;
pService->m_ProbeInformation.m_Timeout.reset(esp8266::polledTimeout::oneShot::neverExpires);
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Done service announcing for '%s'\n"), _DH(), _service2String(pService)););
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Done service announcing for '%s'\n\n"), _DH(), _service2String(pService)););
//DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: Done service announcing for '%s'\n"), _DH(), _service2String(pService));
}
}
}
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _updateProbeStatus: FAILED!\n\n"), _DH()););
return bResult;
}
/*
clsLEAmDNS2_Host::_resetProbeStatus
Resets the probe status.
If 'p_bRestart' is set, the status is set to ProbingStatus_NotStarted. Consequently,
when running 'updateProbeStatus' (which is done in every '_update' loop), the probing
process is restarted.
*/
bool clsLEAMDNSHost::_resetProbeStatus(bool p_bRestart /*= true*/)
{
m_ProbeInformation.clear(false);
m_ProbeInformation.m_ProbingStatus = (p_bRestart ? clsProbeInformation_Base::enuProbingStatus::ReadyToStart : clsProbeInformation_Base::enuProbingStatus::DoneFinally);
for (clsService* pService : m_Services)
{
pService->m_ProbeInformation.clear(false);
pService->m_ProbeInformation.m_ProbingStatus = m_ProbeInformation.m_ProbingStatus;
}
return true;
}
/*
clsLEAmDNS2_Host::_hasProbesWaitingForAnswers
*/
bool clsLEAMDNSHost::_hasProbesWaitingForAnswers(void) const
{
bool bResult = ((clsProbeInformation_Base::enuProbingStatus::InProgress == m_ProbeInformation.m_ProbingStatus) && // Probing
(0 < m_ProbeInformation.m_u32SentCount)); // And really probing
for (clsService::list::const_iterator it = m_Services.cbegin(); ((!bResult) && (it != m_Services.cend())); it++)
{
clsService* pService = *it;
bResult = ((clsProbeInformation_Base::enuProbingStatus::InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing
(0 < pService->m_ProbeInformation.m_u32SentCount)); // And really probing
}
return bResult;
}
/*
clsLEAmDNS2_Host::_sendHostProbe
Asks (probes) in the local network for the planned host domain
- (eg. esp8266.local)
To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in
the 'known answers' section of the query.
Host domain:
- A/AAAA (eg. esp8266.esp -> 192.168.2.120)
*/
bool clsLEAMDNSHost::_sendHostProbe()
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _sendHostProbe (%s.local, %lu)\n"), _DH(), m_pcHostName, millis()););
bool bResult = true;
// Requests for host domain
clsSendParameter sendParameter;
sendParameter.m_bCacheFlush = false; // RFC 6762 10.2
clsRRQuestion* pNewRRQuestion = new clsRRQuestion;
if (((bResult = (0 != pNewRRQuestion))) &&
((bResult = _buildDomainForHost(m_pcHostName, pNewRRQuestion->m_Header.m_Domain))))
{
//sendParameter.m_pQuestions->m_bUnicast = true;
pNewRRQuestion->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY;
pNewRRQuestion->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet
sendParameter.m_RRQuestions.push_back(pNewRRQuestion);
// Add known answers
#ifdef MDNS_IPV4_SUPPORT
sendParameter.m_u32HostReplyMask |= static_cast<uint32_t>(enuContentFlag::A); // Add A answer
#endif
#ifdef MDNS_IPV6_SUPPORT
sendParameter.m_u32HostReplyMask |= static_cast<uint32_t>(enuContentFlag::AAAA); // Add AAAA answer
#endif
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _sendHostProbe: FAILED to create host question!\n"), _DH()););
if (pNewRRQuestion)
{
delete pNewRRQuestion;
pNewRRQuestion = 0;
}
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _sendHostProbe: FAILED!\n"), _DH()););
return ((bResult) &&
(_sendMessage(sendParameter)));
}
/*
clsLEAmDNS2_Host::_sendServiceProbe
Asks (probes) in the local network for the planned service instance domain
- (eg. MyESP._http._tcp.local).
To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in
the 'known answers' section of the query.
Service domain:
- SRV (eg. MyESP._http._tcp.local -> 5000 esp8266.local)
- PTR NAME (eg. _http._tcp.local -> MyESP._http._tcp.local) (TODO: Check if needed, maybe TXT is better)
*/
bool clsLEAMDNSHost::_sendServiceProbe(clsService& p_rService)
{
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _sendServiceProbe (%s, %lu)\n"), _DH(), _service2String(&p_rService), millis()););
bool bResult = true;
// Requests for service instance domain
clsSendParameter sendParameter;
sendParameter.m_bCacheFlush = false; // RFC 6762 10.2
clsRRQuestion* pNewRRQuestion = new clsRRQuestion;
if (((bResult = (0 != pNewRRQuestion))) &&
((bResult = _buildDomainForService(p_rService, true, pNewRRQuestion->m_Header.m_Domain))))
{
pNewRRQuestion->m_bUnicast = true;
pNewRRQuestion->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY;
pNewRRQuestion->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet
sendParameter.m_RRQuestions.push_back(pNewRRQuestion);
// Add known answers
p_rService.m_u32ReplyMask = (static_cast<uint32_t>(enuContentFlag::SRV) | static_cast<uint32_t>(enuContentFlag::PTR_NAME)); // Add SRV and PTR NAME answers
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("%s _sendServiceProbe: FAILED to create service question!\n"), _DH()););
if (pNewRRQuestion)
{
delete pNewRRQuestion;
pNewRRQuestion = 0;
}
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _sendServiceProbe: FAILED!\n"), _DH()););
return ((bResult) &&
(_sendMessage(sendParameter)));
}
/*
clsLEAmDNS2_Host::_cancelProbingForHost
*/
bool clsLEAMDNSHost::_cancelProbingForHost(void)
{
bool bResult;
m_ProbeInformation.clear(false);
// Send host notification
bResult = _callHostProbeResultCallback(false);
for (clsService* pService : m_Services)
{
bResult = bResult && _cancelProbingForService(*pService);
}
return bResult;
}
/*
clsLEAmDNS2_Host::_cancelProbingForService
*/
bool clsLEAMDNSHost::_cancelProbingForService(clsService& p_rService)
{
p_rService.m_ProbeInformation.clear(false);
// Send notification
return _callServiceProbeResultCallback(p_rService, false);
}
/*
clsLEAmDNS2_Host::_callHostProbeResultCallback
*/
bool clsLEAMDNSHost::_callHostProbeResultCallback(bool p_bResult)
{
if (m_ProbeInformation.m_fnProbeResultCallback)
{
m_ProbeInformation.m_fnProbeResultCallback(*this, m_pcHostName, p_bResult);
}
else if (!p_bResult)
{
// Auto-Handle failure by changing the host name, use '-' as divider between base name and index
indexHostName();
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _callHostProbeResultCallback: Changed Host Name: %s\n"), _DH(), (m_pcHostName ? : "")));
}
return true;
}
/*
clsLEAmDNS2_Host::_callServiceProbeResultCallback
*/
bool clsLEAMDNSHost::_callServiceProbeResultCallback(clsLEAMDNSHost::clsService& p_rService,
bool p_bResult)
{
if (p_rService.m_ProbeInformation.m_fnProbeResultCallback)
{
p_rService.m_ProbeInformation.m_fnProbeResultCallback(p_rService, p_rService.instanceName(), p_bResult);
}
else if (!p_bResult)
{
// Auto-Handle failure by changing the service name, use ' #' as divider between base name and index
p_rService.indexInstanceName();
DEBUG_EX_INFO2(DEBUG_OUTPUT.printf_P(PSTR("%s _callServiceProbeResultCallback: Changed Service Domain: %s\n"), _DH(), _service2String(&p_rService)));
}
return true;
}
/*
ANNOUNCING
*/
/*
clsLEAmDNS2_Host::_announce
Announces the host domain:
- A/AAAA (eg. esp8266.local -> 192.168.2.120)
- PTR (eg. 192.168.2.120.in-addr.arpa -> esp8266.local)
and all presented services:
- PTR_TYPE (_services._dns-sd._udp.local -> _http._tcp.local)
- PTR_NAME (eg. _http._tcp.local -> MyESP8266._http._tcp.local)
- SRV (eg. MyESP8266._http._tcp.local -> 5000 esp8266.local)
- TXT (eg. MyESP8266._http._tcp.local -> c#=1)
Goodbye (Un-Announcing) for the host domain and all services is also handled here.
Goodbye messages are created by setting the TTL for the answer to 0, this happens
inside the '_writeXXXAnswer' procs via 'sendParameter.m_bUnannounce = true'
*/
bool clsLEAMDNSHost::_announce(bool p_bAnnounce,
bool p_bIncludeServices)
{
bool bResult = false;
clsSendParameter sendParameter;
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s ::announce() status=%d (waiting4data:%d ready2start:%d inprogress:%d ready2announce:%d done:%d\r\n"),
_DH(), m_ProbeInformation.m_ProbingStatus,
clsProbeInformation_Base::enuProbingStatus::WaitingForData,
clsProbeInformation_Base::enuProbingStatus::ReadyToStart,
clsProbeInformation_Base::enuProbingStatus::InProgress,
clsProbeInformation_Base::enuProbingStatus::ReadyToAnnounce,
clsProbeInformation_Base::enuProbingStatus::DoneFinally););
if ((clsProbeInformation_Base::enuProbingStatus::ReadyToAnnounce == m_ProbeInformation.m_ProbingStatus) ||
(clsProbeInformation_Base::enuProbingStatus::DoneFinally == m_ProbeInformation.m_ProbingStatus))
{
bResult = true;
sendParameter.m_Response = clsSendParameter::enuResponseType::Unsolicited; // Announces are 'Unsolicited authorative responses'
sendParameter.m_bAuthorative = true;
sendParameter.m_bCacheFlush = true; // RFC 6762 8.3
sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers
// Announce host
sendParameter.m_u32HostReplyMask = 0;
#ifdef MDNS_IPV4_SUPPORT
sendParameter.m_u32HostReplyMask |= static_cast<uint32_t>(enuContentFlag::A); // A answer
sendParameter.m_u32HostReplyMask |= static_cast<uint32_t>(enuContentFlag::PTR_IPv4); // PTR_IPv4 answer
#endif
#ifdef MDNS_IPV6_SUPPORT
sendParameter.m_u32HostReplyMask |= static_cast<uint32_t>(enuContentFlag::AAAA); // AAAA answer
sendParameter.m_u32HostReplyMask |= static_cast<uint32_t>(enuContentFlag::PTR_IPv6); // PTR_IPv6 answer
#endif
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _announce: Announcing host %s (content: %s)\n"), _DH(), m_pcHostName, _replyFlags2String(sendParameter.m_u32HostReplyMask)););
if (p_bIncludeServices)
{
// Announce services (service type, name, SRV (location) and TXTs)
for (clsService* pService : m_Services)
{
if ((clsProbeInformation_Base::enuProbingStatus::ReadyToAnnounce == pService->m_ProbeInformation.m_ProbingStatus) ||
(clsProbeInformation_Base::enuProbingStatus::DoneFinally == pService->m_ProbeInformation.m_ProbingStatus))
{
pService->m_u32ReplyMask = (static_cast<uint32_t>(enuContentFlag::PTR_TYPE) |
static_cast<uint32_t>(enuContentFlag::PTR_NAME) |
static_cast<uint32_t>(enuContentFlag::SRV) |
static_cast<uint32_t>(enuContentFlag::TXT));
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _announce: Announcing service '%s' (content %s)\n"), _DH(), _service2String(pService), _replyFlags2String(pService->m_u32ReplyMask)););
}
}
}
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _announce: FAILED!\n\n"), _DH()););
return ((bResult) &&
(_sendMessage(sendParameter)));
}
/*
clsLEAmDNS2_Host::_announceService
*/
bool clsLEAMDNSHost::_announceService(clsLEAMDNSHost::clsService& p_rService,
bool p_bAnnounce /*= true*/)
{
bool bResult = false;
clsSendParameter sendParameter;
if (clsProbeInformation_Base::enuProbingStatus::ReadyToAnnounce == p_rService.m_ProbeInformation.m_ProbingStatus)
{
sendParameter.m_Response = clsSendParameter::enuResponseType::Unsolicited; // Announces are 'Unsolicited authorative responses'
sendParameter.m_bAuthorative = true;
sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers
// DON'T announce host
sendParameter.m_u32HostReplyMask = 0;
// Announce services (service type, name, SRV (location) and TXTs)
p_rService.m_u32ReplyMask = (static_cast<uint32_t>(enuContentFlag::PTR_TYPE) |
static_cast<uint32_t>(enuContentFlag::PTR_NAME) |
static_cast<uint32_t>(enuContentFlag::SRV) |
static_cast<uint32_t>(enuContentFlag::TXT));
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _announceService: Announcing service '%s' (content: %s)\n"), _DH(), _service2String(&p_rService), _replyFlags2String(p_rService.m_u32ReplyMask)););
bResult = true;
}
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _announceService: FAILED!\n"), _DH()););
return ((bResult) &&
(_sendMessage(sendParameter)));
}
/*
QUERY CACHE
*/
/*
clsLEAmDNS2_Host::_checkQueryCache
For any 'living' query (m_bAwaitingAnswers == true) all available answers (their components)
are checked for topicality based on the stored reception time and the answers TTL.
When the components TTL is outlasted by more than 80%, a new question is generated, to get updated information.
When no update arrived (in time), the component is removed from the answer (cache).
*/
bool clsLEAMDNSHost::_checkQueryCache()
{
bool bResult = true;
DEBUG_EX_INFO(
bool printedInfo = false;
);
for (clsQuery::list::iterator itQ = m_Queries.begin(); ((bResult) && (itQ != m_Queries.end())); itQ++)
{
clsQuery* pQuery = *itQ;
//
// Resend dynamic queries, if not already done often enough
if ((!pQuery->m_bStaticQuery) &&
(pQuery->m_ResendTimeout.expired()))
{
if ((bResult = _sendQuery(*pQuery)))
{
// The re-query rate is increased to more than one hour (RFC 6762 5.2)
++pQuery->m_u32SentCount;
uint32_t u32NewDelay = (clsConsts::u32DynamicQueryResendDelay * pow(2, std::min((pQuery->m_u32SentCount - 1), (uint32_t)12)));
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: Next query in %u seconds!\n"), _DH(), (u32NewDelay)););
pQuery->m_ResendTimeout.reset(u32NewDelay);
}
else
{
break;
}
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: %s to resend query!\n"), _DH(), (bResult ? "Succeeded" : "FAILED"));
printedInfo = true;
);
}
//
// Schedule updates for cached answers
if (pQuery->m_bAwaitingAnswers)
{
clsQuery::clsAnswer::list expiredAnswers;
for (clsQuery::clsAnswer::list::iterator itQA = pQuery->m_Answers.begin(); ((bResult) && (itQA != pQuery->m_Answers.end())); itQA++)
{
clsQuery::clsAnswer* pQAnswer = *itQA;
// 1. level answer
if ((bResult) &&
(pQAnswer->m_TTLServiceDomain.flagged()))
{
if (!pQAnswer->m_TTLServiceDomain.finalTimeoutLevel())
{
bResult = ((_sendQuery(*pQuery)) &&
(pQAnswer->m_TTLServiceDomain.restart()));
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: PTR update scheduled for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" %s\n"), (bResult ? "OK" : "FAILURE"));
printedInfo = true;
);
}
else
{
// Timed out! -> Delete
_executeQueryCallback(*pQuery, *pQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::ServiceDomain), false);
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: Will remove PTR answer for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR("\n"));
printedInfo = true;
);
expiredAnswers.push_back(pQAnswer);
continue; // Don't use this answer anymore
}
} // ServiceDomain flagged
// 2. level answers
// HostDomain & Port (from SRV)
if ((bResult) &&
(pQAnswer->m_TTLHostDomainAndPort.flagged()))
{
if (!pQAnswer->m_TTLHostDomainAndPort.finalTimeoutLevel())
{
bResult = ((_sendQuery(pQAnswer->m_ServiceDomain, DNS_RRTYPE_SRV)) &&
(pQAnswer->m_TTLHostDomainAndPort.restart()));
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: SRV update scheduled for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" host domain and port %s\n"), (bResult ? "OK" : "FAILURE"));
printedInfo = true;
);
}
else
{
// Timed out! -> Delete
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: Will remove SRV answer for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n"));
printedInfo = true;
);
// Delete
pQAnswer->m_HostDomain.clear();
//pSQAnswer->releaseHostDomain();
pQAnswer->m_u16Port = 0;
pQAnswer->m_TTLHostDomainAndPort.set(0);
clsQuery::clsAnswer::typeQueryAnswerType queryAnswerContentFlags = (static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::HostDomain) | static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::Port));
// As the host domain is the base for the IPv4- and IPv6Address, remove these too
#ifdef MDNS_IPV4_SUPPORT
pQAnswer->releaseIPv4Addresses();
queryAnswerContentFlags |= static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::IPv4Address);
#endif
#ifdef MDNS_IPV6_SUPPORT
pQAnswer->releaseIPv6Addresses();
queryAnswerContentFlags |= static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::IPv6Address);
#endif
// Remove content flags for deleted answer parts
pQAnswer->m_QueryAnswerFlags &= ~queryAnswerContentFlags;
_executeQueryCallback(*pQuery, *pQAnswer, queryAnswerContentFlags, false);
}
} // HostDomainAndPort flagged
// Txts (from TXT)
if ((bResult) &&
(pQAnswer->m_TTLTxts.flagged()))
{
if (!pQAnswer->m_TTLTxts.finalTimeoutLevel())
{
bResult = ((_sendQuery(pQAnswer->m_ServiceDomain, DNS_RRTYPE_TXT)) &&
(pQAnswer->m_TTLTxts.restart()));
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: TXT update scheduled for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" TXTs %s\n"), (bResult ? "OK" : "FAILURE"));
printedInfo = true;
);
}
else
{
// Timed out! -> Delete
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: Will remove TXT answer for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n"));
printedInfo = true;
);
// Delete
pQAnswer->m_Txts.clear();
pQAnswer->m_TTLTxts.set(0);
// Remove content flags for deleted answer parts
pQAnswer->m_QueryAnswerFlags &= ~static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::Txts);
_executeQueryCallback(*pQuery, *pQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::Txts), false);
}
} // TXTs flagged
// 3. level answers
#ifdef MDNS_IPV4_SUPPORT
// IPv4Address (from A)
clsQuery::clsAnswer::clsIPAddressWithTTL::list expiredIPv4Addresses;
bool bAUpdateQuerySent = false;
for (clsQuery::clsAnswer::clsIPAddressWithTTL::list::iterator itQAIPv4 = pQAnswer->m_IPv4Addresses.begin(); ((bResult) && (itQAIPv4 != pQAnswer->m_IPv4Addresses.end())); itQAIPv4++)
{
clsQuery::clsAnswer::clsIPAddressWithTTL* pIPv4Address = *itQAIPv4;
if (pIPv4Address->m_TTL.flagged())
{
if (!pIPv4Address->m_TTL.finalTimeoutLevel())
{
// Needs update
if ((bAUpdateQuerySent) ||
((bResult = _sendQuery(pQAnswer->m_HostDomain, DNS_RRTYPE_A))))
{
pIPv4Address->m_TTL.restart();
bAUpdateQuerySent = true;
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: IPv4 update scheduled for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" IPv4 address (%s)\n"), (pIPv4Address->m_IPAddress.toString().c_str()));
printedInfo = true;
);
}
}
else
{
// Timed out! -> Delete
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: Will remove IPv4 answer for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" IPv4 address\n"));
printedInfo = true;
);
if (1 == pQAnswer->m_IPv4Addresses.size())
{
// NO IPv4 address left after this -> remove content flag
pQAnswer->m_QueryAnswerFlags &= ~static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::IPv4Address);
}
// Notify client
_executeQueryCallback(*pQuery, *pQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::IPv4Address), false);
expiredIPv4Addresses.push_back(pIPv4Address);
}
} // IPv4 flagged
} // for
// Finally remove expired IPv4 addresses
for (clsQuery::clsAnswer::clsIPAddressWithTTL* pIPv4Address : expiredIPv4Addresses)
{
pQAnswer->removeIPv4Address(pIPv4Address);
}
#endif
#ifdef MDNS_IPV6_SUPPORT
// IPv6Address (from AAAA)
clsQuery::clsAnswer::clsIPAddressWithTTL::list expiredIPv6Addresses;
bool bAAAAUpdateQuerySent = false;
for (clsQuery::clsAnswer::clsIPAddressWithTTL::list::iterator itQAIPv6 = pQAnswer->m_IPv6Addresses.begin(); ((bResult) && (itQAIPv6 != pQAnswer->m_IPv6Addresses.end())); itQAIPv6++)
{
clsQuery::clsAnswer::clsIPAddressWithTTL* pIPv6Address = *itQAIPv6;
if (pIPv6Address->m_TTL.flagged())
{
if (!pIPv6Address->m_TTL.finalTimeoutLevel())
{
// Needs update
if ((bAAAAUpdateQuerySent) ||
((bResult = _sendQuery(pQAnswer->m_HostDomain, DNS_RRTYPE_AAAA))))
{
pIPv6Address->m_TTL.restart();
bAAAAUpdateQuerySent = true;
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: IPv6 update scheduled for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" IPv6 address (%s)\n"), (pIPv6Address->m_IPAddress.toString().c_str()));
printedInfo = true;
);
}
}
else
{
// Timed out! -> Delete
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: Will remove answer for "), _DH());
_printRRDomain(pQAnswer->m_ServiceDomain);
DEBUG_OUTPUT.printf_P(PSTR(" IPv6 address\n"));
printedInfo = true;
);
if (1 == pQAnswer->m_IPv6Addresses.size())
{
// NO IPv6 address left after this -> remove content flag
pQAnswer->m_QueryAnswerFlags &= ~static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::IPv6Address);
}
// Notify client
_executeQueryCallback(*pQuery, *pQAnswer, static_cast<clsQuery::clsAnswer::typeQueryAnswerType>(clsQuery::clsAnswer::enuQueryAnswerType::IPv6Address), false);
expiredIPv6Addresses.push_back(pIPv6Address);
}
} // IPv6 flagged
// Finally remove expired IPv6 addresses
for (clsQuery::clsAnswer::clsIPAddressWithTTL* pIPv6Address : expiredIPv6Addresses)
{
pQAnswer->removeIPv6Address(pIPv6Address);
}
} // while
#endif
}
// Finally remove expired answers
for (clsQuery::clsAnswer* pAnswer : expiredAnswers)
{
pQuery->removeAnswer(pAnswer);
}
}
}
DEBUG_EX_INFO(if (printedInfo) DEBUG_OUTPUT.printf_P(PSTR("\n")););
DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("%s _checkQueryCache: FAILED!\n"), _DH()););
return bResult;
}
/*
clsLEAmDNS2_Host::_replyMaskForHost
Determines the relevant host answers for the given question.
- A question for the hostname (eg. esp8266.local) will result in an A/AAAA (eg. 192.168.2.129) reply.
- A question for the reverse IP address (eg. 192-168.2.120.inarpa.arpa) will result in an PTR_IPv4 (eg. esp8266.local) reply.
In addition, a full name match (question domain == host domain) is marked.
*/
uint32_t clsLEAMDNSHost::_replyMaskForHost(netif* pNetIf,
const clsLEAMDNSHost::clsRRHeader& p_RRHeader,
bool* p_pbFullNameMatch /*= 0*/) const
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _replyMaskForHost\n")););
uint32_t u32ReplyMask = 0;
(p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0);
if ((DNS_RRCLASS_IN == (p_RRHeader.m_Attributes.m_u16Class & (~0x8000))) ||
(DNS_RRCLASS_ANY == (p_RRHeader.m_Attributes.m_u16Class & (~0x8000))))
{
if ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) ||
(DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))
{
// PTR request
#ifdef MDNS_IPV4_SUPPORT
clsRRDomain reverseIPv4Domain;
if ((_getResponderIPAddress(pNetIf, enuIPProtocolType::V4).isSet()) &&
(_buildDomainForReverseIPv4(_getResponderIPAddress(pNetIf, enuIPProtocolType::V4), reverseIPv4Domain)) &&
(p_RRHeader.m_Domain == reverseIPv4Domain))
{
// Reverse domain match
u32ReplyMask |= static_cast<uint32_t>(enuContentFlag::PTR_IPv4);
}
#endif
#ifdef MDNS_IPV6_SUPPORT
clsRRDomain reverseIPv6Domain;
if ((_getResponderIPAddress(pNetIf, enuIPProtocolType::V6).isSet()) &&
(_buildDomainForReverseIPv6(_getResponderIPAddress(pNetIf, enuIPProtocolType::V6), reverseIPv6Domain)) &&
(p_RRHeader.m_Domain == reverseIPv6Domain))
{
// Reverse domain match
u32ReplyMask |= static_cast<uint32_t>(enuContentFlag::PTR_IPv6);
}
#endif
} // Address qeuest
clsRRDomain hostDomain;
if ((_buildDomainForHost(m_pcHostName, hostDomain)) &&
(p_RRHeader.m_Domain == hostDomain)) // Host domain match
{
(p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0));
#ifdef MDNS_IPV4_SUPPORT
if ((DNS_RRTYPE_A == p_RRHeader.m_Attributes.m_u16Type) ||
(DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))
{
// IPv4 address request
u32ReplyMask |= static_cast<uint32_t>(enuContentFlag::A);
}
#endif
#ifdef MDNS_IPV6_SUPPORT
if ((DNS_RRTYPE_AAAA == p_RRHeader.m_Attributes.m_u16Type) ||
(DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))
{
// IPv6 address request
u32ReplyMask |= static_cast<uint32_t>(enuContentFlag::AAAA);
}
#endif
}
}
else
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _replyMaskForHost: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class););
}
DEBUG_EX_INFO(if (u32ReplyMask) DEBUG_OUTPUT.printf_P(PSTR("%s _replyMaskForHost: %s\n"), _DH(), _replyFlags2String(u32ReplyMask)););
return u32ReplyMask;
}
/*
clsLEAmDNS2_Host::_replyMaskForService
Determines the relevant service answers for the given question
- A PTR dns-sd service enum question (_services.dns-sd._udp.local) will result into an PTR_TYPE (eg. _http._tcp.local) answer
- A PTR service type question (eg. _http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer
- A PTR service name question (eg. MyESP._http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer
- A SRV service name question (eg. MyESP._http._tcp.local) will result into an SRV (eg. 5000 MyESP.local) answer
- A TXT service name question (eg. MyESP._http._tcp.local) will result into an TXT (eg. c#=1) answer
In addition, a full name match (question domain == service instance domain) is marked.
*/
uint32_t clsLEAMDNSHost::_replyMaskForService(const clsLEAMDNSHost::clsRRHeader& p_RRHeader,
clsLEAMDNSHost::clsService& p_rService,
bool* p_pbFullNameMatch /*= 0*/)
{
uint32_t u32ReplyMask = 0;
(p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0);
if ((DNS_RRCLASS_IN == (p_RRHeader.m_Attributes.m_u16Class & (~0x8000))) ||
(DNS_RRCLASS_ANY == (p_RRHeader.m_Attributes.m_u16Class & (~0x8000))))
{
clsRRDomain DNSSDDomain;
if ((_buildDomainForDNSSD(DNSSDDomain)) && // _services._dns-sd._udp.local
(p_RRHeader.m_Domain == DNSSDDomain) &&
((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) ||
(DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)))
{
// Common service info requested
u32ReplyMask |= static_cast<uint32_t>(enuContentFlag::PTR_TYPE);
}
clsRRDomain serviceDomain;
if ((_buildDomainForService(p_rService, false, serviceDomain)) && // eg. _http._tcp.local
(p_RRHeader.m_Domain == serviceDomain) &&
((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) ||
(DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)))
{
// Special service info requested
u32ReplyMask |= static_cast<uint32_t>(enuContentFlag::PTR_NAME);
}
if ((_buildDomainForService(p_rService, true, serviceDomain)) && // eg. MyESP._http._tcp.local
(p_RRHeader.m_Domain == serviceDomain))
{
(p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0));
if ((DNS_RRTYPE_SRV == p_RRHeader.m_Attributes.m_u16Type) ||
(DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))
{
// Instance info SRV requested
u32ReplyMask |= static_cast<uint32_t>(enuContentFlag::SRV);
}
if ((DNS_RRTYPE_TXT == p_RRHeader.m_Attributes.m_u16Type) ||
(DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))
{
// Instance info TXT requested
u32ReplyMask |= static_cast<uint32_t>(enuContentFlag::TXT);
}
}
}
else
{
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("%s _replyMaskForService: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class););
}
DEBUG_EX_INFO(if (u32ReplyMask) DEBUG_OUTPUT.printf_P(PSTR("%s _replyMaskForService(%s.%s.%s): %s\n"), _DH(), p_rService.m_pcInstanceName, p_rService.m_pcType, p_rService.m_pcProtocol, _replyFlags2String(u32ReplyMask)););
return u32ReplyMask;
}
} // namespace MDNSImplementation
} // namespace esp8266