1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-27 21:16:50 +03:00
esp8266/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp
2022-03-04 02:28:47 +03:00

778 lines
25 KiB
C++

/*
LEAmDNS_Helpers.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 <lwip/igmp.h>
#include <stdlib_noniso.h> // strrstr()
#include "ESP8266mDNS.h"
#include "LEAmDNS_lwIPdefs.h"
#include "LEAmDNS_Priv.h"
namespace esp8266
{
/*
LEAmDNS
*/
namespace MDNSImplementation
{
/**
HELPERS
*/
/*
MDNSResponder::indexDomain (static)
Updates the given domain 'p_rpcHostname' by appending a delimiter and an index number.
If the given domain already hasa numeric index (after the given delimiter), this index
incremented. If not, the delimiter and index '2' is added.
If 'p_rpcHostname' is empty (==0), the given default name 'p_pcDefaultHostname' is used,
if no default is given, 'esp8266' is used.
*/
/*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain,
const char* p_pcDivider /*= "-"*/,
const char* p_pcDefaultDomain /*= 0*/)
{
bool bResult = false;
// Ensure a divider exists; use '-' as default
const char* pcDivider = (p_pcDivider ?: "-");
if (p_rpcDomain)
{
const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider);
if (pFoundDivider) // maybe already extended
{
char* pEnd = 0;
unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10);
if ((ulIndex) && ((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain))
&& (!*pEnd)) // Valid (old) index found
{
char acIndexBuffer[16];
sprintf(acIndexBuffer, "%lu", (++ulIndex));
size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider))
+ strlen(acIndexBuffer) + 1);
char* pNewHostname = new char[stLength];
if (pNewHostname)
{
memcpy(pNewHostname, p_rpcDomain,
(pFoundDivider - p_rpcDomain + strlen(pcDivider)));
pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0;
strcat(pNewHostname, acIndexBuffer);
delete[] p_rpcDomain;
p_rpcDomain = pNewHostname;
bResult = true;
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.println(
F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
}
}
else
{
pFoundDivider = 0; // Flag the need to (base) extend the hostname
}
}
if (!pFoundDivider) // not yet extended (or failed to increment extension) -> start
// indexing
{
size_t stLength = strlen(p_rpcDomain)
+ (strlen(pcDivider) + 1 + 1); // Name + Divider + '2' + '\0'
char* pNewHostname = new char[stLength];
if (pNewHostname)
{
sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider);
delete[] p_rpcDomain;
p_rpcDomain = pNewHostname;
bResult = true;
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.println(
F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
}
}
}
else
{
// No given host domain, use base or default
const char* cpcDefaultName = (p_pcDefaultDomain ?: "esp8266");
size_t stLength = strlen(cpcDefaultName) + 1; // '\0'
p_rpcDomain = new char[stLength];
if (p_rpcDomain)
{
strncpy(p_rpcDomain, cpcDefaultName, stLength);
bResult = true;
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.println(
F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
}
}
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] indexDomain: %s\n"), p_rpcDomain););
return bResult;
}
/*
UDP CONTEXT
*/
bool MDNSResponder::_callProcess(void)
{
DEBUG_EX_INFO(
DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(),
IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()););
return _process(false);
}
/*
MDNSResponder::_allocUDPContext
(Re-)Creates the one-and-only UDP context for the MDNS responder.
The context is added to the 'multicast'-group and listens to the MDNS port (5353).
The travel-distance for multicast messages is set to 1 (local, via MDNS_MULTICAST_TTL).
Messages are received via the MDNSResponder '_update' function. CAUTION: This function
is called from the WiFi stack side of the ESP stack system.
*/
bool MDNSResponder::_allocUDPContext(void)
{
DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext"););
_releaseUDPContext();
_joinMulticastGroups();
m_pUDPContext = new UdpContext;
m_pUDPContext->ref();
if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT))
{
m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL);
m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this));
}
else
{
return false;
}
return true;
}
/*
MDNSResponder::_releaseUDPContext
*/
bool MDNSResponder::_releaseUDPContext(void)
{
if (m_pUDPContext)
{
m_pUDPContext->unref();
m_pUDPContext = 0;
_leaveMulticastGroups();
}
return true;
}
/*
SERVICE QUERY
*/
/*
MDNSResponder::_allocServiceQuery
*/
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void)
{
stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery;
if (pServiceQuery)
{
// Link to query list
pServiceQuery->m_pNext = m_pServiceQueries;
m_pServiceQueries = pServiceQuery;
}
return m_pServiceQueries;
}
/*
MDNSResponder::_removeServiceQuery
*/
bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery)
{
bool bResult = false;
if (p_pServiceQuery)
{
stcMDNSServiceQuery* pPred = m_pServiceQueries;
while ((pPred) && (pPred->m_pNext != p_pServiceQuery))
{
pPred = pPred->m_pNext;
}
if (pPred)
{
pPred->m_pNext = p_pServiceQuery->m_pNext;
delete p_pServiceQuery;
bResult = true;
}
else // No predecessor
{
if (m_pServiceQueries == p_pServiceQuery)
{
m_pServiceQueries = p_pServiceQuery->m_pNext;
delete p_pServiceQuery;
bResult = true;
}
else
{
DEBUG_EX_ERR(DEBUG_OUTPUT.println(
"[MDNSResponder] _releaseServiceQuery: INVALID service query!"););
}
}
}
return bResult;
}
/*
MDNSResponder::_removeLegacyServiceQuery
*/
bool MDNSResponder::_removeLegacyServiceQuery(void)
{
stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery();
return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true);
}
/*
MDNSResponder::_findServiceQuery
'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existence)
*/
MDNSResponder::stcMDNSServiceQuery*
MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery)
{
stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries;
while (pServiceQuery)
{
if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery)
{
break;
}
pServiceQuery = pServiceQuery->m_pNext;
}
return pServiceQuery;
}
/*
MDNSResponder::_findLegacyServiceQuery
*/
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void)
{
stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries;
while (pServiceQuery)
{
if (pServiceQuery->m_bLegacyQuery)
{
break;
}
pServiceQuery = pServiceQuery->m_pNext;
}
return pServiceQuery;
}
/*
MDNSResponder::_releaseServiceQueries
*/
bool MDNSResponder::_releaseServiceQueries(void)
{
while (m_pServiceQueries)
{
stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext;
delete m_pServiceQueries;
m_pServiceQueries = pNext;
}
return true;
}
/*
MDNSResponder::_findNextServiceQueryByServiceType
*/
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(
const stcMDNS_RRDomain& p_ServiceTypeDomain, const stcMDNSServiceQuery* p_pPrevServiceQuery)
{
stcMDNSServiceQuery* pMatchingServiceQuery = 0;
stcMDNSServiceQuery* pServiceQuery
= (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries);
while (pServiceQuery)
{
if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain)
{
pMatchingServiceQuery = pServiceQuery;
break;
}
pServiceQuery = pServiceQuery->m_pNext;
}
return pMatchingServiceQuery;
}
/*
HOSTNAME
*/
/*
MDNSResponder::_setHostname
*/
bool MDNSResponder::_setHostname(const char* p_pcHostname)
{
// DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"),
// p_pcHostname););
bool bResult = false;
_releaseHostname();
size_t stLength = 0;
if ((p_pcHostname)
&& (MDNS_DOMAIN_LABEL_MAXLENGTH
>= (stLength = strlen(p_pcHostname)))) // char max size for a single label
{
// Copy in hostname characters as lowercase
if ((bResult = (0 != (m_pcHostname = new char[stLength + 1]))))
{
#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME
size_t i = 0;
for (; i < stLength; ++i)
{
m_pcHostname[i]
= (isupper(p_pcHostname[i]) ? tolower(p_pcHostname[i]) : p_pcHostname[i]);
}
m_pcHostname[i] = 0;
#else
strncpy(m_pcHostname, p_pcHostname, (stLength + 1));
#endif
}
}
return bResult;
}
/*
MDNSResponder::_releaseHostname
*/
bool MDNSResponder::_releaseHostname(void)
{
if (m_pcHostname)
{
delete[] m_pcHostname;
m_pcHostname = 0;
}
return true;
}
/*
SERVICE
*/
/*
MDNSResponder::_allocService
*/
MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName,
const char* p_pcService,
const char* p_pcProtocol,
uint16_t p_u16Port)
{
stcMDNSService* pService = 0;
if (((!p_pcName) || (MDNS_DOMAIN_LABEL_MAXLENGTH >= strlen(p_pcName))) && (p_pcService)
&& (MDNS_SERVICE_NAME_LENGTH >= strlen(p_pcService)) && (p_pcProtocol)
&& (MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) && (p_u16Port)
&& (0 != (pService = new stcMDNSService))
&& (pService->setName(p_pcName ?: m_pcHostname)) && (pService->setService(p_pcService))
&& (pService->setProtocol(p_pcProtocol)))
{
pService->m_bAutoName = (0 == p_pcName);
pService->m_u16Port = p_u16Port;
// Add to list (or start list)
pService->m_pNext = m_pServices;
m_pServices = pService;
}
return pService;
}
/*
MDNSResponder::_releaseService
*/
bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService)
{
bool bResult = false;
if (p_pService)
{
stcMDNSService* pPred = m_pServices;
while ((pPred) && (pPred->m_pNext != p_pService))
{
pPred = pPred->m_pNext;
}
if (pPred)
{
pPred->m_pNext = p_pService->m_pNext;
delete p_pService;
bResult = true;
}
else // No predecessor
{
if (m_pServices == p_pService)
{
m_pServices = p_pService->m_pNext;
delete p_pService;
bResult = true;
}
else
{
DEBUG_EX_ERR(
DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!"););
}
}
}
return bResult;
}
/*
MDNSResponder::_releaseServices
*/
bool MDNSResponder::_releaseServices(void)
{
stcMDNSService* pService = m_pServices;
while (pService)
{
_releaseService(pService);
pService = m_pServices;
}
return true;
}
/*
MDNSResponder::_findService
*/
MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName,
const char* p_pcService,
const char* p_pcProtocol)
{
stcMDNSService* pService = m_pServices;
while (pService)
{
if ((0 == strcmp(pService->m_pcName, p_pcName))
&& (0 == strcmp(pService->m_pcService, p_pcService))
&& (0 == strcmp(pService->m_pcProtocol, p_pcProtocol)))
{
break;
}
pService = pService->m_pNext;
}
return pService;
}
/*
MDNSResponder::_findService
*/
MDNSResponder::stcMDNSService*
MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService)
{
stcMDNSService* pService = m_pServices;
while (pService)
{
if (p_hService == (hMDNSService)pService)
{
break;
}
pService = pService->m_pNext;
}
return pService;
}
/*
SERVICE TXT
*/
/*
MDNSResponder::_allocServiceTxt
*/
MDNSResponder::stcMDNSServiceTxt*
MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService, const char* p_pcKey,
const char* p_pcValue, bool p_bTemp)
{
stcMDNSServiceTxt* pTxt = 0;
if ((p_pService) && (p_pcKey)
&& (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() + 1 + // Length byte
(p_pcKey ? strlen(p_pcKey) : 0) + 1 + // '='
(p_pcValue ? strlen(p_pcValue) : 0))))
{
pTxt = new stcMDNSServiceTxt;
if (pTxt)
{
size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0);
pTxt->m_pcKey = new char[stLength + 1];
if (pTxt->m_pcKey)
{
strncpy(pTxt->m_pcKey, p_pcKey, stLength);
pTxt->m_pcKey[stLength] = 0;
}
if (p_pcValue)
{
stLength = (p_pcValue ? strlen(p_pcValue) : 0);
pTxt->m_pcValue = new char[stLength + 1];
if (pTxt->m_pcValue)
{
strncpy(pTxt->m_pcValue, p_pcValue, stLength);
pTxt->m_pcValue[stLength] = 0;
}
}
pTxt->m_bTemp = p_bTemp;
// Add to list (or start list)
p_pService->m_Txts.add(pTxt);
}
}
return pTxt;
}
/*
MDNSResponder::_releaseServiceTxt
*/
bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService,
MDNSResponder::stcMDNSServiceTxt* p_pTxt)
{
return ((p_pService) && (p_pTxt) && (p_pService->m_Txts.remove(p_pTxt)));
}
/*
MDNSResponder::_updateServiceTxt
*/
MDNSResponder::stcMDNSServiceTxt*
MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService,
MDNSResponder::stcMDNSServiceTxt* p_pTxt,
const char* p_pcValue, bool p_bTemp)
{
if ((p_pService) && (p_pTxt)
&& (MDNS_SERVICE_TXT_MAXLENGTH
> (p_pService->m_Txts.length() - (p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0)
+ (p_pcValue ? strlen(p_pcValue) : 0))))
{
p_pTxt->update(p_pcValue);
p_pTxt->m_bTemp = p_bTemp;
}
return p_pTxt;
}
/*
MDNSResponder::_findServiceTxt
*/
MDNSResponder::stcMDNSServiceTxt*
MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, const char* p_pcKey)
{
return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0);
}
/*
MDNSResponder::_findServiceTxt
*/
MDNSResponder::stcMDNSServiceTxt*
MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, const hMDNSTxt p_hTxt)
{
return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt)
: 0);
}
/*
MDNSResponder::_addServiceTxt
*/
MDNSResponder::stcMDNSServiceTxt*
MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService, const char* p_pcKey,
const char* p_pcValue, bool p_bTemp)
{
stcMDNSServiceTxt* pResult = 0;
if ((p_pService) && (p_pcKey) && (strlen(p_pcKey)))
{
stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey);
if (pTxt)
{
pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp);
}
else
{
pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp);
}
}
return pResult;
}
MDNSResponder::stcMDNSServiceTxt*
MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery,
const uint32_t p_u32AnswerIndex)
{
stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery);
stcMDNSServiceQuery::stcAnswer* pSQAnswer
= (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0);
// Fill m_pcTxts (if not already done)
return (pSQAnswer) ? pSQAnswer->m_Txts.m_pTxts : 0;
}
/*
MDNSResponder::_collectServiceTxts
*/
bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService)
{
// Call Dynamic service callbacks
if (m_fnServiceTxtCallback)
{
m_fnServiceTxtCallback((hMDNSService)&p_rService);
}
if (p_rService.m_fnTxtCallback)
{
p_rService.m_fnTxtCallback((hMDNSService)&p_rService);
}
return true;
}
/*
MDNSResponder::_releaseTempServiceTxts
*/
bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService)
{
return (p_rService.m_Txts.removeTempTxts());
}
/*
MISC
*/
#ifdef DEBUG_ESP_MDNS_RESPONDER
/*
MDNSResponder::_printRRDomain
*/
bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const
{
// DEBUG_OUTPUT.printf_P(PSTR("Domain: "));
const char* pCursor = p_RRDomain.m_acName;
uint8_t u8Length = *pCursor++;
if (u8Length)
{
while (u8Length)
{
for (uint8_t u = 0; u < u8Length; ++u)
{
DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++));
}
u8Length = *pCursor++;
if (u8Length)
{
DEBUG_OUTPUT.printf_P(PSTR("."));
}
}
}
else // empty domain
{
DEBUG_OUTPUT.printf_P(PSTR("-empty-"));
}
// DEBUG_OUTPUT.printf_P(PSTR("\n"));
return true;
}
/*
MDNSResponder::_printRRAnswer
*/
bool MDNSResponder::_printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const
{
DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] RRAnswer: "));
_printRRDomain(p_RRAnswer.m_Header.m_Domain);
DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, "),
p_RRAnswer.m_Header.m_Attributes.m_u16Type,
p_RRAnswer.m_Header.m_Attributes.m_u16Class, p_RRAnswer.m_u32TTL);
switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type
& (~0x8000)) // Topmost bit might carry 'cache flush' flag
{
#ifdef MDNS_IP4_SUPPORT
case DNS_RRTYPE_A:
DEBUG_OUTPUT.printf_P(
PSTR("A IP:%s"),
((const stcMDNS_RRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str());
break;
#endif
case DNS_RRTYPE_PTR:
DEBUG_OUTPUT.printf_P(PSTR("PTR "));
_printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain);
break;
case DNS_RRTYPE_TXT:
{
size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength();
char* pTxts = new char[stTxtLength];
if (pTxts)
{
((/*const c_str()!!*/ stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts);
DEBUG_OUTPUT.printf_P(PSTR("TXT(%zu) %s"), stTxtLength, pTxts);
delete[] pTxts;
}
break;
}
#ifdef MDNS_IP6_SUPPORT
case DNS_RRTYPE_AAAA:
DEBUG_OUTPUT.printf_P(
PSTR("AAAA IP:%s"),
((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str());
break;
#endif
case DNS_RRTYPE_SRV:
DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "),
((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_u16Port);
_printRRDomain(((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_SRVDomain);
break;
default:
DEBUG_OUTPUT.printf_P(PSTR("generic "));
break;
}
DEBUG_OUTPUT.printf_P(PSTR("\n"));
return true;
}
#endif
} // namespace MDNSImplementation
} // namespace esp8266